src/ai/api/ai_object.cpp
author truebrain
Mon, 16 Jun 2008 14:43:19 +0000
branchnoai
changeset 10978 13fd0364b2c6
parent 10977 6c1a6657c7db
child 11057 188a9ca6d8de
permissions -rw-r--r--
(svn r13532) [NoAI] -Fix: in MultiPlayer SignID wasn't set correctly, causing weird effects. Code is now more unified, which should avoid simular effects in the future (tnx to glx for initial patch)
/* $Id$ */

/** @file ai_object.cpp Implementation of AIObject. */

#include "ai_object.hpp"
#include "ai_log.hpp"
#include "ai_error.hpp"
#include "table/strings.h"
#include "../../openttd.h"
#include "../../command_func.h"
#include "../../map_func.h"
#include "../../network/network.h"
#include "../../player_func.h"
#include "../../signs_func.h"
#include "../../tunnelbridge.h"
#include "../../vehicle_func.h"
#include "../ai.h"
#include "../ai_threads.h"

struct AIObject::AIDoCommandStruct {
	AIModeProc *mode;
	AIObject *mode_instance;
	uint delay;
	CommandCost costs;
	uint last_error;
	bool last_command_res;
	VehicleID new_vehicle_id;
	SignID new_sign_id;
	TileIndex new_tunnel_endtile;
	RoadType road_type;
	void *event_data;
	void *log_data;
};

void AIObject::SetDoCommandDelay(uint ticks)
{
	assert(ticks > 0);
	GetDoCommandStruct(_current_player)->delay = ticks;
}

uint AIObject::GetDoCommandDelay()
{
	return GetDoCommandStruct(_current_player)->delay;
}

void AIObject::SetDoCommandMode(AIModeProc *proc, AIObject *instance)
{
	GetDoCommandStruct(_current_player)->mode = proc;
	GetDoCommandStruct(_current_player)->mode_instance = instance;
}

AIModeProc *AIObject::GetDoCommandMode()
{
	return GetDoCommandStruct(_current_player)->mode;
}

AIObject *AIObject::GetDoCommandModeInstance()
{
	return GetDoCommandStruct(_current_player)->mode_instance;
}

void AIObject::SetDoCommandCosts(Money value)
{
	GetDoCommandStruct(_current_player)->costs = CommandCost(value);
}

void AIObject::IncreaseDoCommandCosts(Money value)
{
	GetDoCommandStruct(_current_player)->costs.AddCost(value);
}

Money AIObject::GetDoCommandCosts()
{
	return GetDoCommandStruct(_current_player)->costs.GetCost();
}

void AIObject::SetLastError(AIErrorType last_error)
{
	GetDoCommandStruct(_current_player)->last_error = last_error;
}

AIErrorType AIObject::GetLastError()
{
	return GetDoCommandStruct(_current_player)->last_error;
}

void AIObject::SetRoadType(RoadType road_type)
{
	GetDoCommandStruct(_current_player)->road_type = road_type;
}

RoadType AIObject::GetRoadType()
{
	return GetDoCommandStruct(_current_player)->road_type;
}

void AIObject::SetLastCommandRes(bool res)
{
	GetDoCommandStruct(_current_player)->last_command_res = res;
	/* Also store the results of various global variables */
	SetNewVehicleID(_new_vehicle_id);
	SetNewSignID(_new_sign_id);
	SetNewTunnelEndtile(_build_tunnel_endtile);
}

bool AIObject::GetLastCommandRes()
{
	return GetDoCommandStruct(_current_player)->last_command_res;
}

void AIObject::SetNewVehicleID(VehicleID vehicle_id)
{
	GetDoCommandStruct(_current_player)->new_vehicle_id = vehicle_id;
}

VehicleID AIObject::GetNewVehicleID()
{
	return GetDoCommandStruct(_current_player)->new_vehicle_id;
}

void AIObject::SetNewSignID(SignID sign_id)
{
	GetDoCommandStruct(_current_player)->new_sign_id = sign_id;
}

SignID AIObject::GetNewSignID()
{
	return GetDoCommandStruct(_current_player)->new_sign_id;
}

