/* $Id$ */ #include "global.hpp" #include "bonusmanager.hpp" #include "world.hpp" #include "flag.hpp" /**********************************************************/ #define GENERAL_SECTION "GENERAL" #define USAGE_SECTION "USAGE" #define LIMIT_SECTION "LIMITS" /**********************************************************/ // setting attributes #define SATTR (Setting::COMMA_SEPARATED | \ Setting::EXPLICIT_ASSIGNMENT | \ Setting::CONFIG_FILE_ONLY) #define SATTRM (SATTR | Setting::MANDATORY) #define SETTING_STRING(ID) BonusManager::m_SettingDef[ID].m_IDString /**********************************************************/ const SettingDef BonusManager:: m_SettingDef[BonusManager::NUM_SETTING_DEFS+1] = { SettingDef("active", NULL, Setting::BOOL, 1, 1, SATTRM), SettingDef("spawndelay", NULL, Setting::INT, 2, 2, SATTRM), SettingDef() }; /**********************************************************/ BonusManager::BonusManager( World* const world, const unsigned int seed ) : m_world( world ), m_rand( seed & 0xffff ), m_active( false ), m_minDelay( -1 ), m_maxDelay( -1 ), m_delayCounter( -1 ) { // create array for limits and registered number of boni ASSERT( NULL != (m_Limits = NEW int [2*Bonus::getNumTypes()]), "BonusManager::BonusManager: could not get int [%d]\n", 2 * Bonus::getNumTypes() ); for( int i = 0; i < 2*Bonus::getNumTypes(); i++ ) { m_Limits[i] = 0; } m_numExisting = m_Limits + Bonus::getNumTypes(); } /**********************************************************/ BonusManager::~BonusManager() { reset(); delete [] m_Limits; } /**********************************************************/ void BonusManager::reset() { for( int i = 0; i < 2*Bonus::getNumTypes(); i++ ) m_Limits[i] = 0; } /**********************************************************/ bool BonusManager::loadSettings( const char* const filename ) { const char fn[] = "BonusManager::loadSettings:"; SettingDataBase settings; SettingDef *defs = createSettingDefinitions(); INFO( "%s: loading settings for boni from \"%s\"\n", fn, filename ); // read general settings settings.restrictToSection( GENERAL_SECTION ); if( !CHECK( settings.readSettings(m_SettingDef, filename), "%s could not read standard settings in file \"%s\"\n", fn, filename ) || !CHECK( settings.finalCheck(m_SettingDef), "%s failed final check of standard settings\n", fn ) ) { delete [] defs; return false; } // get active flag m_active = settings.getSetting(SETTING_STRING(ACTIV_FLAG_DEF))->getBool(); // check the delay interval // (setting is present, since we passed the final check) const Setting* const setting = settings.getSetting( SETTING_STRING(SPAWN_DELAY_INTERVAL_DEF) ); m_minDelay = setting->getInt( 0 ); m_maxDelay = setting->getInt( 1 ); m_delayCounter = m_maxDelay; if( !CHECK( m_minDelay <= m_maxDelay && m_minDelay > 0, "%s [%d,%d] is not a valid range for spawn delays. " "Loaded from \"%s\"\n", fn, m_minDelay, m_maxDelay, filename ) ) { delete [] defs; return false; } // read the settings for usage of boni settings.restrictToSection( USAGE_SECTION ); if( !CHECK( settings.readSettings(defs, filename), "%s could not read settings from section \"" USAGE_SECTION"\" in file \"%s\"\n", fn, filename ) ) { delete [] defs; return false; } // abuse the limits array for buffering temporarily the weights for( int i = 0; i < Bonus::getNumTypes(); i++ ) { Setting *setting2 = settings.getSetting( defs[i].m_IDString ); m_Limits[i] = setting2 != NULL ? setting2->getInt() : 0; } // insert the weights into the internal random number generator if( !CHECK( m_rand.setWeights(Bonus::getNumTypes(), m_Limits), "%s could not set usage weights\n", fn ) ) { delete [] defs; return false; } // read settings for number limits settings.reset(); settings.restrictToSection( LIMIT_SECTION ); if( !CHECK( settings.readSettings(defs, filename), "%s could not read settings from section \"" LIMIT_SECTION"\" in file \"%s\"\n", fn, filename ) ) { delete [] defs; return false; } // set the limits for the number of bonus objects for( int i = 0; i < Bonus::getNumTypes(); i++ ) { Setting *setting2 = settings.getSetting( defs[i].m_IDString ); m_Limits[i] = setting2 != NULL ? setting2->getInt() : -1; } delete [] defs; return true; } /**********************************************************/ int BonusManager::selectRandomBonusID() { // get a weighted random number int newID = m_rand.getWeightedUint32(); // if the selected bonus type is not limited, or the // maximal number is not yet reached, ... if( m_Limits[newID] < 0 || m_Limits[newID] > m_numExisting[newID] ) { // ... translate the random number into a valid object ID newID += FIRST_BONUS; // random number cannot be used } else { // set ID to -1, to signalize, that we did not create // a valid one newID = -1; } return newID; } /**********************************************************/ void BonusManager::updatePlacement() { // check, if the bonus system is activated if( !m_active ) return; // check, if we need a new bonus if( --m_delayCounter > 0 ) return; // get the ID of a new bonus const int bonusID = selectRandomBonusID(); if( bonusID < 0 ) return; // get the new bonus object from the world Bonus* bonus = dynamic_cast( m_world->newObject(bonusID) ); DBG( 3 ) ASSERT( bonus, "BonusManager::updatePlacement: received " "a non-bonus object\n" ); // try to place the new bonus object const Uint32 minPosXY = 30, maxPosX = m_world->getMap()->getSizeX() - 30, maxPosY = m_world->getMap()->getSizeY() - 30; // start only a finite number of tries for( int nTries = 0; nTries < 20; nTries++ ) { // get a proposal for the new position Uint32 posX = m_rand.getUint32Between( minPosXY, maxPosX ), posY = m_rand.getUint32Between( minPosXY, maxPosY ); // check the map at this position if( 0 == (Flags::UNABLE_ANY & m_world->getMap()->getOrCombinedFlagsInRect( posX + bonus->getCollRectX(), posY + bonus->getCollRectY(), posX + bonus->getCollRectX() + bonus->getCollRectWidth(), posY + bonus->getCollRectY() + bonus->getCollRectHeight())) ) { // this is a valid place for the new bonus object // -> first digg a hole, that is large enoug for the bonus m_world->getMap()->makeHole( posX, posY, bonus->getCollRectWidth() > bonus->getCollRectHeight() ? bonus->getCollRectWidth() : bonus->getCollRectHeight(), Flags::UNDIGGABLE ); // place the new bonus inside this hole bonus->setPos( posX, posY ); // set the collission rectangle for the first time bonus->setCollRect(); // reset the delay counter m_delayCounter = m_rand.getUint32Between( m_minDelay, m_maxDelay ); LOG( 2 ) INFO( "BonusManager::updatePlacement: placed bonus \"%s\" " "at position (%d,%d)\n", ObjectIDString[bonus->getID()], posX, posY ); // leave the function return; } else { LOG( 3 ) INFO( "BonusManager::updatePlacement: could not place " "bonus \"%s\" at position (%d,%d)\n", ObjectIDString[bonus->getID()], posX, posY ); } } // we could not find an appropriate place for the bonus // => delete it and hope for more success in the next try m_world->deleteObject( bonus ); // ok } /**********************************************************/ void BonusManager::registerBonusEntrance( Bonus &bonus ) { if( bonus.getRegistrationFlag() == false ) { // inkrement number of registered objects of this ID m_numExisting[bonus.getID() - FIRST_BONUS]++; // set the registration flag bonus.setRegistrationFlag( true ); // check in debug mode DBG( 1 ) { if( !CHECK(m_Limits[bonus.getID() - FIRST_BONUS] >= 0 && m_numExisting[bonus.getID() - FIRST_BONUS] <= m_Limits[bonus.getID() - FIRST_BONUS], "BonusManager::registerBonusEntrance:\n " "registered %d boni of type \"%s\", but maximum " "is %d\n", m_numExisting[bonus.getID() - FIRST_BONUS], ObjectIDString[bonus.getID()], m_Limits[bonus.getID() - FIRST_BONUS]) ) { CHECK( bonus.getID() != BONUS_SHIELD, "the previous warning may be neglected,\n " "since a shield bonus is created at each " "avatar's respawn\n" ); } } } } /**********************************************************/ void BonusManager::registerBonusExit( Bonus &bonus ) { if( bonus.getRegistrationFlag() == true ) { // dekrement number of registered object of this ID m_numExisting[bonus.getID() - FIRST_BONUS]--; // delete the registration flag bonus.setRegistrationFlag( false ); // check in debug mode DBG( 1 ) { CHECK( m_numExisting[bonus.getID() - FIRST_BONUS] >= 0, "BonusManager::registerBonusExit: now registered" " %d boni of type \"%s\". Hey dude, this is a " "number < 0.\n", m_numExisting[bonus.getID() - FIRST_BONUS], ObjectIDString[bonus.getID()] ); } } } /**********************************************************/ Uint32 BonusManager::getSerializeBufferSize() const { return m_rand.getSerializeBufferSize() + Serialize::sizeOf( m_active ) + Serialize::sizeOf( m_minDelay ) + Serialize::sizeOf( m_maxDelay ) + Serialize::sizeOf( m_delayCounter ) + Bonus::getNumTypes() * Serialize::sizeOf() // adds additional size for debugging (see serialize.hpp), // but only, if the tags are used PLUS_TAG_SIZE( 2 ); } /**********************************************************/ void BonusManager::serialize( Uint8*& bufferPointer ) const { START_SERIALIZED_SIZE_CHECK( bufferPointer ); // expands to tag serialization SERIALIZE_TAG( bufferPointer ); // serialize the random number generator m_rand.serialize( bufferPointer ); Serialize::serialize( m_active, bufferPointer ); Serialize::serialize( m_minDelay, bufferPointer ); Serialize::serialize( m_maxDelay, bufferPointer ); Serialize::serialize( m_delayCounter, bufferPointer ); // serialize the array for random weights and limits Serialize::serialize( Bonus::getNumTypes(), m_Limits, bufferPointer ); // expands to tag serialization SERIALIZE_TAG( bufferPointer ); END_SERIALIZED_SIZE_CHECK( bufferPointer, BonusManager ); } /**********************************************************/ void BonusManager::deserialize( Uint8*& bufferPointer ) { // expands to tag deserialization DESERIALIZE_TAG( bufferPointer ); // deserialize the random number generator m_rand.deserialize( bufferPointer ); Serialize::deserialize( bufferPointer, m_active ); Serialize::deserialize( bufferPointer, m_minDelay ); Serialize::deserialize( bufferPointer, m_maxDelay ); Serialize::deserialize( bufferPointer, m_delayCounter ); // deserialize the array for random weights and limits Serialize::deserialize( Bonus::getNumTypes(), bufferPointer, m_Limits ); // reset the number of present boni for( int i = 0; i < Bonus::getNumTypes(); i++ ) { m_numExisting[i] = 0; } // expands to tag deserialization DESERIALIZE_TAG( bufferPointer ); } /**********************************************************/ SettingDef* BonusManager::createSettingDefinitions() { // create the array // NOTE that we also must create a terminating, empty definition SettingDef *defs = NEW SettingDef [Bonus::getNumTypes()+1]; ASSERT( defs != NULL, "BonusManager::createSettingDefinitions: could not " "create SettingDef[%d]\n", Bonus::getNumTypes()+1 ); // fill the definitions (they only differ in their ID string) for( int i = 0; i < Bonus::getNumTypes(); i++ ) { defs[i].m_IDString = ObjectIDString[FIRST_BONUS + i]; defs[i].m_Type = Setting::INT; defs[i].m_minNumParams = 1; defs[i].m_maxNumParams = 1; defs[i].m_Attributes = Setting::EXPLICIT_ASSIGNMENT; } return defs; } /**********************************************************/