1 /* $Id$ */
2 
3 #include  "global.hpp"
4 #include  "bonusmanager.hpp"
5 #include  "world.hpp"
6 #include  "flag.hpp"
7 
8 /**********************************************************/
9 
10 #define  GENERAL_SECTION  "GENERAL"
11 #define  USAGE_SECTION    "USAGE"
12 #define  LIMIT_SECTION    "LIMITS"
13 
14 /**********************************************************/
15 
16 // setting attributes
17 #define  SATTR  (Setting::COMMA_SEPARATED     | \
18                  Setting::EXPLICIT_ASSIGNMENT | \
19                  Setting::CONFIG_FILE_ONLY)
20 #define  SATTRM (SATTR | Setting::MANDATORY)
21 
22 #define  SETTING_STRING(ID)  BonusManager::m_SettingDef[ID].m_IDString
23 
24 /**********************************************************/
25 
26 const SettingDef BonusManager::
27 m_SettingDef[BonusManager::NUM_SETTING_DEFS+1] =
28 {
29 	SettingDef("active",     NULL, Setting::BOOL, 1, 1, SATTRM),
30 	SettingDef("spawndelay", NULL, Setting::INT,  2, 2, SATTRM),
31 	SettingDef()
32 };
33 
34 /**********************************************************/
35 
BonusManager(World * const world,const unsigned int seed)36 BonusManager::BonusManager( World* const       world,
37                             const unsigned int seed   )
38             : m_world( world ),
39               m_rand( seed & 0xffff ),
40               m_active( false ),
41               m_minDelay( -1 ), m_maxDelay( -1 ),
42               m_delayCounter( -1 )
43 {
44 	// create array for limits and registered number of boni
45 	ASSERT( NULL != (m_Limits = NEW int [2*Bonus::getNumTypes()]),
46 	        "BonusManager::BonusManager: could not get int [%d]\n",
47 	        2 * Bonus::getNumTypes() );
48 
49 	for( int i = 0; i < 2*Bonus::getNumTypes(); i++ ) {
50 		m_Limits[i] = 0; }
51 	m_numExisting = m_Limits + Bonus::getNumTypes();
52 }
53 
54 /**********************************************************/
55 
~BonusManager()56 BonusManager::~BonusManager()
57 {
58 	reset();
59 
60 	delete [] m_Limits;
61 }
62 
63 /**********************************************************/
64 
reset()65 void BonusManager::reset()
66 {
67 	for( int i = 0; i < 2*Bonus::getNumTypes(); i++ )  m_Limits[i] = 0;
68 }
69 
70 /**********************************************************/
71 
loadSettings(const char * const filename)72 bool BonusManager::loadSettings( const char* const filename )
73 {
74 	const char fn[] = "BonusManager::loadSettings:";
75 	SettingDataBase settings;
76 	SettingDef      *defs = createSettingDefinitions();
77 
78 	INFO( "%s: loading settings for boni from \"%s\"\n", fn, filename );
79 
80 	// read general settings
81 	settings.restrictToSection( GENERAL_SECTION );
82 	if( !CHECK( settings.readSettings(m_SettingDef, filename),
83 	            "%s could not read standard settings in file \"%s\"\n",
84 	            fn, filename ) ||
85 	    !CHECK( settings.finalCheck(m_SettingDef),
86 	            "%s failed final check of standard settings\n",
87 	            fn ) ) {
88 		delete [] defs;
89 		return false;
90 	}
91 	// get active flag
92 	m_active = settings.getSetting(SETTING_STRING(ACTIV_FLAG_DEF))->getBool();
93 	// check the delay interval
94 	// (setting is present, since we passed the final check)
95 	const Setting* const setting =
96 	    settings.getSetting( SETTING_STRING(SPAWN_DELAY_INTERVAL_DEF) );
97 	m_minDelay     = setting->getInt( 0 );
98 	m_maxDelay     = setting->getInt( 1 );
99 	m_delayCounter = m_maxDelay;
100 	if( !CHECK( m_minDelay <= m_maxDelay && m_minDelay > 0,
101 	            "%s [%d,%d] is not a valid range for spawn delays. "
102 	            "Loaded from \"%s\"\n",
103 	            fn, m_minDelay, m_maxDelay, filename ) ) {
104 		delete [] defs;
105 		return false;
106 	}
107 
108 	// read the settings for usage of boni
109 	settings.restrictToSection( USAGE_SECTION );
110 	if( !CHECK( settings.readSettings(defs, filename),
111 	            "%s could not read settings from section \""
112 	            USAGE_SECTION"\" in file \"%s\"\n",
113 	            fn, filename ) ) {
114 		delete [] defs;
115 		return false;
116 	}
117 
118 	// abuse the limits array for buffering temporarily the weights
119 	for( int i = 0; i < Bonus::getNumTypes(); i++ ) {
120 		Setting *setting2 = settings.getSetting( defs[i].m_IDString );
121 		m_Limits[i] = setting2 != NULL ? setting2->getInt() : 0;
122 	}
123 	// insert the weights into the internal random number generator
124 	if( !CHECK( m_rand.setWeights(Bonus::getNumTypes(), m_Limits),
125 	            "%s could not set usage weights\n", fn ) ) {
126 		delete [] defs;
127 		return false;
128 	}
129 
130 	// read settings for number limits
131 	settings.reset();
132 	settings.restrictToSection( LIMIT_SECTION );
133 	if( !CHECK( settings.readSettings(defs, filename),
134 	            "%s could not read settings from section \""
135 	            LIMIT_SECTION"\" in file \"%s\"\n",
136 	            fn, filename ) ) {
137 		delete [] defs;
138 		return false;
139 	}
140 	// set the limits for the number of bonus objects
141 	for( int i = 0; i < Bonus::getNumTypes(); i++ ) {
142 		Setting *setting2 = settings.getSetting( defs[i].m_IDString );
143 		m_Limits[i] = setting2 != NULL ? setting2->getInt() : -1;
144 	}
145 
146 	delete [] defs;
147 
148 	return true;
149 }
150 
151 /**********************************************************/
152 
selectRandomBonusID()153 int BonusManager::selectRandomBonusID()
154 {
155 	// get a weighted random number
156 	int newID = m_rand.getWeightedUint32();
157 	// if the selected bonus type is not limited, or the
158 	// maximal number is not yet reached, ...
159 	if( m_Limits[newID] < 0 ||
160 	    m_Limits[newID] > m_numExisting[newID] ) {
161 		// ... translate the random number into a valid object ID
162 		newID += FIRST_BONUS;
163 	// random number cannot be used
164 	} else {
165 		// set ID to -1, to signalize, that we did not create
166 		// a valid one
167 		newID = -1;
168 	}
169 
170 	return newID;
171 }
172 
173 /**********************************************************/
174 
updatePlacement()175 void BonusManager::updatePlacement()
176 {
177 	// check, if the bonus system is activated
178 	if( !m_active )  return;
179 	// check, if we need a new bonus
180 	if( --m_delayCounter > 0 )  return;
181 
182 	// get the ID of a new bonus
183 	const int bonusID = selectRandomBonusID();
184 	if( bonusID < 0 )  return;
185 
186 	// get the new bonus object from the world
187 	Bonus* bonus = dynamic_cast<Bonus*>( m_world->newObject(bonusID) );
188 	DBG( 3 ) ASSERT( bonus, "BonusManager::updatePlacement: received "
189 	                 "a non-bonus object\n" );
190 
191 	// try to place the new bonus object
192 	const Uint32 minPosXY = 30,
193 	             maxPosX  = m_world->getMap()->getSizeX() - 30,
194 	             maxPosY  = m_world->getMap()->getSizeY() - 30;
195 	// start only a finite number of tries
196 	for( int nTries = 0; nTries < 20; nTries++ ) {
197 		// get a proposal for the new position
198 		Uint32 posX = m_rand.getUint32Between( minPosXY, maxPosX ),
199 		       posY = m_rand.getUint32Between( minPosXY, maxPosY );
200 		// check the map at this position
201 		if( 0 == (Flags::UNABLE_ANY &
202 		    m_world->getMap()->getOrCombinedFlagsInRect(
203 		        posX + bonus->getCollRectX(),
204 		        posY + bonus->getCollRectY(),
205 		        posX + bonus->getCollRectX() + bonus->getCollRectWidth(),
206 		        posY + bonus->getCollRectY() + bonus->getCollRectHeight())) ) {
207 			// this is a valid place for the new bonus object
208 			// -> first digg a hole, that is large enoug for the bonus
209 			m_world->getMap()->makeHole(
210 			    posX, posY,
211 			       bonus->getCollRectWidth() > bonus->getCollRectHeight()
212 			     ? bonus->getCollRectWidth() : bonus->getCollRectHeight(),
213 			    Flags::UNDIGGABLE );
214 			// place the new bonus inside this hole
215 			bonus->setPos( posX, posY );
216 			// set the collission rectangle for the first time
217 			bonus->setCollRect();
218 			// reset the delay counter
219 			m_delayCounter = m_rand.getUint32Between( m_minDelay, m_maxDelay );
220 			LOG( 2 ) INFO( "BonusManager::updatePlacement: placed bonus \"%s\" "
221 			               "at position (%d,%d)\n",
222 			               ObjectIDString[bonus->getID()], posX, posY );
223 			// leave the function
224 			return;
225 		} else {
226 			LOG( 3 ) INFO( "BonusManager::updatePlacement: could not place "
227 			               "bonus \"%s\" at position (%d,%d)\n",
228 			               ObjectIDString[bonus->getID()], posX, posY );
229 		}
230 	}
231 
232 	// we could not find an appropriate place for the bonus
233 	// => delete it and hope for more success in the next try
234 	m_world->deleteObject( bonus ); // ok
235 }
236 
237 /**********************************************************/
238 
registerBonusEntrance(Bonus & bonus)239 void BonusManager::registerBonusEntrance( Bonus &bonus )
240 {
241 	if( bonus.getRegistrationFlag() == false ) {
242 		// inkrement number of registered objects of this ID
243 		m_numExisting[bonus.getID() - FIRST_BONUS]++;
244 		// set the registration flag
245 		bonus.setRegistrationFlag( true );
246 		// check in debug mode
247 		DBG( 1 ) {
248 			if( !CHECK(m_Limits[bonus.getID() - FIRST_BONUS] >= 0 &&
249 			           m_numExisting[bonus.getID() - FIRST_BONUS] <=
250 			           m_Limits[bonus.getID() - FIRST_BONUS],
251 			           "BonusManager::registerBonusEntrance:\n  "
252 			           "registered %d boni of type \"%s\", but maximum "
253 			           "is %d\n",
254 			           m_numExisting[bonus.getID() - FIRST_BONUS],
255 			           ObjectIDString[bonus.getID()],
256 			           m_Limits[bonus.getID() - FIRST_BONUS]) ) {
257 				CHECK( bonus.getID() != BONUS_SHIELD,
258 				       "the previous warning may be neglected,\n  "
259 				       "since a shield bonus is created at each "
260 				       "avatar's respawn\n" );
261 			}
262 		}
263 	}
264 }
265 
266 /**********************************************************/
267 
registerBonusExit(Bonus & bonus)268 void BonusManager::registerBonusExit( Bonus &bonus )
269 {
270 	if( bonus.getRegistrationFlag() == true ) {
271 		// dekrement number of registered object of this ID
272 		m_numExisting[bonus.getID() - FIRST_BONUS]--;
273 		// delete the registration flag
274 		bonus.setRegistrationFlag( false );
275 		// check in debug mode
276 		DBG( 1 ) {
277 			CHECK( m_numExisting[bonus.getID() - FIRST_BONUS] >= 0,
278 			       "BonusManager::registerBonusExit: now registered"
279 			       " %d boni of type \"%s\". Hey dude, this is a "
280 			       "number < 0.\n",
281 			       m_numExisting[bonus.getID() - FIRST_BONUS],
282 			       ObjectIDString[bonus.getID()] );
283 		}
284 	}
285 }
286 
287 /**********************************************************/
288 
getSerializeBufferSize() const289 Uint32 BonusManager::getSerializeBufferSize() const
290 {
291 	return   m_rand.getSerializeBufferSize()
292 	       + Serialize<bool>::sizeOf( m_active )
293 	       + Serialize<Sint32>::sizeOf( m_minDelay )
294 	       + Serialize<Sint32>::sizeOf( m_maxDelay )
295 	       + Serialize<Sint32>::sizeOf( m_delayCounter )
296 	       + Bonus::getNumTypes() * Serialize<Sint32>::sizeOf()
297 	         // adds additional size for debugging (see serialize.hpp),
298 	         // but only, if the tags are used
299 	         PLUS_TAG_SIZE( 2 );
300 }
301 
302 /**********************************************************/
303 
serialize(Uint8 * & bufferPointer) const304 void BonusManager::serialize( Uint8*& bufferPointer ) const
305 {
306 	START_SERIALIZED_SIZE_CHECK( bufferPointer );
307 	// expands to tag serialization
308 	SERIALIZE_TAG( bufferPointer );
309 
310 	// serialize the random number generator
311 	m_rand.serialize( bufferPointer );
312 
313 	Serialize<bool  >::serialize( m_active,       bufferPointer );
314 	Serialize<Sint32>::serialize( m_minDelay,     bufferPointer );
315 	Serialize<Sint32>::serialize( m_maxDelay,     bufferPointer );
316 	Serialize<Sint32>::serialize( m_delayCounter, bufferPointer );
317 
318 	// serialize the array for random weights and limits
319 	Serialize<Sint32>::serialize( Bonus::getNumTypes(),
320 	                           m_Limits,
321 	                           bufferPointer );
322 
323 	// expands to tag serialization
324 	SERIALIZE_TAG( bufferPointer );
325 	END_SERIALIZED_SIZE_CHECK( bufferPointer, BonusManager );
326 }
327 
328 /**********************************************************/
329 
deserialize(Uint8 * & bufferPointer)330 void BonusManager::deserialize( Uint8*& bufferPointer )
331 {
332 	// expands to tag deserialization
333 	DESERIALIZE_TAG( bufferPointer );
334 
335 	// deserialize the random number generator
336 	m_rand.deserialize( bufferPointer );
337 
338 	Serialize<bool  >::deserialize( bufferPointer, m_active       );
339 	Serialize<Sint32>::deserialize( bufferPointer, m_minDelay	    );
340 	Serialize<Sint32>::deserialize( bufferPointer, m_maxDelay	    );
341 	Serialize<Sint32>::deserialize( bufferPointer, m_delayCounter );
342 
343 	// deserialize the array for random weights and limits
344 	Serialize<Sint32>::deserialize( Bonus::getNumTypes(),
345 	                             bufferPointer,
346 	                             m_Limits );
347 	// reset the number of present boni
348 	for( int i = 0; i < Bonus::getNumTypes(); i++ ) {
349 		m_numExisting[i] = 0;
350 	}
351 
352 	// expands to tag deserialization
353 	DESERIALIZE_TAG( bufferPointer );
354 }
355 
356 /**********************************************************/
357 
createSettingDefinitions()358 SettingDef* BonusManager::createSettingDefinitions()
359 {
360 	// create the array
361 	// NOTE that we also must create a terminating, empty definition
362 	SettingDef *defs = NEW SettingDef [Bonus::getNumTypes()+1];
363 	ASSERT( defs != NULL,
364 	        "BonusManager::createSettingDefinitions: could not "
365 	        "create SettingDef[%d]\n", Bonus::getNumTypes()+1 );
366 
367 	// fill the definitions (they only differ in their ID string)
368 	for( int i = 0; i < Bonus::getNumTypes(); i++ ) {
369 		defs[i].m_IDString     = ObjectIDString[FIRST_BONUS + i];
370 		defs[i].m_Type         = Setting::INT;
371 		defs[i].m_minNumParams = 1;
372 		defs[i].m_maxNumParams = 1;
373 		defs[i].m_Attributes   = Setting::EXPLICIT_ASSIGNMENT;
374 	}
375 
376 	return defs;
377 }
378 
379 /**********************************************************/
380 
381