Add infos into target window
[ryzomcore.git] / ryzom / server / src / entities_game_service / deposit.cpp
bloba234e177f921a0b976d687cf2fa0c966bad6d08c
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "stdpch.h"
19 #include "deposit.h"
20 #include "player_manager/character.h"
21 #include "player_manager/player_manager.h"
22 #include "player_manager/player.h"
23 #include "zone_manager.h"
24 #include "entities_game_service.h"
25 #include "egs_globals.h"
26 #include "nel/misc/noise_value.h"
27 #include "nel/misc/variable.h"
28 #include "nel/misc/words_dictionary.h"
29 #include "game_share/time_weather_season/time_date_season_manager.h"
30 #include "game_share/people.h"
31 #include "egs_sheets/egs_sheets.h"
32 #include "phrase_manager/fg_prospection_phrase.h"
33 #include "phrase_manager/phrase_utilities_functions.h"
34 #include "game_share/send_chat.h"
35 #include "game_share/multi_target.h"
36 #include "phrase_manager/s_effect.h"
37 #include "projectile_stats.h"
39 using namespace std;
40 using namespace NLMISC;
41 using namespace NLLIGO;
42 using namespace NLNET;
43 using namespace RM_FAMILY;
46 const float SmallDepositAreaThreshold = 64.0f*64.0f;
47 const float SmallDepositAreaReference = 16.0f*32.0f;
49 // The ecotype zones (only valid at init, cleared after deposit built)
50 CEcotypeZones CDeposit::_EcotypeZones;
52 NL_INSTANCE_COUNTER_IMPL(CAutoSpawnProperties);
53 NL_INSTANCE_COUNTER_IMPL(CQuantityConstraints);
55 // Verbose deposits debug variable
56 //bool VerboseDeposits = false;
58 // Total number of CRecentForageSite objects (debug info)
59 uint32 TotalNbRecentForageSites = 0;
61 /// Export deposit contents report
62 bool ExportDepositContents = false;
64 /// Verbose the items parsed when filtering deposits
65 bool VerboseDepositFiltering = false;
68 void cbChangeDepositUpdateFrequency( NLMISC::IVariable& v )
70 /*const TGameCycle expectedTTL = 6000; // 10 min
71 if ( DepositUpdateFrequency.get() != 0 )
72 ForageSiteNbUpdatesToLive.set( (uint16)(expectedTTL / DepositUpdateFrequency.get()) );*/
73 nlinfo( "TODO: change ForageSiteNbUpdatesToLive according to DepositUpdateFrequency" );
78 * Utility functions
81 bool malformed( const char *field, const string& name )
83 nlwarning( "FG: Malformed primitive %s, missing field '%s'", name.c_str(), field );
84 return false;
88 * Get the enums of the families (using RM_FAMILY::toFamily(), assumes the string are ended by _value)
89 * where value is the identifier number (see .typ).
90 * Removes duplicates (with a warning).
92 void convertRMFamiliesNames( const string& name, const vector<string>& src, vector<TRMFamily>& dest )
94 for ( uint i=0; i!=src.size(); ++i )
96 if ( ! src[i].empty() )
98 string::size_type p = src[i].find_last_of( '_' );
99 if ( p != string::npos )
101 TRMFamily family = toFamily( src[i].substr( p + 1 ) );
102 if ( find( dest.begin(), dest.end(), family ) == dest.end() )
103 dest.push_back( family );
104 else
105 nlwarning( "FG: Deposit %s: %s found twice", name.c_str(), src[i].c_str() );
107 else
109 nlwarning( "FG: Deposit %s: %s: not found", name.c_str(), src[i].c_str() );
116 * Get the indices of the item parts (assumes the strings are ended by _index).
117 * Ex: blade_0.
118 * Removes duplicates (with a warning).
120 void convertItemPartsNames( const string& name, const vector<string>& src, vector<uint>& dest )
122 for ( uint i=0; i!=src.size(); ++i )
124 if ( ! src[i].empty() )
126 string::size_type p = src[i].find_last_of( '_' );
127 if ( p != string::npos )
129 uint itemPart;
130 NLMISC::fromString(src[i].substr( p + 1 ), itemPart);
131 if ( find( dest.begin(), dest.end(), itemPart ) == dest.end() )
132 dest.push_back( itemPart );
133 else
134 nlwarning( "FG: Deposit %s: %s found twice", name.c_str(), src[i].c_str() );
136 else
138 nlwarning( "FG: Deposit %s: %s: not found", name.c_str(), src[i].c_str() );
145 * Get the civ enum.
147 void convertCraftCivNames( const vector<string>& src, vector<ITEM_ORIGIN::EItemOrigin>& dest )
149 dest.resize( src.size() );
150 for ( uint i=0; i!=src.size(); ++i )
152 dest[i] = ITEM_ORIGIN::stringToEnum( src[i] );
160 inline void makeFullSItemCode( string& code )
162 if ( code.find( ".sitem" ) == string::npos )
164 code += ".sitem";
170 * Return always a forage site
172 CRecentForageSite *CDeposit::findOrCreateForageSite( const NLMISC::CVector& pos )
174 // Search in existing ones
175 for ( CRecentForageSites::iterator ihs=_RecentForageSites.begin(); ihs!=_RecentForageSites.end(); ++ihs )
177 if ( (*ihs).contains( pos ) )
179 return &(*ihs);
183 // Not found, create one
184 ++TotalNbRecentForageSites;
185 CRecentForageSite newForageSite( this, pos );
186 _RecentForageSites.push_back( newForageSite );
187 return &_RecentForageSites.back(); // returning the pointer is valid because an element in std::list is never invalidated
194 bool CRecentForageSite::contains( const NLMISC::CVector& pos ) const
196 float r = ForageSiteRadius.get();
197 return ((pos-_Pos).sqrnorm() < r*r);
202 * Display debug or stat info
204 void CRecentForageSite::display( NLMISC::CLog& log ) const
206 log.displayNL( "Forage site at %s: %u sources, stock %u, %u lowfreq updates remaining",
207 _Pos.asString().c_str(), _NbActiveSources, _LowScopeStock, _TimeToLive );
211 //-----------------------------------------------------------------------------
212 // Parse primitive file for one ecotype
213 //-----------------------------------------------------------------------------
214 bool CEcotypeZone::build( const NLLIGO::CPrimZone* zone )
216 *( (NLLIGO::CPrimZone*)this ) = *zone;
218 // Read primitive name
219 string name;
220 if ( ! zone->getPropertyByName( "name", name ) ) return malformed( "name", name );
222 // Read ecotype
223 string ecotypeS;
224 if ( ! zone->getPropertyByName( "ecotype", ecotypeS ) ) return malformed( "ecotype", name );
225 _Ecotype = ECOSYSTEM::stringToEcosystem( ecotypeS ); // needs a case-unsensitive comparison, because the
226 if ( _Ecotype == ECOSYSTEM::unknown ) // World Editor lists names of files in CVS (in deposit_system/ecotypes)
227 nlwarning( "%s: Ecotype %s unknown", name.c_str(), ecotypeS.c_str() );
228 return true;
233 * Destructor
235 CDeposit::~CDeposit()
237 if ( _AutoSpawnSourcePt )
238 delete _AutoSpawnSourcePt;
239 _AutoSpawnSourcePt = NULL;
240 if ( _QuantityConstraintsPt )
241 delete _QuantityConstraintsPt;
242 _QuantityConstraintsPt = NULL;
244 // Avoid any bug, remove the deposit from zone manager
245 CZoneManager::getInstance().unregisterDepositToAutoSpawnUpdate(this);
250 * Get the ecotype zone under the position.
251 * This information is valid only at init time. After the deposits are built, this information
252 * can be found only in deposits.
253 * If not found, a NULL pointer is returned.
255 CEcotypeZone *CDeposit::getEcotypeZone( const NLMISC::CVector& pos )
257 // The ecotypes must not be overlapped: only the first one found is returned
258 for ( CEcotypeZones::iterator it=_EcotypeZones.begin(); it!=_EcotypeZones.end(); ++it )
260 CEcotypeZone *ecotypeZone = (*it);
261 if ( ecotypeZone->contains( pos ) )
263 return ecotypeZone;
266 return NULL;
271 * Clear ecotype information, after having built the deposits
273 void CDeposit::clearEcotypes()
275 for ( CEcotypeZones::iterator iez=_EcotypeZones.begin(); iez!=_EcotypeZones.end(); ++iez )
277 delete (*iez);
279 _EcotypeZones.clear();
283 struct TCompareStaticItemPtrBySheetId : public std::binary_function<CStaticItem*,CStaticItem*,bool>
285 bool operator() ( const CStaticItem* p1, const CStaticItem* p2 )
287 return (p1->SheetId < p2->SheetId);
292 //-----------------------------------------------------------------------------
293 // Parse primitive file for one deposit
294 //-----------------------------------------------------------------------------
295 bool CDeposit::build( const NLLIGO::CPrimZone* zone )
297 if ( IsRingShard )
298 return false;
300 //_Id =id;
301 *( (NLLIGO::CPrimZone*)this ) = *zone;
303 // Read primitive name
304 string name;
305 if ( ! zone->getPropertyByName( "name", name ) ) return malformed( "name", name );
306 _Name = name;
308 // Read exact raw material codes to add
309 vector<string> *exactRMCodesS = NULL;
310 if ( ! (zone->getPropertyByName( "exact_mp_item", exactRMCodesS ) && exactRMCodesS) ) return malformed( "exact_mp_item", name );
312 // Read raw material family filter
313 vector<string> *rmFamilyFilterS = NULL;
314 if ( ! (zone->getPropertyByName( "mps", rmFamilyFilterS ) && rmFamilyFilterS) ) return malformed( "mps", name );
316 // Read item part / craft civ filter
317 vector<string> *itemPartsFilterS = NULL, *craftCivS = NULL;
318 if ( ! (zone->getPropertyByName( "item_parts", itemPartsFilterS ) && itemPartsFilterS) ) return malformed( "item_parts", name );
319 if ( ! (zone->getPropertyByName( "craft_civ", craftCivS ) && craftCivS) ) return malformed( "craft_civ", name );
321 // Read stat+quality filters
322 string minEnergyS, maxEnergyS, minQualityS, maxQualityS;
323 if ( ! zone->getPropertyByName( "deposit_statquality_min", minEnergyS ) ) return malformed( "deposit_statquality_min", name );
324 if ( ! zone->getPropertyByName( "deposit_statquality_max", maxEnergyS ) ) return malformed( "deposit_statquality_max", name );
325 if ( ! zone->getPropertyByName( "deposit_min_quality_250", minQualityS ) ) return malformed( "deposit_min_quality_250", name );
326 if ( ! zone->getPropertyByName( "deposit_max_quality_250", maxQualityS ) ) return malformed( "deposit_max_quality_250", name );
327 NLMISC::fromString(minQualityS, _MinQuality);
328 NLMISC::fromString(maxQualityS, _MaxQuality);
330 // Read quantity constraints
331 string qttyLimitS, qttyRespawnTimeS;
332 if ( ! zone->getPropertyByName( "deposit_quantity_limit", qttyLimitS ) ) return malformed( "deposit_quantity_limit", name );
333 if ( ! zone->getPropertyByName( "deposit_quantity_respawn_time_ryzomdays", qttyRespawnTimeS ) ) return malformed( "deposit_quantity_respawn_time_ryzomdays", name );
334 sint qttyLimit;
335 NLMISC::fromString(qttyLimitS, qttyLimit);
336 if ( qttyLimit > -1 )
338 sint qttyRespawnTime;
339 NLMISC::fromString(qttyRespawnTimeS, qttyRespawnTime);
340 if ( (qttyLimit == 0) || (qttyLimit > 0xFFFF) || (qttyRespawnTime < 1) || (qttyRespawnTime > 0xFFFF) )
341 nlwarning( "Invalid limit or respawn time too high in %s", name.c_str() );
342 else
344 _QuantityConstraintsPt = new CQuantityConstraints();
345 _QuantityConstraintsPt->CurrentQuantity = (float)qttyLimit;
346 _QuantityConstraintsPt->InitialQuantity = (uint16)qttyLimit;
347 _QuantityConstraintsPt->RespawnTimeRyzomDays = qttyRespawnTime;
351 // Get ecotype
352 CEcotypeZone *ecotypeZone = getEcotypeZone( getBarycentre() );
353 _Ecotype = ecotypeZone ? ecotypeZone->ecotype() : ECOSYSTEM::common_ecosystem;
354 if ( ! ecotypeZone )
355 nlwarning( "FG: Deposit %s has no ecotype", name.c_str() );
357 _FilterPhase = 0;
359 // Read season(s)
360 string s1s, s2s, s3s, s4s;
361 if ( ! zone->getPropertyByName( "while_season_spring", s1s ) ) return malformed( "while_season_spring", name );
362 if ( ! zone->getPropertyByName( "while_season_summer", s2s ) ) return malformed( "while_season_summer", name );
363 if ( ! zone->getPropertyByName( "while_season_automn", s3s ) ) return malformed( "while_season_automn", name );
364 if ( ! zone->getPropertyByName( "while_season_winter", s4s ) ) return malformed( "while_season_winter", name );
365 bool s1 = (s1s=="true"), s2 = (s2s=="true"), s3 = (s3s=="true"), s4 = (s4s=="true");
366 if ( ! (s1 && s2 && s3 && s4) )
368 if ( s1 ) { _SeasonFilter.push_back( EGSPD::CSeason::Spring ); _FilterPhase |= 0x1; }
369 if ( s2 ) { _SeasonFilter.push_back( EGSPD::CSeason::Summer ); _FilterPhase |= 0x2; }
370 if ( s3 ) { _SeasonFilter.push_back( EGSPD::CSeason::Autumn ); _FilterPhase |= 0x4; }
371 if ( s4 ) { _SeasonFilter.push_back( EGSPD::CSeason::Winter ); _FilterPhase |= 0x8; }
374 // Read weather
375 string w1s, w2s, w3s, w4s;
376 if ( ! zone->getPropertyByName( "while_weather_0_best", w1s ) ) return malformed( "while_weather_0_best", name );
377 if ( ! zone->getPropertyByName( "while_weather_1_good", w2s ) ) return malformed( "while_weather_1_good", name );
378 if ( ! zone->getPropertyByName( "while_weather_2_bad", w3s ) ) return malformed( "while_weather_2_bad", name );
379 if ( ! zone->getPropertyByName( "while_weather_3_worst", w4s ) ) return malformed( "while_weather_3_worst", name );
380 bool w1 = (w1s=="true"), w2 = (w2s=="true"), w3 = (w3s=="true"), w4 = (w4s=="true");
381 if ( ! (w1 && w2 && w3 && w4) )
383 if ( w1 ) { _WeatherFilter.push_back( CRyzomTime::best ); _FilterPhase |= 0x10; }
384 if ( w2 ) { _WeatherFilter.push_back( CRyzomTime::good ); _FilterPhase |= 0x20; }
385 if ( w3 ) { _WeatherFilter.push_back( CRyzomTime::bad ); _FilterPhase |= 0x40; }
386 if ( w4 ) { _WeatherFilter.push_back( CRyzomTime::worst ); _FilterPhase |= 0x80; }
389 // Read time of day
390 string t1s, t2s;
391 if ( ! zone->getPropertyByName( "while_its_day", t1s ) ) return malformed ( "while_its_day", name );
392 if ( ! zone->getPropertyByName( "while_its_night", t2s ) ) return malformed ( "while_its_night", name );
393 bool t1 = (t1s=="true"), t2 = (t2s=="true");
394 if ( ! (t1 && t2) )
396 if ( t1 )
398 _TimeOfDayFilter.push_back( CRyzomTime::dawn );
399 _TimeOfDayFilter.push_back( CRyzomTime::day );
400 _TimeOfDayFilter.push_back( CRyzomTime::evening );
401 _FilterPhase |= 0x100;
403 if ( t2 )
405 _TimeOfDayFilter.push_back( CRyzomTime::nightfall );
406 _TimeOfDayFilter.push_back( CRyzomTime::night );
407 _FilterPhase |= 0x200;
411 // Read auto-spawn properties
412 string assS;
413 if ( ! zone->getPropertyByName( "auto_spawn_sources", assS ) ) return malformed( "auto_spawn_sources", name );
414 if ( assS=="true" )
416 string aspS, asltS, asetS, amin; // not really 'average' anymore
417 if ( ! zone->getPropertyByName( "auto_spawn_average_period_s", aspS ) ) return malformed( "auto_spawn_average_period", name );
418 if ( ! zone->getPropertyByName( "auto_spawn_lifetime_s", asltS ) ) return malformed( "auto_spawn_lifetime", name );
419 if ( ! zone->getPropertyByName( "auto_spawn_extraction_time_s", asetS ) ) return malformed( "auto_spawn_sources", name );
420 if ( ! zone->getPropertyByName( "auto_spawn_min_source", amin ) ) return malformed( "auto_spawn_min_source", name );
421 _AutoSpawnSourcePt = new CAutoSpawnProperties;
422 NLMISC::fromString(aspS, _AutoSpawnSourcePt->SpawnPeriodGc);
423 _AutoSpawnSourcePt->SpawnPeriodGc *= 10;
424 NLMISC::fromString(asltS, _AutoSpawnSourcePt->LifeTimeGc);
425 _AutoSpawnSourcePt->LifeTimeGc *= 10;
426 NLMISC::fromString(asetS, _AutoSpawnSourcePt->ExtractionTimeGc);
427 _AutoSpawnSourcePt->ExtractionTimeGc *= 10;
428 NLMISC::fromString(amin, _AutoSpawnSourcePt->MinimumSpawnedSources);
429 // security!
430 _AutoSpawnSourcePt->MinimumSpawnedSources = min(uint32(100), _AutoSpawnSourcePt->MinimumSpawnedSources);
432 else
434 _AutoSpawnSourcePt = NULL;
437 // Read source FX index
438 string srcFXIndexS;
439 if ( ! zone->getPropertyByName( "source_fx", srcFXIndexS ) ) return malformed( "source_fx", name );
440 NLMISC::fromString(srcFXIndexS, _SourceFXIndex);
442 // Read other initial properties
443 string cpS, eS, ikaS, adpR;
444 if ( ! zone->getPropertyByName( "can_prospect", cpS ) ) return malformed( "can_prospect", name );
445 if ( ! zone->getPropertyByName( "enabled", eS ) ) return malformed( "enabled", name );
446 if ( ! zone->getPropertyByName( "initial_kami_anger", ikaS ) ) return malformed( "initial_kami_anger", name );
447 if ( ! zone->getPropertyByName( "can_have_depletion_risk", adpR ) ) return malformed( "can_have_depletion_risk", name );
448 _CanProspect = (cpS=="true");
449 _Enabled = (eS=="true");
450 _AllowDepletionRisk = (adpR=="true");
451 _KamiAnger = (float)atof( ikaS.c_str() );
452 if ( (_KamiAnger != -1.0f) && (_KamiAnger < 0) )
453 nlwarning( "Invalid initial_kami_anger %.1f in %s", _KamiAnger, name.c_str() );
455 // Apply filters
456 uint32 minEnergy, maxEnergy;
457 NLMISC::fromString(minEnergyS, minEnergy);
458 NLMISC::fromString(maxEnergyS, maxEnergy);
459 if ( exactRMCodesS->empty() && rmFamilyFilterS->empty() && itemPartsFilterS->empty() )
461 nlwarning( "FG: Deposit %s: No RM, exactRms or item parts specified!", name.c_str() );
462 return false;
464 else
465 selectRMsByFilters( *exactRMCodesS, *rmFamilyFilterS, *itemPartsFilterS, *craftCivS, minEnergy, maxEnergy );
467 nldebug( "FG: Built deposit %s %s %s %s %s", name.c_str(), _AutoSpawnSourcePt?"AUTO":"-", _CanProspect?"PRO":"-", _AllowDepletionRisk?"ADPR":"-", _Enabled?"ON":"OFF" );
468 return true;
473 * Select the raw materials, using the specified filters and _Ecotype
475 void CDeposit::selectRMsByFilters( std::vector<std::string>& exactRMCodesS, const std::vector<std::string>& rmFamilyFilterS, const std::vector<std::string>& itemPartsFilterS, const std::vector<std::string>& craftCivS, uint minEnergy, uint maxEnergy )
477 vector<TRMFamily> rmFamilyFilter;
478 convertRMFamiliesNames( _Name, rmFamilyFilterS, rmFamilyFilter );
479 vector<uint> itemPartsFilter;
480 convertItemPartsNames( _Name, itemPartsFilterS, itemPartsFilter );
481 vector<ITEM_ORIGIN::EItemOrigin> craftCivFilter;
482 convertCraftCivNames( craftCivS, craftCivFilter );
483 for_each( exactRMCodesS.begin(), exactRMCodesS.end(), makeFullSItemCode );
485 if ( VerboseDepositFiltering )
486 nldebug( "%s", _Name.c_str() );
488 // Sort the items by sheetId so that the deposits are not dependant on the hash map ordering
489 const CAllStaticItems& allItems = CSheets::getItemMapForm();
490 vector< const CStaticItem* > sortedItems( allItems.size() );
491 uint i = 0;
492 for ( CAllStaticItems::const_iterator it=allItems.begin(); it!=allItems.end(); ++it, ++i )
494 sortedItems[i] = &((*it).second);
496 std::sort( sortedItems.begin(), sortedItems.end(), TCompareStaticItemPtrBySheetId() );
498 for ( vector< const CStaticItem* >::const_iterator it=sortedItems.begin(); it!=sortedItems.end(); ++it )
500 // Eliminate non raw materials
501 const CStaticItem& staticItem = *(*it);
502 if ( ! staticItem.Mp )
503 continue;
504 const string& sheetName = staticItem.SheetId.toString();
505 if ( (sheetName.size() < (CREATURE_OR_DEPOSIT_MP_CHAR+1)) || (sheetName[0] != 'm' ) )
506 continue;
508 // Keep raw material directly if matching one of specified exact raw material codes (including creature's raw materials)
509 if ( find( exactRMCodesS.begin(), exactRMCodesS.end(), sheetName ) == exactRMCodesS.end() )
511 // Eliminate non 'forage' raw materials
512 if ( VerboseDepositFiltering )
513 nldebug( "FG: Submitting %s", sheetName.c_str() );
514 if ( (sheetName[CREATURE_OR_DEPOSIT_MP_CHAR] != 'd') )
515 continue;
517 // Eliminate raw materials of incompatible ecosystem
518 if ( (_Ecotype != ECOSYSTEM::common_ecosystem) && (staticItem.Mp->Ecosystem != ECOSYSTEM::common_ecosystem) &&
519 (staticItem.Mp->Ecosystem != _Ecotype) )
521 if ( VerboseDepositFiltering ) nldebug( "-Ecotype %s", ECOSYSTEM::toString( staticItem.Mp->Ecosystem ).c_str() );
522 continue;
525 // Match energy filter (TEMP: 25 is the minimum maxEnergy threshold) (include boundaries)
526 if ( (staticItem.Mp->StatEnergy < minEnergy) || (staticItem.Mp->StatEnergy > maxEnergy /*max(maxEnergy,(uint)25)*/) )
528 if ( VerboseDepositFiltering ) nldebug( "-StatEnergy %hu", staticItem.Mp->StatEnergy );
529 continue;
532 // Match rmFamilyFilter or itemPartsFilter
533 if ( find( rmFamilyFilter.begin(), rmFamilyFilter.end(), staticItem.Mp->Family ) == rmFamilyFilter.end() )
535 // If not found in rmFamilyFilter, try if one of itemPartsFilter is matched along with civ
536 bool found = false;
537 for ( vector<uint>::iterator ipf=itemPartsFilter.begin(); ipf!=itemPartsFilter.end(); ++ipf )
539 uint itemPartIndex = (*ipf);
541 // Find if the current RM has the item part matching one of the filter
542 const CMP::TMpFaberParameters *mpFaberParam = staticItem.Mp->getMpFaberParameters( itemPartIndex );
543 if ( mpFaberParam && (mpFaberParam->Durability != 0) )
545 if ( (craftCivFilter.empty()) || // no civ constraint
546 (mpFaberParam->CraftCivSpec == ITEM_ORIGIN::COMMON) || // RM matches all civs
547 (find( craftCivFilter.begin(), craftCivFilter.end(), mpFaberParam->CraftCivSpec ) != craftCivFilter.end()) ) // RM matches civ constraint
549 if ( VerboseDepositFiltering ) nldebug( "+StatEnergy %hu +ItemPart %c +CivSpec %s", staticItem.Mp->StatEnergy, 'A' + (char)itemPartIndex, ITEM_ORIGIN::enumToString( mpFaberParam->CraftCivSpec ).c_str() );
550 found = true;
551 break; // RM is selected, no need to test other item parts
553 else
554 if ( VerboseDepositFiltering ) nldebug( "-CivSpec %s", ITEM_ORIGIN::enumToString( mpFaberParam->CraftCivSpec ).c_str() );
556 else
558 if ( VerboseDepositFiltering ) nldebug( "-ItemPart %c", 'A' + (char)itemPartIndex );
561 if ( ! found )
562 continue;
564 /*else
566 if ( VerboseDepositFiltering ) nldebug( "+rmFamilyFilter %u", staticItem.Mp->Family );
569 /*else
571 if ( VerboseDepositFiltering ) nldebug( "+exact_mp_item %s", sheetName.c_str() );
574 // Select if matching (do not check duplicate, keep them if item part matches explicit family)
575 CStaticDepositRawMaterial rm;
576 rm.MaterialSheet = staticItem.SheetId;
577 _RawMaterials.push_back( rm );
578 if ( VerboseDepositFiltering )
579 nldebug( "SELECTED %s", sheetName.c_str() );
581 // Export deposit contents report if requested
582 if ( ExportDepositContents )
584 static FILE *depositReportFile;
585 static bool depositReportCreated = false;
586 if ( ! depositReportCreated )
588 depositReportCreated = true;
589 depositReportFile = nlfopen( "deposit_contents.csv", "wt" ); // fclose() auto?
590 if ( depositReportFile )
592 fprintf( depositReportFile, "Deposit;RM;When in year;When in day;Weather;\n" );
595 if ( depositReportFile )
596 fprintf( depositReportFile, "%s;%s;%s;%s;%s;\n", _Name.c_str(), rm.MaterialSheet.toString().c_str(), getSeasonStr().c_str(), getTimeOfDayStr().c_str(), getWeatherStr().c_str() );
599 if ( _RawMaterials.empty() )
600 nlwarning( "FG: Selected 0 items in deposit %s", _Name.c_str() );
601 else
602 nldebug( "FG: Selected %u RM (on %u items) in deposit %s", _RawMaterials.size(), allItems.size(), _Name.c_str() );
607 * Helper for CDeposit::hasFamily()
609 struct CIsOfFamilyPred : public std::binary_function< CStaticDepositRawMaterial, RM_FAMILY::TRMFamily, bool >
611 /// Predicate
612 bool operator() ( const CStaticDepositRawMaterial& rm, RM_FAMILY::TRMFamily family ) const
614 const CAllStaticItems& allItems = CSheets::getItemMapForm();
615 CAllStaticItems::const_iterator it = allItems.find( rm.MaterialSheet );
616 if ( it != allItems.end() )
618 const CStaticItem& staticItem = (*it).second;
619 return staticItem.Mp && (staticItem.Mp->Family == family);
621 else
622 return false;
628 * Helper for CDeposit::hasFamily()
630 struct CIsOfGroupPred : public std::binary_function< CStaticDepositRawMaterial, RM_GROUP::TRMGroup, bool >
632 /// Predicate
633 bool operator() ( const CStaticDepositRawMaterial& rm, RM_GROUP::TRMGroup group ) const
635 const CAllStaticItems& allItems = CSheets::getItemMapForm();
636 CAllStaticItems::const_iterator it = allItems.find( rm.MaterialSheet );
637 if ( it != allItems.end() )
639 const CStaticItem& staticItem = (*it).second;
640 return staticItem.Mp && (staticItem.Mp->getGroup() == group);
642 else
643 return false;
649 * Helper for CDeposit::hasRMForItemPart()
651 struct CCanCraftItemPartPred : public std::binary_function< CStaticDepositRawMaterial, uint, bool >
653 /// Predicate
654 bool operator() ( const CStaticDepositRawMaterial& rm, uint itemPartIndex ) const
656 const CAllStaticItems& allItems = CSheets::getItemMapForm();
657 CAllStaticItems::const_iterator it = allItems.find( rm.MaterialSheet );
658 if ( it != allItems.end() )
660 const CStaticItem& staticItem = (*it).second;
661 if ( staticItem.Mp )
663 const CMP::TMpFaberParameters *mpFaberParam = staticItem.Mp->getMpFaberParameters( itemPartIndex );
664 return (mpFaberParam && (mpFaberParam->Durability != 0));
666 else
667 return false;
669 else
670 return false;
676 * Helper for CDeposit::hasFamily()
678 struct CMatchStatEnergyPred : public std::binary_function< CStaticDepositRawMaterial, uint8, bool >
680 /// Predicate
681 bool operator() ( const CStaticDepositRawMaterial& rm, uint8 maxStatEnergy ) const
683 const CAllStaticItems& allItems = CSheets::getItemMapForm();
684 CAllStaticItems::const_iterator it = allItems.find( rm.MaterialSheet );
685 if ( it != allItems.end() )
687 const CStaticItem& staticItem = (*it).second;
688 return staticItem.Mp && (staticItem.Mp->StatEnergy <= maxStatEnergy); // include boundary
690 else
692 nlwarning( "%s not found", rm.MaterialSheet.toString().c_str() );
693 return true;
700 * Helper for CDeposit::hasFamily()
702 struct CMatchExactStatEnergyPred : public std::binary_function< CStaticDepositRawMaterial, uint8, bool >
704 /// Predicate
705 bool operator() ( const CStaticDepositRawMaterial& rm, uint8 maxStatEnergy ) const
707 const CAllStaticItems& allItems = CSheets::getItemMapForm();
708 CAllStaticItems::const_iterator it = allItems.find( rm.MaterialSheet );
709 if ( it != allItems.end() )
711 const CStaticItem& staticItem = (*it).second;
712 return staticItem.Mp && (staticItem.Mp->StatEnergy == maxStatEnergy);
714 else
715 return false;
721 * Return true if the deposit contains at least one RM of the specified family
723 bool CDeposit::hasFamily( RM_FAMILY::TRMFamily family ) const
725 return find_if( _RawMaterials.begin(), _RawMaterials.end(), bind2nd( CIsOfFamilyPred(), family ) ) != _RawMaterials.end();
729 * Return true if the deposit contains at least one RM of the specified group
731 bool CDeposit::hasGroup( RM_GROUP::TRMGroup group ) const
733 return find_if( _RawMaterials.begin(), _RawMaterials.end(), bind2nd( CIsOfGroupPred(), group ) ) != _RawMaterials.end();
737 * Return true if the deposit contains at least one RM than can craft the specified item part
739 bool CDeposit::hasRMForItemPart( uint itemPartIndex ) const
741 return find_if( _RawMaterials.begin(), _RawMaterials.end(), bind2nd( CCanCraftItemPartPred(), itemPartIndex ) ) != _RawMaterials.end();
745 * Return true if the deposit contains at least one RM with energy lower_eq than the specified value
747 bool CDeposit::hasLowerStatEnergy( uint8 maxStatEnergy ) const
749 return find_if( _RawMaterials.begin(), _RawMaterials.end(), bind2nd( CMatchStatEnergyPred(), maxStatEnergy ) ) != _RawMaterials.end();
753 * Return true if the deposit contains at least one RM with energy equalling the specifing value
755 bool CDeposit::hasExactStatEnergy( uint8 statEnergy ) const
757 return find_if( _RawMaterials.begin(), _RawMaterials.end(), bind2nd( CMatchExactStatEnergyPred(), statEnergy ) ) != _RawMaterials.end();
762 * Called by lowFreqUpdate and autoSpawnUpdate
764 void CDeposit::autoSpawnSource(const CVector &cornerMin, const CVector &cornerMax)
767 Yoyo: This method may fail because it selects a random position in a box and then check if it's in the zone.
768 We can do better by making a list of triangles of the concave polygon, then selecting randomly and
769 carefully in this list of triangle.
772 // Randomize a position in the deposit
773 CVector pos;
774 pos.z = 0;
775 const uint NB_ATTEMPTS = 5;
776 uint iAttempt;
777 for ( iAttempt=0; iAttempt!=NB_ATTEMPTS; ++iAttempt )
779 pos.x = cornerMin.x + RandomGenerator.frand( cornerMax.x - cornerMin.x );
780 pos.y = cornerMin.y + RandomGenerator.frand( cornerMax.y - cornerMin.y );
781 if ( contains( pos ) )
782 break;
785 // If a valid position could not be found, abort spawning of this source
786 if ( iAttempt == NB_ATTEMPTS )
787 return;
789 // Spawn a source
790 CFgProspectionPhrase::autoSpawnSource( pos, this );
795 * Update deposit (especially recent forage sites)
797 void CDeposit::lowFreqUpdate()
799 if ( !HarvestSystemEnabled )
800 return;
801 // Update recent forage sites
802 for ( CRecentForageSites::iterator it=_RecentForageSites.begin(); it!=_RecentForageSites.end(); )
804 CRecentForageSite& forageSite = (*it);
805 if ( forageSite.lowFreqUpdate() )
807 ++it;
809 else
811 --TotalNbRecentForageSites;
812 it = _RecentForageSites.erase( it );
816 // Decrease kami anger level
817 decKamiAnger( ForageKamiAngerDecreasePerHour.get() / 36000.0f * ((float)DepositUpdateFrequency.get()) );
819 // Auto-spawn a source from time to time (if the deposit has this flag)
820 if ( _AutoSpawnSourcePt && _Enabled )
822 // For Speed Test
823 //TTime testYoyoLT0= CTime::getLocalTime();
824 //uint countBefore= _CurrentNbAutoSpawnedSources;
826 uint spawnPeriodLFUpdates = (AutoSpawnForageSourcePeriodOverride.get() != 0) ? AutoSpawnForageSourcePeriodOverride.get() / DepositUpdateFrequency.get() : _AutoSpawnSourcePt->SpawnPeriodGc / DepositUpdateFrequency.get();
828 // get deposit bbox
829 CVector cornerMin, cornerMax;
830 getAABox( cornerMin, cornerMax );
832 // Previously, the rythm of spawning was randomized. Now it's constant.
833 //TGameCycle spawnAvgPeriod = (AutoSpawnForageSourceAveragePeriodOverride.get() != 0) ? AutoSpawnForageSourceAveragePeriodOverride.get() : _AutoSpawnSourcePt->SpawnAveragePeriodGc;
834 //sint32 r = RandomGenerator.rand( (uint16)(spawnAvgPeriod / DepositUpdateFrequency.get()) );
835 //if ( r == 0 )
836 bool spawnSourceBecauseOfFrequency= (CTickEventHandler::getGameCycle() / DepositUpdateFrequency.get()) % spawnPeriodLFUpdates == 0;
838 // Avoid infinite loop if the spawn of source is too buggy:
839 // count first the number of sources to force spawn then run this count, and forget the ones that fail.
840 uint numSourceToForceSpawn= 0;
841 if(_CurrentNbAutoSpawnedSources < _AutoSpawnSourcePt->MinimumSpawnedSources)
842 numSourceToForceSpawn= _AutoSpawnSourcePt->MinimumSpawnedSources - _CurrentNbAutoSpawnedSources;
843 uint numSourceSpawned= 0;
844 while ( spawnSourceBecauseOfFrequency || numSourceSpawned<numSourceToForceSpawn)
846 // this method may fail, but don't cares and continues
847 autoSpawnSource(cornerMin, cornerMax);
848 numSourceSpawned++;
850 // If continue this loop, won't be because of frequency update, but because of minimum source requirement
851 spawnSourceBecauseOfFrequency= false;
854 // For Speed Test
855 //TTime testYoyoLT1= CTime::getLocalTime();
856 //nlinfo("*** [%03d]AutoSpawnLOWFREQ Time(%d->%d): %d ms", CTickEventHandler::getGameCycle()%1000, countBefore, _CurrentNbAutoSpawnedSources, testYoyoLT1 - testYoyoLT0);
859 // Beware: can return before
864 * Update deposit that need to auto spawn because number of harvest sources is too low
866 void CDeposit::autoSpawnUpdate()
868 if ( !HarvestSystemEnabled )
869 return;
871 // Auto-spawn a source from time to time (if the deposit has this flag)
872 if ( _AutoSpawnSourcePt && _Enabled && _CurrentNbAutoSpawnedSources < _AutoSpawnSourcePt->MinimumSpawnedSources)
874 // For Speed Test
875 //TTime testYoyoLT0= CTime::getLocalTime();
876 //uint countBefore= _CurrentNbAutoSpawnedSources;
878 // get deposit bbox
879 CVector cornerMin, cornerMax;
880 getAABox( cornerMin, cornerMax );
882 // Avoid infinite loop if the spawn of source is too buggy:
883 // count first the number of sources to force spawn then run this count, and forget the ones that fail.
884 uint numSourceToForceSpawn= _AutoSpawnSourcePt->MinimumSpawnedSources - _CurrentNbAutoSpawnedSources;
886 // for all sources to spawn
887 for( uint i= 0; i<numSourceToForceSpawn; i++)
889 // this method may fail, but don't cares and continues
890 autoSpawnSource(cornerMin, cornerMax);
892 // For Speed Test
893 //TTime testYoyoLT1= CTime::getLocalTime();
894 //nlinfo("*** [%03d]AutoSpawnFORCE Time(%d->%d): %d ms", CTickEventHandler::getGameCycle()%1000, countBefore, _CurrentNbAutoSpawnedSources, testYoyoLT1 - testYoyoLT0);
897 // Beware: can return before
902 * Auto Spawn Deposit mgt
904 void CDeposit::decreaseAutoSpawnedSources()
906 BOMB_IF(_CurrentNbAutoSpawnedSources==0, "nb auto spawned sources should be > 0!", return);
907 _CurrentNbAutoSpawnedSources--;
909 // If the deposit has not enough ressources, must refill at next tick update
910 // NB: don't do it at next lowFreqUpdate(), to avoid big jump in CPU each 30 sec
911 // as soon an autospawned harvest source is unspawned, the deposit will refill at next tick.
912 if(_AutoSpawnSourcePt && _CurrentNbAutoSpawnedSources < _AutoSpawnSourcePt->MinimumSpawnedSources)
914 CZoneManager::getInstance().registerDepositToAutoSpawnUpdate(this);
918 void CDeposit::increaseAutoSpawnedSources()
920 _CurrentNbAutoSpawnedSources++;
924 //-----------------------------------------------------------------------------
925 // character take MP, kami eat this fool character ?
926 //-----------------------------------------------------------------------------
927 #if 0
928 //void CDeposit::characterTakeRM( const CEntityId& charId, uint32 depositIndexContent, uint16 quantity )
930 // // check if depositIndexContent is in valid range
931 // if( depositIndexContent < _DepositContent.size() )
932 // {
933 // // process kami guardian reaction and fame impact
934 // CDepositRawMaterialSeasonParameters * seasonParameters;
935 // switch( CTimeDateSeasonManager::getRyzomTimeReference().getRyzomSeason() )
936 // {
937 // case EGSPD::CSeason::Spring:
938 // seasonParameters = &_DepositContent[ depositIndexContent ]->SpringParams;
939 // break;
940 // case EGSPD::CSeason::Summer:
941 // seasonParameters = &_DepositContent[ depositIndexContent ]->SummerParams;
942 // break;
943 // case EGSPD::CSeason::Autumn:
944 // seasonParameters = &_DepositContent[ depositIndexContent ]->AutumnParams;
945 // break;
946 // case EGSPD::CSeason::Winter:
947 // seasonParameters = &_DepositContent[ depositIndexContent ]->WinterParams;
948 // break;
949 // default:
950 // nlstop; //=> debug CTimeDateSeasonManager....
951 // }
953 // if( seasonParameters->AngryLevel >= _DepositContent[ depositIndexContent ]->CurrentQuantity )
954 // {
955 // sint32 fameImpact;
956 // uint32 kamiImpact;
958 // CMessage msgString("STATIC_STRING");
959 // msgString.serial( const_cast< CEntityId& > ( charId ) );
960 // // serial exclude set of empty exclude set
961 // set< CEntityId > empty;
962 // msgString.serialCont( empty );
964 // if( seasonParameters->BlackKamiLevel >= _DepositContent[ depositIndexContent ]->CurrentQuantity )
965 // {
966 // // kami impact for AIS when Kami expulse a black kami
967 // kamiImpact = 300;
969 // // set fame impact
970 // fameImpact = -15;
972 // string str("WOS_KAMI_BLACK_KAMI");
973 // msgString.serial(str);
974 // }
975 // else if( seasonParameters->FuryLevel >= _DepositContent[ depositIndexContent ]->CurrentQuantity )
976 // {
977 // // kami impact for AIS when Kami is fury against harvester
978 // kamiImpact = 200;
980 // // set fame impact
981 // fameImpact = -10;
983 // string str("WOS_KAMI_FURY");
984 // msgString.serial( str );
985 // }
986 // else if( seasonParameters->AngryLevel >= _DepositContent[ depositIndexContent ]->CurrentQuantity )
987 // {
988 // // kami impact for AIS when Kami is angry against harvester
989 // kamiImpact = 100;
991 // // set fame impact
992 // fameImpact = -5;
994 // string str("WOS_KAMI_ANGRY");
995 // msgString.serial( str );
996 // }
997 // sendMessageViaMirror( "IOS", msgString );
999 // // Send message to AIS for kami impact
1000 // CMessage msgout("KAMI_IMPACT");
1001 // // TODO : if this code reactivated, add the instance number and send to the corect AIS
1002 // msgout.serial( const_cast<CEntityId&> (charId) );
1003 // msgout.serial( _Alias );
1004 // msgout.serial( kamiImpact );
1005 // sendMessageViaMirror( "AIS", msgout );
1008 // // send message to EGS for fame impact
1009 // // TODO boris : update fame with new fame system
1010 ///* CCharacter * c = PlayerManager.getChar( charId );
1011 // uint8 fameType = FAMES::kami;
1012 // if( c )
1013 // {
1014 // c->fameChange( fameType, fameImpact );
1015 // }
1016 //*/ }
1018 // // update deposit quantity
1019 // if( _DepositContent[ depositIndexContent ]->CurrentQuantity < quantity )
1020 // {
1021 // _DepositContent[ depositIndexContent ]->CurrentQuantity = 0;
1022 // }
1023 // else
1024 // {
1025 // _DepositContent[ depositIndexContent ]->CurrentQuantity -= quantity;
1026 // }
1027 // }
1028 // else
1029 // {
1030 // nlwarning("FG: <CDeposit::characterTakeRM> Received invalid depositIndexContent %d (deposit content size id %d)", depositIndexContent, _DepositContent.size() );
1031 // }
1033 #endif
1036 string CDeposit::getSeasonStr() const
1038 string s;
1039 if ( _SeasonFilter.empty() )
1040 return "All year";
1041 for ( uint i=0; i!=_SeasonFilter.size(); ++i )
1043 if ( ! s.empty() )
1044 s += " ";
1045 s += EGSPD::CSeason::toString( _SeasonFilter[i] );
1047 return s;
1050 string CDeposit::getTimeOfDayStr() const
1052 string s;
1053 if ( _TimeOfDayFilter.empty() )
1054 return "Night & day";
1055 for ( uint i=0; i!=_TimeOfDayFilter.size(); ++i )
1057 if ( ! s.empty() )
1058 s += " ";
1059 s += toString( "%u", (uint)_TimeOfDayFilter[i] );
1061 return s;
1064 string CDeposit::getWeatherStr() const
1066 string s;
1067 if ( _WeatherFilter.empty() )
1068 return "All weathers";
1069 for ( uint i=0; i!=_WeatherFilter.size(); ++i )
1071 if ( ! s.empty() )
1072 s += " ";
1073 s += toString( "%u", (uint)_WeatherFilter[i] );
1075 return s;
1079 //-----------------------------------------------------------------------------
1080 // display deposit content
1081 //-----------------------------------------------------------------------------
1082 void CDeposit::displayContent( NLMISC::CLog * log, bool extendedInfo, NLMISC::CWordsDictionary *itemDictionary )
1084 const CAllStaticItems& allItems = CSheets::getItemMapForm();
1085 uint32 materialNumber = 0;
1086 log->displayRawNL( "---- DEPOSIT %s ----", _Name.c_str() );
1087 log->displayRawNL( "\t Centre: %s", getBarycentre().toString().c_str() );
1088 if ( getContents().empty() )
1089 log->displayRawNL( "\tNo RM was selected in this deposit" );
1090 log->displayRawNL( "\t%u RMs:", getContents().size() );
1091 for( std::vector< CStaticDepositRawMaterial >::const_iterator it = getContents().begin(); it != getContents().end(); ++it )
1093 if ( extendedInfo )
1094 log->displayRawNL( "\tRM #%d", materialNumber );
1095 ++materialNumber;
1096 CSString sheetCode = (*it).MaterialSheet.toString();
1097 string fullNameInfo;
1098 if ( itemDictionary )
1100 CVectorSString result;
1101 sheetCode = sheetCode.splitTo( '.' );
1102 itemDictionary->lookup( sheetCode, result );
1103 if ( ! result.empty() )
1104 fullNameInfo = string(" ") + result[0];
1106 if ( fullNameInfo.empty() )
1107 log->displayRawNL( "\t\tRM Id: %s", sheetCode.c_str() );
1108 else
1109 log->displayRawNL( "\t\tRM Id: %s", fullNameInfo.c_str() );
1110 //log->displayNL( "\t\tMax Amount: %d", (*it).MaxAmount );
1111 if ( extendedInfo )
1113 CAllStaticItems::const_iterator itItem = allItems.find( (*it).MaterialSheet );
1114 if ( itItem != allItems.end() && (*itItem).second.Mp )
1116 const CStaticItem& staticItem = (*itItem).second;
1117 log->displayRawNL( "\t\tFamily: %u", staticItem.Mp->Family );
1118 log->displayRawNL( "\t\tGroup: %u", staticItem.Mp->getGroup() );
1119 log->displayRawNL( "\t\tEcosystem: %s", ECOSYSTEM::toString( staticItem.Mp->Ecosystem ).c_str() );
1120 //log->displayRawNL( "\t\tRarity: %hu", staticItem.Mp->Rarity );
1121 log->displayRawNL( "\t\tStatEnergy: %hu", staticItem.Mp->StatEnergy );
1125 log->displayRawNL( "\tEcotype: %s", ECOSYSTEM::toString( _Ecotype ).c_str() );
1126 string ws;
1127 if ( _WeatherFilter.empty() )
1128 ws = "all";
1129 else
1131 for( vector<CRyzomTime::EWeather>::const_iterator iwf=_WeatherFilter.begin(); iwf!=_WeatherFilter.end(); ++iwf )
1132 ws += toString( "%u:", *iwf );
1134 log->displayRawNL( "\tWeathers: %s", ws.c_str() );
1135 string ts;
1136 if ( _TimeOfDayFilter.empty() )
1137 ts = "all";
1138 else
1140 for( vector<CRyzomTime::ETimeOfDay>::const_iterator itf=_TimeOfDayFilter.begin(); itf!=_TimeOfDayFilter.end(); ++itf )
1141 ts += toString( "%u:", *itf );
1143 log->displayRawNL( "\tTimes of day: %s", ts.c_str() );
1144 string ss;
1145 if ( _SeasonFilter.empty() )
1146 ss = "all";
1147 else
1149 for( vector<CRyzomTime::ESeason>::const_iterator isf=_SeasonFilter.begin(); isf!=_SeasonFilter.end(); ++isf )
1150 ws += toString( "%u:", *isf );
1152 log->displayRawNL( "\tSeasons: %s", ss.c_str() );
1153 log->displayRaw( "\tKami anger: %.1f", _KamiAnger );
1154 if ( _KamiAnger == -1.0f )
1155 log->displayRawNL( " disabled" );
1156 else
1157 log->displayRawNL( " on %.1f, %.1f", ForageKamiAngerThreshold1.get(), ForageKamiAngerThreshold2.get() );
1158 if ( _AutoSpawnSourcePt )
1159 log->displayRawNL( "\tAutoSpawnSource: %s", _AutoSpawnSourcePt?"ON":"OFF" );
1160 if ( ! _CanProspect )
1161 log->displayRawNL( "\tCanProspect: %d", _CanProspect );
1162 if ( ! _Enabled )
1163 log->displayRawNL( "\tEnabled: %d", _Enabled );
1164 if ( ! _AllowDepletionRisk )
1165 log->displayRawNL( "\tAllowDepletionRisk: %d", _AllowDepletionRisk );
1169 float GenRand = 1.0f;
1170 float GenFreq = 0.05f;
1172 NLMISC_COMMAND( setDepositRand, "Set CNoiseValue params for deposits", "<rand> <freq>" )
1174 if ( args.size() < 2 )
1175 return false;
1176 GenRand = (float)atof( args[0].c_str() );
1177 GenFreq = (float)atof( args[1].c_str() );
1178 return true;
1185 class CIndexNoiseValue : public CNoiseValue
1187 public:
1190 CIndexNoiseValue( uint arraySize, uint phase, float freq ) : CNoiseValue(0.0f, 1.0f, freq), _MaxIndex((float)(arraySize)), _Phase(1210.191f*phase, 523.8883f*phase, 403.57614f*phase) {}
1192 /// Evaluate the value corresponding to the pos
1193 uint eval( const CVector& pos ) const
1195 uint indexOfMax = 0;
1196 float maxNoise = 0;
1198 // 1 dephased noise per possible value, and take the max result (otherwise with '(CNoiseValue::eval( pos ) * _MaxIndex)', the result would be biased)
1199 for( uint i=0; i!=_MaxIndex; ++i )
1201 float f = CNoiseValue::eval(
1202 pos + _Phase + CVector(5245.346f*i, 785.67985f*i, 7842.783367f*i) );
1203 if ( f > maxNoise )
1205 maxNoise = f;
1206 indexOfMax = i;
1209 return indexOfMax;
1212 private:
1214 float _MaxIndex;
1216 CVector _Phase;
1221 * Compute freq according to deposit size (small deposits under 64x64 have a proportionally higher frequency)
1223 inline float getFreqFromDepositArea( float depositBBoxArea )
1225 if ( depositBBoxArea < SmallDepositAreaThreshold )
1227 const float gradient = (0.05f-0.3f)*2.0f/(SmallDepositAreaThreshold-SmallDepositAreaReference);
1228 return gradient*depositBBoxArea + (GenFreq - gradient*(SmallDepositAreaThreshold));
1230 else
1232 return GenFreq;
1238 * Get a random RM from the neighbourhood of the specified position.
1239 * OptFastFloorBegin()/OptFastFloorEnd() must enclose one or more calls to this method.
1241 const CStaticDepositRawMaterial *CDeposit::getRandomRMAtPos( const NLMISC::CVector& pos, bool testIfSiteDepleted, bool& isDepleted )
1243 const float cellWidth = 1.0f;
1244 const uint16 nbNeighbours = 5; // center + 4 neighbour cells
1246 //nldebug( "Selecting RM in deposit" );
1248 if ( getContents().empty() )
1249 return NULL;
1251 // Test is local zone is depleted
1252 if ( testIfSiteDepleted )
1254 for ( CRecentForageSites::iterator ihs=_RecentForageSites.begin(); ihs!=_RecentForageSites.end(); ++ihs )
1256 if ( (*ihs).isDepleted() && (*ihs).contains( pos ) )
1258 //nldebug( "Harvest site depleted" );
1259 isDepleted = true;
1260 return NULL;
1265 // Compute freq according to deposit size
1266 float freq = getFreqFromDepositArea( getAreaOfAABox() );
1268 // Get raw materials in neighbourhood
1269 uint neighbourhoud [nbNeighbours];
1270 CVector cellPos = pos;
1271 CIndexNoiseValue noiseIndex( getContentSize(), _FilterPhase, freq ); // the phrase prevents to have the same map for two deposits with identical size but different seasons, etc.
1272 neighbourhoud[0] = noiseIndex.eval( cellPos );
1273 cellPos.x -= cellWidth;
1274 cellPos.y -= cellWidth;
1275 neighbourhoud[1] = noiseIndex.eval( cellPos );
1276 cellPos.x = pos.x + cellWidth;
1277 neighbourhoud[2] = noiseIndex.eval( cellPos );
1278 cellPos.y = pos.y + cellWidth;
1279 neighbourhoud[3] = noiseIndex.eval( cellPos );
1280 cellPos.x = pos.x - cellWidth;
1281 neighbourhoud[4] = noiseIndex.eval( cellPos );
1282 //nldebug( "Indices: %u %u %u %u", neighbourhoud[0], neighbourhoud[1], neighbourhoud[2], neighbourhoud[3] );
1284 // Select a random RM among neighbourhood
1285 uint rmIndex = neighbourhoud[RandomGenerator.rand( nbNeighbours-1 )];
1286 return &(getContents()[rmIndex]);
1289 if( seasonParameters->AngryLevel >= _DepositContent[ infos.DepositIndexContent ]->CurrentQuantity )
1291 CMessage msgout("STATIC_STRING");
1292 msgout.serial( const_cast< CEntityId& > ( charId ) );
1293 // serial exclude set of empty exclude set
1294 set< CEntityId > empty;
1295 msgout.serialCont( empty );
1296 string str("EGS_KAMI_ALERTE");
1297 msgout.serial( str );
1298 sendMessageViaMirror( "IOS", msgout );
1305 * Get the RM at the specified position.
1306 * OptFastFloorBegin()/OptFastFloorEnd() must enclose one or more calls to this method.
1307 * \param testIfSiteDepleted Set it to false for map generation.
1309 const CStaticDepositRawMaterial *CDeposit::getRMAtPos( const NLMISC::CVector& pos, bool testIfSiteDepleted, bool& isDepleted )
1311 //const float cellWidth = 5.0f;
1313 //nldebug( "Selecting RM in deposit" );
1315 if ( getContents().empty() )
1316 return NULL;
1318 // Test is local zone is depleted
1319 if ( testIfSiteDepleted )
1321 for ( CRecentForageSites::iterator ihs=_RecentForageSites.begin(); ihs!=_RecentForageSites.end(); ++ihs )
1323 if ( (*ihs).isDepleted() && (*ihs).contains( pos ) )
1325 //nldebug( "Harvest site depleted" );
1326 isDepleted = true;
1327 return NULL;
1332 // Compute freq according to deposit size
1333 float freq = getFreqFromDepositArea( getAreaOfAABox() );
1335 // Get raw material at position
1336 CIndexNoiseValue noiseIndex( getContentSize(), _FilterPhase, freq );
1337 uint rmIndex = noiseIndex.eval( pos );
1338 //nldebug( "Index: %u", rmIndex );
1339 if ( rmIndex >= getContentSize() )
1341 nlwarning( "FG: CIndexNoiseValue returned a value out of range" );
1342 rmIndex = getContentSize()-1;
1344 return &(getContents()[rmIndex]);
1349 * Return the current quantity. If 0 and the respawn time is elapsed, unlock.
1351 float CQuantityConstraints::getCurrentQuantity()
1353 if ( CurrentQuantity == 0 )
1355 // Time to unlock?
1356 const CRyzomTime& ryzomTime = CTimeDateSeasonManager::getRyzomTimeReference();
1357 if ( ryzomTime.getRyzomDay() < NextRespawnDay )
1359 // Still locked
1360 return 0;
1362 else
1364 // Unlock and retry
1365 CurrentQuantity = InitialQuantity;
1368 return CurrentQuantity;
1373 * Consume
1375 * Last possible consumption => lock deposit for RespawnTimeRyzomDays.
1376 * Does not unlock the deposit (see getCurrentQuantity() instead)
1377 * Argument by value.
1379 float CQuantityConstraints::consumeQuantity( float consumed )
1381 if ( CurrentQuantity > 0 )
1383 float newQuantity = CurrentQuantity - consumed;
1384 if ( newQuantity <= 0 )
1386 // Lock if the deposit is now empty (and wasn't before)
1387 const CRyzomTime& ryzomTime = CTimeDateSeasonManager::getRyzomTimeReference();
1388 NextRespawnDay = ryzomTime.getRyzomDay() + (uint32)RespawnTimeRyzomDays;
1390 // Give only the remaining quantity
1391 consumed = CurrentQuantity;
1392 newQuantity = 0;
1395 // Consume
1396 CurrentQuantity = newQuantity;
1397 return consumed;
1399 else
1400 return 0;
1405 * Display forage sites info
1407 void CDeposit::displayRecentForageSites( NLMISC::CLog& log ) const
1409 if ( ! _RecentForageSites.empty() )
1411 log.displayNL( "Deposit %s:", _Name.c_str() );
1412 for ( CRecentForageSites::const_iterator it=_RecentForageSites.begin(); it!=_RecentForageSites.end(); ++it )
1414 const CRecentForageSite& forageSite = (*it);
1415 forageSite.display( log );
1422 * Empty all forage sites
1424 void CDeposit::depleteAllRecentForageSites()
1426 for ( CRecentForageSites::iterator it=_RecentForageSites.begin(); it!=_RecentForageSites.end(); ++it )
1428 CRecentForageSite& forageSite = (*it);
1429 forageSite.depleteAll();
1435 * Increment the kami anger level (delta < 768). React if a threshold is reached. Return the kami anger level.
1437 float CDeposit::incKamiAnger( float delta, const std::vector<TDataSetRow>& foragers )
1439 if ( _KamiAnger == -1.0f )
1440 return -1.0f;
1442 if ( ForageKamiAngerOverride.get() != 0 )
1443 _KamiAnger = ForageKamiAngerOverride.get();
1444 else
1445 _KamiAnger += delta;
1446 if ( _KamiAnger > ForageKamiAngerThreshold1.get() )
1448 if ( _KamiAnger < ForageKamiAngerThreshold2.get() )
1450 // Only a warning (and small punishment ?)
1451 for ( vector<TDataSetRow>::const_iterator itf=foragers.begin(); itf!=foragers.end(); ++itf )
1453 // Send a warning message
1454 const TDataSetRow& rowId = (*itf);
1455 TDataSetRow noRow;
1456 npcTellToPlayer( noRow, rowId, "FORAGE_KAMI_ANGER_WARNING", false );
1459 else
1461 // Full punishment
1462 if( HarvestAreaEffectOn )
1464 for ( vector<TDataSetRow>::const_iterator itf=foragers.begin(); itf!=foragers.end(); ++itf )
1466 const TDataSetRow& rowId = (*itf);
1467 CEntityBase *entity = CEntityBaseManager::getEntityBasePtr( rowId );
1468 if ( entity )
1470 // Send a warning message
1471 TDataSetRow noRow;
1472 npcTellToPlayer( noRow, rowId, "FORAGE_KAMI_ANGER_PUNISH", false );
1474 // Set target list for FX (must be done before behaviour)
1475 CMirrorPropValueList<uint32> targets( TheDataset, rowId, DSPropertyTARGET_LIST );
1476 targets.clear();
1477 targets.push_front( 0 ); // null distance (self)
1478 uint32 urowId = *((uint32*)&rowId);
1479 targets.push_front( urowId );
1481 // Set behaviour for FX (must be done after target list)
1482 MBEHAV::CBehaviour behav( MBEHAV::CAST_OFF_SUCCESS );
1483 behav.Spell.SpellMode = MAGICFX::Bomb;
1484 behav.Spell.SpellId = MAGICFX::Piercing;// + ForageKamiAngerPunishFX.get();
1485 behav.Spell.SpellIntensity = 5;
1486 behav.Spell.Resist = 0;
1487 behav.Spell2.SelfSpell = 1; // 'self offensive cast' does not plays the cast anim, only impact & FX
1488 PHRASE_UTILITIES::sendUpdateBehaviour( rowId, behav );
1490 // tmp nico : stats about projectiles
1491 projStatsIncrement();
1493 // Damage
1494 CHarvestSource::hitEntity( RYZOMID::creature, entity, ForageKamiAngerPunishDamage.get(), ForageKamiAngerPunishDamage.get(), false );
1496 //Bsi.append( StatPath, NLMISC::toString("[FKWP] %s '%s' %.1f", entity->getId().toString().c_str(), name().c_str(), _KamiAnger) );
1497 //EgsStat.displayNL("[FKWP] %s '%s' %.1f", entity->getId().toString().c_str(), name().c_str(), _KamiAnger);
1498 // EGSPD::forageKamiWrathPunishment(entity->getId(), name(), _KamiAnger);
1502 // Don't reset the kami anger level to the min value, but nearly to the threshold 2 value!
1503 _KamiAnger = ForageKamiAngerThreshold2.get() - 1.0f; // +2 will reach the threshold 2 again
1506 return _KamiAnger;
1510 //NLMISC_VARIABLE( bool, VerboseDeposits, "Verbose info for deposits" );
1512 NLMISC_COMMAND( forageDisplayRecentForageSitesNb, "Display the number of recent forage sites", "" )
1514 log.displayNL( "%u forage sites", TotalNbRecentForageSites );
1515 return true;
1518 NLMISC_COMMAND( forageDisplayRecentForageSitesInfo, "Display the info on all recent forage sites", "" )
1520 const std::vector<CDeposit*>& deposits = CZoneManager::getInstance().getDeposits();
1521 std::vector<CDeposit*>::const_iterator it;
1522 for ( it=deposits.begin(); it!=deposits.end(); ++it )
1524 CDeposit *deposit = (*it);
1525 deposit->displayRecentForageSites( log );
1527 return true;
1530 NLMISC_COMMAND( forageEmptyAllRecentForageSites, "Empty all forage sites", "" )
1532 const std::vector<CDeposit*>& deposits = CZoneManager::getInstance().getDeposits();
1533 std::vector<CDeposit*>::const_iterator it;
1534 for ( it=deposits.begin(); it!=deposits.end(); ++it )
1536 CDeposit *deposit = (*it);
1537 deposit->depleteAllRecentForageSites();
1539 return true;
1542 NLMISC_COMMAND( forageDisplayDeposit, "Display info about one or all the deposits", "<name=ALL> <extendedInfo=0>" )
1544 string depName = "ALL";
1545 bool extendedInfo = false;
1546 if ( args.size() > 0 )
1548 depName = args[0];
1549 if ( args.size() > 1 )
1550 extendedInfo = (args[1] == "1");
1552 CZoneManager::getInstance().dumpDeposits( log, depName, extendedInfo );
1553 return true;
1556 struct TCompareDepositsByHighestKamiAnger : public std::binary_function<CDeposit*,CDeposit*,bool>
1558 bool operator() ( const CDeposit* d1, const CDeposit* d2 )
1560 return d1->kamiAnger() > d2->kamiAnger();
1564 NLMISC_COMMAND( forageDisplayKamiAngerLevels, "Display the N deposits with the highest anger levels", "<N=10>" )
1566 uint nb = 10;
1567 if ( args.size() > 0 )
1568 NLMISC::fromString(args[0], nb);
1570 std::vector<CDeposit*> newDepositList = CZoneManager::getInstance().getDeposits();
1571 std::sort( newDepositList.begin(), newDepositList.end(), TCompareDepositsByHighestKamiAnger() );
1573 log.displayNL( "%u first deposits by kami anger:", nb );
1574 for ( uint i=0; i!=nb && i<newDepositList.size(); ++i )
1576 log.displayRawNL( "%.1f\t%s", newDepositList[i]->kamiAnger(), newDepositList[i]->name().c_str() );
1578 return true;
1581 NLMISC_VARIABLE( bool, VerboseDepositFiltering, "Verbose the items parsed when filtering deposits" );
1582 NLMISC_VARIABLE( bool, ExportDepositContents, "When loading the deposit primitives, export contents report" );