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