/* $Id: bot.cpp,v 1.9 2005/10/16 17:53:05 pohlt Exp $ */ #include "bot.hpp" #include "collidableobject.hpp" #include "noncollidableobject.hpp" #include "avatar.hpp" #include "wopsettings.hpp" #include "scusibot.hpp" #include "tombot.hpp" /**********************************************************/ #define SECTION_NAME2CONFIG "NAME2CONFIG" #define SECTION_NAME2CLASS "NAME2CLASS" #define SECTION_BASE_CONFIG "BASE_BOT" #define STDATTR (Setting::COMMA_SEPARATED | \ Setting::EXPLICIT_ASSIGNMENT | \ Setting::CONFIG_FILE_ONLY) #define DEFSTR(ID) m_SettingDef[ID].m_IDString #define USE_RADIUS_SIGHT /**********************************************************/ #ifdef USE_RADIUS_SIGHT #define SIGHT_RADIUS 300.0 #define SIGHT_RADIUS_2 (SIGHT_RADIUS * SIGHT_RADIUS) #endif /**********************************************************/ // static members of class Bot const char* Bot::m_internalTypeString[Bot::NUM_BOT_TYPES+1] = { "UNDEFINED_BOT", "SCUSI_BOT", "TOM_BOT", "GISMO_BOT" }; const char** Bot::m_typeString = m_internalTypeString + 1; const SettingDef Bot::m_SettingDef[Bot::NUM_SETTING_DEFS+1] = { SettingDef("name", 0x0, Setting::STRING, 1, -1, STDATTR), SettingDef("team", 0x0, Setting::INT, 1, -1, STDATTR), SettingDef("color", 0x0, Setting::INT, 3, -1, STDATTR), SettingDef("follow", 0x0, Setting::STRING, 1, -1, STDATTR), SettingDef() }; /**********************************************************/ Bot::Bot() : m_ID(Bot::UNDEFINED_BOT), m_currObjVecType(UNDEFINED), m_currIndex(0) {} /**********************************************************/ Bot::~Bot() {} /**********************************************************/ Bot* Bot::createBot( const char* const botName ) { Bot *newBot = 0x0; // map the bot name to a class and create the object Sint32 botID = name2classID( botName ); switch( botID ) { case SCUSI_BOT: newBot = NEW ScusiBot; break; case TOM_BOT: newBot = NEW TomBot; break; default: break; } ASSERT( newBot, "Bot::createBot: could not " "create bot \"%s\"\n", botName ); // initialize the bot randomly newBot->defaultConfig(); // augment with configurations from file newBot->load( botName ); return newBot; } /**********************************************************/ Uint32 Bot::getSerializeBufferSize() const { return Player::getSerializeBufferSize() + Serialize::sizeOf() + Serialize::sizeOf( m_currIndex ) // adds additional size for debugging (see serialize.hpp), // but only, if the tags are used PLUS_TAG_SIZE( 1 ); } /**********************************************************/ void Bot::serialize( Uint8*& bufferPointer ) const { START_SERIALIZED_SIZE_CHECK( bufferPointer ); Player::serialize( bufferPointer ); Sint32 currObjVecType = (Sint32)m_currObjVecType; Serialize::serialize( currObjVecType, bufferPointer ); Serialize::serialize( m_currIndex, bufferPointer ); // expands to tag serialization SERIALIZE_TAG( bufferPointer ); END_SERIALIZED_SIZE_CHECK( bufferPointer, Bot ); } /**********************************************************/ void Bot::deserialize( Uint8*& bufferPointer ) { Sint32 currObjVecType; Player::deserialize( bufferPointer ); Serialize::deserialize( bufferPointer, currObjVecType ); Serialize::deserialize( bufferPointer, m_currIndex ); m_currObjVecType = (ObjVecType)currObjVecType; // expands to tag deserialization DESERIALIZE_TAG( bufferPointer ); } /**********************************************************/ void Bot::defaultConfig() { setName( "Z. Branigan" ); // Usage of LOCAL random generator is OK here, since // the bot is created on clients only and distributed // to all other clients after that. setPlayerColor( localRnd.getUint32() & 0x00ffffff ); setTeamID( 10 ); } /**********************************************************/ bool Bot::load( const char* const botName ) { const char fn[] = "Bot::load:"; String configFile; SettingDataBase settings; // if no individual file is defined, just skip this configuration if( ! name2config(botName, configFile) ) return true; if( CHECK( settings.restrictToSection(SECTION_BASE_CONFIG), "%s could not restrict to section %s\n", fn, SECTION_BASE_CONFIG ) && CHECK( settings.readSettings(m_SettingDef, (char*)configFile), "%s could not read base configuration\n", fn ) && CHECK( settings.finalCheck(m_SettingDef), "%s failed final check\n", fn ) ) { // process loaded configuration Setting *setting = 0x0; LOG(2) INFO( "%s configuring \"%s\" from file:\n", fn, botName ); // set name if( 0x0 != (setting = settings.getSetting(DEFSTR(NAME_DEF))) ) { const int iname = localRnd.getUint32() % setting->getNumParameters(); setName( setting->getString(iname) ); LOG(2) INFO( "%s name : \"%s\"\n", fn, setting->getString(iname) ); } // set team ID if( 0x0 != (setting = settings.getSetting(DEFSTR(TEAM_DEF))) ) { const Uint32 iteam = localRnd.getUint32() % setting->getNumParameters(); LOG(2) INFO( "%s team : %d\n", fn, setting->getInt(iteam) ); setTeamID( setting->getInt(iteam) ); } // set color if( 0x0 != (setting = settings.getSetting(DEFSTR(COLOR_DEF))) ) { int numColors = setting->getNumParameters(); CHECK( 0 == (numColors % 3), "%s number of RGB values in setting \"%s\" in " "file \"%s\" is %d (%3!=0). Using only the first " "%d complete colors\n", fn, DEFSTR(COLOR_DEF), (char*)configFile, numColors, numColors/3 ); numColors /= 3; const int icol = localRnd.getUint32() % numColors; LOG(2) INFO( "%s color: %d,%d,%d\n", fn, setting->getInt(3*icol ), setting->getInt(3*icol + 1), setting->getInt(3*icol + 2) ); setPlayerColor( TPLCOL_RGB(setting->getInt(3*icol ), setting->getInt(3*icol + 1), setting->getInt(3*icol + 2)) ); } // configuration of derived class if( 0x0 != (setting = settings.getSetting(DEFSTR(FOLLOW_DEF))) ) { if( !loadDerived(*setting) ) return false; } } return true; } /**********************************************************/ bool Bot::name2config( const char* const botName, String& file ) { const char fn[] = "Bot::name2config:"; bool success = false; SettingDataBase mapSettings, classMapSettings; String mapFile( WopSettings::getInstance()->getData() ); mapFile += "/"BOT_MAP_PATH"/"BOT_MAP_FILE; // implicitely load list of possible bot names if( ! loadName2classIDsettings(classMapSettings, mapFile) ) { return false; } // prepare setting definitions Setting *classMap = classMapSettings.getSetting( "name2class" ); int nValidNames = classMap->getNumParameters() / 2; SettingDef *defs = NEW SettingDef [nValidNames+1]; ASSERT( defs != 0x0, "%s could not create SettingDef[%d]\n", fn, nValidNames+1 ); // fill definitions (they only differ in their ID string) for( int i = 0; i < nValidNames; i++ ) { defs[i].m_IDString = classMap->getString(2*i); defs[i].m_Type = Setting::STRING; defs[i].m_minNumParams = 1; defs[i].m_maxNumParams = 1; defs[i].m_Attributes = Setting::EXPLICIT_ASSIGNMENT; } // read the base settings if( CHECK( mapSettings.restrictToSection(SECTION_NAME2CONFIG), "%s could not restrict to section \"%s\"\n", fn, SECTION_NAME2CONFIG ) && CHECK( mapSettings.readSettings(defs, mapFile.getString()), "%s could not read bot map in \"%s\"\n", fn, mapFile.getString() ) && CHECK( mapSettings.finalCheck(defs), "%s failed final check of bot map \"%s\"\n", fn, mapFile.getString() ) ) { // read the configuration file name Setting *setting = mapSettings.getSetting( botName ); if( setting ) { // build full path of configuration file file = WopSettings::getInstance()->getData(); file += "/"BOT_MAP_PATH"/"; file += setting->getString(); success = true; } else { LOG(1) INFO( "%s no configuration file specified for bot " "\"%s\" in map file \"%s\" -> using random " "configuration\n", fn, botName, mapFile.getString() ); } } delete [] defs; return success; } /**********************************************************/ Sint32 Bot::name2classID( const char* const botName ) { const char fn[] = "Bot::name2classID"; SettingDataBase mapSettings; // read the base settings String mapFile( WopSettings::getInstance()->getData() ); mapFile += "/"BOT_MAP_PATH"/"BOT_MAP_FILE; if( loadName2classIDsettings(mapSettings, mapFile) ) { // Setting *setting = mapSettings.getSetting( "name2class" ); // search the passed name in the settings for( int i = 0; i < setting->getNumParameters(); i += 2 ) { if( !strcmp(botName, setting->getString(i)) ) { // map the specified ID string to a bot ID for( int j = 0; j < Bot::NUM_BOT_TYPES; j++ ) { if( !strcmp(m_typeString[j], setting->getString(i+1)) ) { return j; } } ASSERT( false, "%s \"%s\" is not a valid bot ID " "in file %s\n", fn, setting->getString(i+1), mapFile.getString() ); } } CHECK( false, "%s file \"%s\" defines no class for " "bot \"%s\"\n", fn, mapFile.getString(), botName ); CHECK( false, "Valid bot names are:\n" ); // print list of valid names for( int i = 0; i < setting->getNumParameters(); i += 2 ) { CHECK( false, " (%d) \"%s\"\n", i, setting->getString(i) ); } } return UNDEFINED_BOT; } /**********************************************************/ bool Bot::loadName2classIDsettings( SettingDataBase& settings, const String &mapFile ) { const char fn[] = "Bot::loadName2classIDsettings"; SettingDef defs[] = { SettingDef( "name2class", 0x0, Setting::STRING, 2, -1, Setting::MANDATORY | Setting::EXPLICIT_ASSIGNMENT ), SettingDef() }; if( CHECK( settings.restrictToSection(SECTION_NAME2CLASS), "%s could not restrict to section \"%s\"\n", fn, SECTION_NAME2CLASS ) && CHECK( settings.readSettings(defs, mapFile.getString()), "%s could not read class map in \"%s\"\n", fn, mapFile.getString() ) && CHECK( settings.finalCheck(defs), "%s failed final check of class map \"%s\"\n", fn, mapFile.getString() ) && CHECK( !(settings.getSetting("name2class")->getNumParameters()&1), "%s the class map must contain PAIRS of strings\n", fn ) ) { return true; } return false; } /**********************************************************/ Object* Bot::getFirstVisibleObject() { Object *object = 0x0; std::vector *const collidables = getAvatar()->m_worldPointer->getCollidables(); for( m_currIndex = 0; m_currIndex < collidables->size(); m_currIndex++ ) { object = (*collidables)[m_currIndex]; if( object->isWeaponVisible() && #ifdef USE_RADIUS_SIGHT (object->getPos() - getAvatar()->getPos()).abs2() <= SIGHT_RADIUS_2 ) { #endif m_currObjVecType = COLLIDABLES; return object; } } std::vector *const noncollidables = getAvatar()->m_worldPointer->getNoncollidables(); for( m_currIndex = 0; m_currIndex < noncollidables->size(); m_currIndex++ ) { object = (*noncollidables)[m_currIndex]; if( object->isWeaponVisible() && #ifdef USE_RADIUS_SIGHT (object->getPos() - getAvatar()->getPos()).abs2() <= SIGHT_RADIUS_2 ) { #endif m_currObjVecType = NONCOLLIDABLES; return object; } } m_currObjVecType = UNDEFINED; return 0x0; } /**********************************************************/ Object* Bot::getNextVisibleObject() { DBG( 3 ) { if( !CHECK(m_currObjVecType != UNDEFINED, "Bot::getNextVisibleObject: invalid current iterator " "position\n") ) { return 0x0; } } Object *object = 0x0; if( m_currObjVecType == COLLIDABLES ) { std::vector *const collidables = getAvatar()->m_worldPointer->getCollidables(); while( m_currIndex < collidables->size() ) { object = (*collidables)[m_currIndex]; if( object->isWeaponVisible() && #ifdef USE_RADIUS_SIGHT (object->getPos() - getAvatar()->getPos()).abs2() <= SIGHT_RADIUS_2 ) { #endif return object; } m_currIndex++; } m_currObjVecType = NONCOLLIDABLES; m_currIndex = 0; } if( m_currObjVecType == NONCOLLIDABLES ) { std::vector *const noncollidables = getAvatar()->m_worldPointer->getNoncollidables(); while( m_currIndex < noncollidables->size() ) { object = (*noncollidables)[m_currIndex]; if( object->isWeaponVisible() && #ifdef USE_RADIUS_SIGHT (object->getPos() - getAvatar()->getPos()).abs2() <= SIGHT_RADIUS_2 ) { #endif return object; } m_currIndex++; } m_currObjVecType = UNDEFINED; } return 0x0; } /**********************************************************/ void Bot::getVisibleCollidableObjects( std::vector& vector ) { vector.clear(); std::vector* collidables = getAvatar()->m_worldPointer->getCollidables(); for ( unsigned int i = 0; i < collidables->size(); i++ ) { CollidableObject* object = (*collidables)[i]; if ( object->isWeaponVisible() && ( ! object->isAvatar() || dynamic_cast( object )->isLiving()) && #ifdef USE_RADIUS_SIGHT (object->getPos() - getAvatar()->getPos()).abs2() <= SIGHT_RADIUS_2 ) { #endif vector.push_back( object ); } } } /**********************************************************/ void Bot::getVisibleNonCollidableObjects( std::vector& vector ) { vector.clear(); std::vector* noncollidables = getAvatar()->m_worldPointer->getNoncollidables(); for ( unsigned int i = 0; i < noncollidables->size(); i++ ) { NonCollidableObject* object = (*noncollidables)[i]; if ( object->isWeaponVisible() && #ifdef USE_RADIUS_SIGHT (object->getPos() - getAvatar()->getPos()).abs2() <= SIGHT_RADIUS_2 ) { #endif vector.push_back( object ); } } } /**********************************************************/