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