src/ai/core/ai_factory.hpp
author truelight
Thu, 15 Mar 2007 10:29:43 +0000
branchnoai
changeset 9401 04502c3557f2
parent 9400 6da42d57fda2
child 9402 45581b218c98
permissions -rw-r--r--
(svn r9196) [NoAI] -Fix: avoid include-magic by including needed includes directly
/* $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"

class AIController;

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

	static Factories &GetFactories()
	{
		static Factories s_factories;
		return s_factories;
	}

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

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

public:
	virtual ~AIFactoryBase() { if (this->name != NULL) GetFactories().erase(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 AIController *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 {
				if (it == GetFactories().end()) it = GetFactories().begin();
				else it++;
				/* 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->CreateInstance();
	}



	/**
	 * 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 */