void AIObject::SetNewTunnelEndtile(TileIndex tile)
{
	GetDoCommandStruct(_current_player)->new_tunnel_endtile = tile;
}

TileIndex AIObject::GetNewTunnelEndtile()
{
	return GetDoCommandStruct(_current_player)->new_tunnel_endtile;
}

void *&AIObject::GetEventPointer()
{
	return GetDoCommandStruct(_current_player)->event_data;
}

void *&AIObject::GetLogPointer()
{
	return GetDoCommandStruct(_current_player)->log_data;
}

AIObject::AIDoCommandStruct *AIObject::GetDoCommandStruct(PlayerID player)
{
	/* Storage for data on per-AI level */
	static AIDoCommandStruct command_struct[MAX_PLAYERS];
	static bool initialized = false;

	/* Make sure all memory is NULL when we start */
	if (!initialized) {
		initialized = true;
		memset(&command_struct, 0, sizeof(command_struct));
	}

	return &command_struct[player];
}

void AIObject::ResetInternalPlayerData()
{
	AIDoCommandStruct *command_struct = GetDoCommandStruct(_current_player);
	command_struct->mode = NULL;
	command_struct->delay = 1;
	command_struct->costs = CommandCost();
	if (command_struct->event_data != NULL) AIEventController::FreeEventPointer();
	command_struct->event_data = NULL;
	if (command_struct->log_data != NULL) AILog::FreeLogPointer();
	command_struct->log_data = NULL;
}

bool AIObject::DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint procc, bool water_protection)
{
	uint32 flags = 0;
	PlayerID old_lp;
	CommandCost res;
	const char *tmp_cmdtext;

	/* Make sure the last error is reset, so we don't give faulty warnings */
	SetLastError(AIError::ERR_NONE);

	if (procc != CMD_LANDSCAPE_CLEAR) flags |= DC_AUTO;
	if (water_protection) flags |= DC_NO_WATER;

	/* The test already resets _cmd_text, so backup the pointer */
	tmp_cmdtext = _cmd_text;

	/* First, do a test-run to see if we can do this */
	res = ::DoCommand(tile, p1, p2, flags, procc);
	/* The command failed, so return */
	if (::CmdFailed(res)) {
		SetLastError(AIError::StringToError(_error_message));
		return false;
	}

	/* Restore _cmd_text */
	_cmd_text = tmp_cmdtext;

	/* Check what the callback wants us to do */
	if (GetDoCommandMode() != NULL && !GetDoCommandMode()(tile, p1, p2, procc, res)) {
		IncreaseDoCommandCosts(res.GetCost());
		return true;
	}

	/* If we did a DC_EXEC, and the command did not return an error, execute it
	 * over the network */
	if (flags & DC_NO_WATER) procc |= CMD_NO_WATER;

#ifdef ENABLE_NETWORK
	/* Send the command */
	if (_networking) {
		/* NetworkSend_Command needs _local_player to be set correctly, so
		 * adjust it, and put it back right after the function */
		old_lp = _local_player;
		_local_player = _current_player;
		::NetworkSend_Command(tile, p1, p2, procc, CcAI);
		_local_player = old_lp;

		/* Suspend the AI till the command is really executed */
		::AI_SuspendPlayer(_current_player, -(int)GetDoCommandDelay());
		/* Check if the callback still agrees with us, else return error */
		if (!GetLastCommandRes()) res = CMD_ERROR;
	} else {
#else
	{
#endif
		/* For SinglePlayer we execute the command immediatly */
		if (!::DoCommandP(tile, p1, p2, NULL, procc)) res = CMD_ERROR;
		SetLastCommandRes(!CmdFailed(res));

		/* Suspend the AI player for 1 tick, so it simulates MultiPlayer */
		::AI_SuspendPlayer(_current_player, GetDoCommandDelay());
	}

	if (::CmdFailed(res)) {
		SetLastError(AIError::StringToError(_error_message));
		return false;
	}

	IncreaseDoCommandCosts(res.GetCost());
	return true;
}