src/ai/api/ai_transactionmode.cpp
author rubidium
Wed, 09 Jan 2008 18:11:12 +0000
branchnoai
changeset 9723 eee46cb39750
parent 9629 66dde6412125
child 9833 89a64246458f
permissions -rw-r--r--
(svn r11796) [NoAI] -Sync: with trunk r11502:11795.
/* $Id$ */

/** @file ai_transactionmode.cpp class to switch the AI to Transaction mode */

#include "ai_transactionmode.hpp"
#include "ai_execmode.hpp"
#include "../../command_func.h"
#include "../../debug.h"
#include <stack>

bool AITransactionMode::ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCost costs)
{
	AITransactionMode *instance = (AITransactionMode *)AIObject::GetDoCommandModeInstance();
	/* Don't record if we are stopped */
	if (instance->stopped) return false;
	AITransactionModeCommand command;

	/* Store all params in a nice list, so we can execute them later */
	command.tile  = tile;
	command.p1    = p1;
	command.p2    = p2;
	command.procc = procc;
	command.text  = (_cmd_text == NULL) ? NULL : strdup(_cmd_text);
	instance->command_stack.push(command);
	instance->costs.AddCost(costs);

	/* Now we return 'false', as we don't want to execute the commands just yet */
	return false;
}

AITransactionMode::AITransactionMode()
{
	this->costs         = CommandCost();
	this->stopped       = false;
	this->last_mode     = this->GetDoCommandMode();
	this->last_instance = this->GetDoCommandModeInstance();
	this->SetDoCommandMode(&AITransactionMode::ModeProc, this);
}

AITransactionMode::~AITransactionMode()
{
	this->SetDoCommandMode(this->last_mode, this->last_instance);

	/* Clean up the command_stack nicely (as in: free the cmd_text) */
	AITransactionModeCommand command;
	while (!this->command_stack.empty()) {
		command = this->command_stack.front();
		free(command.text);
		this->command_stack.pop();
	}

	/* Clean up the reverse_stack too */
	while (!this->reverse_stack.empty()) {
		command = this->reverse_stack.front();
		free(command.text);
		this->reverse_stack.pop();
	}
}

bool AITransactionMode::Execute()
{
	/* Make sure we are stopped */
	this->Stop();

	/* Remove the costs this transaction costs from the total costs, so any
	 *  'accounting' running on the background will get the real costs for
	 *  executing, and not the double value of both testing and executing */
	this->IncreaseDoCommandCosts(-this->GetCosts());

	/* Switch to a forced execute mode */
	AIExecMode exec;

	/* Reverse stack should be empty */
	assert(this->reverse_stack.empty());

	AITransactionModeCommand command;
	while (!this->command_stack.empty()) {
		command = this->command_stack.front();
		_cmd_text = command.text;
		/* Make sure we return 'false' if the command fails to execute. This allows rollback of failed builds. */
		if (!this->DoCommand(command.tile, command.p1, command.p2, command.procc)) return false;
		/* Add the command to the reverse stack */
		this->reverse_stack.push(command);
		/* Remove the item from the list */
		this->command_stack.pop();
	}

	/* Everything went okay, clean the reverse stack */
	while (!this->reverse_stack.empty()) {
		command = this->reverse_stack.front();
		free(command.text);
		this->reverse_stack.pop();
	}

	/* Reverse stack should be empty again */
	assert(this->reverse_stack.empty());

	return true;
}

void AITransactionMode::Stop()
{
	this->stopped = true;
}

void AITransactionMode::Rollback()
{
	AITransactionModeCommand command;

	DEBUG(ai, 0, "AITransactionMode::Rollback is not yet implemented");
	return;

	while (!this->reverse_stack.empty()) {
		command = this->reverse_stack.front();
		_cmd_text = command.text;

		/* Find the reverse, and issue it */
		/* TODO -- Make a table which reverses every command.procc */
		this->DoCommand(command.tile, command.p1, command.p2, command.procc);

		/* Remove the item from the list */
		this->reverse_stack.pop();
	}
}

Money AITransactionMode::GetCosts()
{
	return this->costs.GetCost();
}

void AITransactionMode::Append(AITransactionMode *transaction)
{
	/* Can't append to myself! */
	if (transaction == this) return;

	/* Simply move the queue from transaction to ourself */
	while (!transaction->command_stack.empty()) {
		this->command_stack.push(transaction->command_stack.front());
		transaction->command_stack.pop();
	}
}