src/ai/ai_factory.hpp
author truebrain
Thu, 28 Feb 2008 01:11:23 +0000
branchnoai
changeset 9803 c86d5834fb11
parent 9770 ad3c5f807d7c
permissions -rw-r--r--
(svn r12309) [NoAI] -Codechange: optimize a little bit (a very small little bit, but every bit counts :) ) (glx)
/* $Id$ */

/** @file ai_factory.hpp declaration of class for AIFactory class */

#ifndef AI_FACTORY_HPP
#define AI_FACTORY_HPP

#include <string>
#include <map>
#include <assert.h>
#include "../stdafx.h"
#include "../functions.h"
#include "../network/network.h"

class AIController;

class AIFactoryBase {
private:
	char *name;
	typedef std::map<std::string, AIFactoryBase *> Factories;

	static Factories &GetFactories()
	{
		static Factories &s_factories = *new Factories();
		return s_factories;
	}

protected:
	/**
	 * This registers your AI to the main system so we know about you.
	 */
	void RegisterFactory(const char *name)
	{
		/* Don't register nameless Factories */
		if (name == NULL) return;

		this->name = strdup(name);
		std::pair<Factories::iterator, bool> P = GetFactories().insert(Factories::value_type(name, this));
		assert(P.second);
	}

public:
	AIFactoryBase() :
		name(NULL)
	{}
	virtual ~AIFactoryBase() { if (this->name != NULL) GetFactories().erase(this->name); free(this->name); }

	/**
	 * Run all initializers.
	 */
	static void RunInitializers()
	{
		/* Protect this function from being run several times */
		static bool run_once = false;
		if (run_once) return;
		run_once = true;
		if (GetFactories().size() == 0) return;
		Factories::iterator it = GetFactories().begin();
		for (; it != GetFactories().end(); it++) {
			AIFactoryBase *f = (*it).second;
			f->Initializer();
		}
	}

	/**
	 * Select a random AI from all known AIs.
	 */
	static AIFactoryBase *SelectRandomAI()
	{
		if (GetFactories().size() == 0) return NULL;

		/* Find a random AI */
		uint i;
		if (_networking) i = InteractiveRandomRange(GetFactories().size());
		else             i =            RandomRange(GetFactories().size());

		/* Find the Nth item from the array */
		Factories::iterator it = GetFactories().begin();
		Factories::iterator first_it;
		for (; i > 0; i--) it++;
		first_it = it;
		AIFactoryBase *f = it->second;

		if (!f->AllowStartup()) {
			/* We can't start this AI, try to find the next best */
			do {
				it++;
				if (it == GetFactories().end()) it = GetFactories().begin();
				/* Back at the beginning? Drop out! */
				if (first_it == it) break;
				f = it->second;
			} while (!f->AllowStartup());
			/* Unable to start any AI */
			if (first_it == it) return NULL;
		}
		return f;
	}

	/**
	 * Select the AI with the given name.
	 * @param name the AI to select.
	 */
	static AIFactoryBase *SelectAI(const char *name)
	{
		if (GetFactories().size() == 0) return NULL;

		Factories::iterator it = GetFactories().begin();
		for (; it != GetFactories().end(); it++) {
			AIFactoryBase *f = (*it).second;

			if (strcasecmp(name, f->name) == 0 && f->AllowStartup()) {
				return f;
			}
		}

		return NULL;
	}

	/**
	 * Fills the given buffer with the AIs we know.
	 * @param p    start of the buffer.
	 * @param last till were we may write.
	 * @return till were we have written.
	 */
	static char *GetAIConsoleList(char *p, const char *last)
	{
		p += snprintf(p, last - p, "List of AIs:\n");
		Factories::iterator it = GetFactories().begin();
		for (; it != GetFactories().end(); it++) {
			AIFactoryBase *f = (*it).second;
			if (!f->AllowStartup()) continue;
			p += snprintf(p, last - p, "%10s: %s\n", f->name, f->GetDescription());
		}
		p += snprintf(p, last - p, "\n");

		return p;
	}


	/**
	 * Get the name of the AI internally.
	 */
	const char *GetAIName() { return this->name; }

	/**
	 * Get the author of the AI.
	 */
	virtual const char *GetAuthor() = 0;

	/**
	 * Get a description of the AI.
	 */
	virtual const char *GetDescription() = 0;

	/**
	 * Get the version of this AI.
	 */
	virtual int GetVersion() = 0;

	/**
	 * Get the date of the version.
	 * @return A string like '2007-08-29'.
	 */
	virtual const char *GetDate() = 0;

	/**
	 * Create an instance of your AI.
	 */
	virtual AIController *CreateInstance() = 0;

	/**
	 * In this function you can do some very limited querying of the game.
	 *  If you for example only supply road-vehicle-based AI, and RVs are
	 *  disabled, you return 'false', so you won't boot.
	 */
	virtual bool AllowStartup() { return true; }

	/**
	 * Normally you just make a static instance of AIFactory, and you
	 *  will be registered. In very limited cases, like Squirrel-module,
	 *  it is needed to be called when OpenTTD is launched (so not as
	 *  constructor). Then you can use this function.
	 * @note Avoid using it!
	 */
	virtual void Initializer() { }
};

/**
 * The template to define your AIFactory. This makes sure RegisterFactory
 *  works correctly on initialization.
 * Example: class FYourClass: public AIFactory<FYourClass> { }
 */
template <class T>
class AIFactory: public AIFactoryBase {
public:
	AIFactory() { this->RegisterFactory(((T *)this)->GetName()); }

	/**
	 * Get a short name for the AI.
	 */
	const char *GetName();
};

#endif /* AI_FACTORY_HPP */