// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdpch.h" #include "deposit.h" #include "player_manager/character.h" #include "player_manager/player_manager.h" #include "player_manager/player.h" #include "zone_manager.h" #include "entities_game_service.h" #include "egs_globals.h" #include "nel/misc/noise_value.h" #include "nel/misc/variable.h" #include "nel/misc/words_dictionary.h" #include "game_share/time_weather_season/time_date_season_manager.h" #include "game_share/people.h" #include "egs_sheets/egs_sheets.h" #include "phrase_manager/fg_prospection_phrase.h" #include "phrase_manager/phrase_utilities_functions.h" #include "game_share/send_chat.h" #include "game_share/multi_target.h" #include "phrase_manager/s_effect.h" #include "projectile_stats.h" using namespace std; using namespace NLMISC; using namespace NLLIGO; using namespace NLNET; using namespace RM_FAMILY; const float SmallDepositAreaThreshold = 64.0f*64.0f; const float SmallDepositAreaReference = 16.0f*32.0f; // The ecotype zones (only valid at init, cleared after deposit built) CEcotypeZones CDeposit::_EcotypeZones; NL_INSTANCE_COUNTER_IMPL(CAutoSpawnProperties); NL_INSTANCE_COUNTER_IMPL(CQuantityConstraints); // Verbose deposits debug variable //bool VerboseDeposits = false; // Total number of CRecentForageSite objects (debug info) uint32 TotalNbRecentForageSites = 0; /// Export deposit contents report bool ExportDepositContents = false; /// Verbose the items parsed when filtering deposits bool VerboseDepositFiltering = false; void cbChangeDepositUpdateFrequency( NLMISC::IVariable& v ) { /*const TGameCycle expectedTTL = 6000; // 10 min if ( DepositUpdateFrequency.get() != 0 ) ForageSiteNbUpdatesToLive.set( (uint16)(expectedTTL / DepositUpdateFrequency.get()) );*/ nlinfo( "TODO: change ForageSiteNbUpdatesToLive according to DepositUpdateFrequency" ); } /* * Utility functions */ bool malformed( const char *field, const string& name ) { nlwarning( "FG: Malformed primitive %s, missing field '%s'", name.c_str(), field ); return false; } /* * Get the enums of the families (using RM_FAMILY::toFamily(), assumes the string are ended by _value) * where value is the identifier number (see .typ). * Removes duplicates (with a warning). */ void convertRMFamiliesNames( const string& name, const vector& src, vector& dest ) { for ( uint i=0; i!=src.size(); ++i ) { if ( ! src[i].empty() ) { string::size_type p = src[i].find_last_of( '_' ); if ( p != string::npos ) { TRMFamily family = toFamily( src[i].substr( p + 1 ) ); if ( find( dest.begin(), dest.end(), family ) == dest.end() ) dest.push_back( family ); else nlwarning( "FG: Deposit %s: %s found twice", name.c_str(), src[i].c_str() ); } else { nlwarning( "FG: Deposit %s: %s: not found", name.c_str(), src[i].c_str() ); } } } } /* * Get the indices of the item parts (assumes the strings are ended by _index). * Ex: blade_0. * Removes duplicates (with a warning). */ void convertItemPartsNames( const string& name, const vector& src, vector& dest ) { for ( uint i=0; i!=src.size(); ++i ) { if ( ! src[i].empty() ) { string::size_type p = src[i].find_last_of( '_' ); if ( p != string::npos ) { uint itemPart = atoi( src[i].substr( p + 1 ).c_str() ); if ( find( dest.begin(), dest.end(), itemPart ) == dest.end() ) dest.push_back( itemPart ); else nlwarning( "FG: Deposit %s: %s found twice", name.c_str(), src[i].c_str() ); } else { nlwarning( "FG: Deposit %s: %s: not found", name.c_str(), src[i].c_str() ); } } } } /* * Get the civ enum. */ void convertCraftCivNames( const vector& src, vector& dest ) { dest.resize( src.size() ); for ( uint i=0; i!=src.size(); ++i ) { dest[i] = ITEM_ORIGIN::stringToEnum( src[i] ); } } /* * */ inline void makeFullSItemCode( string& code ) { if ( code.find( ".sitem" ) == string::npos ) { code += ".sitem"; } } /* * Return always a forage site */ CRecentForageSite *CDeposit::findOrCreateForageSite( const NLMISC::CVector& pos ) { // Search in existing ones for ( CRecentForageSites::iterator ihs=_RecentForageSites.begin(); ihs!=_RecentForageSites.end(); ++ihs ) { if ( (*ihs).contains( pos ) ) { return &(*ihs); } } // Not found, create one ++TotalNbRecentForageSites; CRecentForageSite newForageSite( this, pos ); _RecentForageSites.push_back( newForageSite ); return &_RecentForageSites.back(); // returning the pointer is valid because an element in std::list is never invalidated } /* * */ bool CRecentForageSite::contains( const NLMISC::CVector& pos ) const { float r = ForageSiteRadius.get(); return ((pos-_Pos).sqrnorm() < r*r); } /* * Display debug or stat info */ void CRecentForageSite::display( NLMISC::CLog& log ) const { log.displayNL( "Forage site at %s: %u sources, stock %u, %u lowfreq updates remaining", _Pos.asString().c_str(), _NbActiveSources, _LowScopeStock, _TimeToLive ); } //----------------------------------------------------------------------------- // Parse primitive file for one ecotype //----------------------------------------------------------------------------- bool CEcotypeZone::build( const NLLIGO::CPrimZone* zone ) { *( (NLLIGO::CPrimZone*)this ) = *zone; // Read primitive name string name; if ( ! zone->getPropertyByName( "name", name ) ) return malformed( "name", name ); // Read ecotype string ecotypeS; if ( ! zone->getPropertyByName( "ecotype", ecotypeS ) ) return malformed( "ecotype", name ); _Ecotype = ECOSYSTEM::stringToEcosystem( ecotypeS ); // needs a case-unsensitive comparison, because the if ( _Ecotype == ECOSYSTEM::unknown ) // World Editor lists names of files in CVS (in deposit_system/ecotypes) nlwarning( "%s: Ecotype %s unknown", name.c_str(), ecotypeS.c_str() ); return true; } /* * Destructor */ CDeposit::~CDeposit() { if ( _AutoSpawnSourcePt ) delete _AutoSpawnSourcePt; _AutoSpawnSourcePt = NULL; if ( _QuantityConstraintsPt ) delete _QuantityConstraintsPt; _QuantityConstraintsPt = NULL; // Avoid any bug, remove the deposit from zone manager CZoneManager::getInstance().unregisterDepositToAutoSpawnUpdate(this); } /* * Get the ecotype zone under the position. * This information is valid only at init time. After the deposits are built, this information * can be found only in deposits. * If not found, a NULL pointer is returned. */ CEcotypeZone *CDeposit::getEcotypeZone( const NLMISC::CVector& pos ) { // The ecotypes must not be overlapped: only the first one found is returned for ( CEcotypeZones::iterator it=_EcotypeZones.begin(); it!=_EcotypeZones.end(); ++it ) { CEcotypeZone *ecotypeZone = (*it); if ( ecotypeZone->contains( pos ) ) { return ecotypeZone; } } return NULL; } /* * Clear ecotype information, after having built the deposits */ void CDeposit::clearEcotypes() { for ( CEcotypeZones::iterator iez=_EcotypeZones.begin(); iez!=_EcotypeZones.end(); ++iez ) { delete (*iez); } _EcotypeZones.clear(); } struct TCompareStaticItemPtrBySheetId : public std::binary_function { bool operator() ( const CStaticItem* p1, const CStaticItem* p2 ) { return (p1->SheetId < p2->SheetId); } }; //----------------------------------------------------------------------------- // Parse primitive file for one deposit //----------------------------------------------------------------------------- bool CDeposit::build( const NLLIGO::CPrimZone* zone ) { if ( IsRingShard ) return false; //_Id =id; *( (NLLIGO::CPrimZone*)this ) = *zone; // Read primitive name string name; if ( ! zone->getPropertyByName( "name", name ) ) return malformed( "name", name ); _Name = name; // Read exact raw material codes to add vector *exactRMCodesS = NULL; if ( ! (zone->getPropertyByName( "exact_mp_item", exactRMCodesS ) && exactRMCodesS) ) return malformed( "exact_mp_item", name ); // Read raw material family filter vector *rmFamilyFilterS = NULL; if ( ! (zone->getPropertyByName( "mps", rmFamilyFilterS ) && rmFamilyFilterS) ) return malformed( "mps", name ); // Read item part / craft civ filter vector *itemPartsFilterS = NULL, *craftCivS = NULL; if ( ! (zone->getPropertyByName( "item_parts", itemPartsFilterS ) && itemPartsFilterS) ) return malformed( "item_parts", name ); if ( ! (zone->getPropertyByName( "craft_civ", craftCivS ) && craftCivS) ) return malformed( "craft_civ", name ); // Read stat+quality filters string minEnergyS, maxEnergyS, minQualityS, maxQualityS; if ( ! zone->getPropertyByName( "deposit_statquality_min", minEnergyS ) ) return malformed( "deposit_statquality_min", name ); if ( ! zone->getPropertyByName( "deposit_statquality_max", maxEnergyS ) ) return malformed( "deposit_statquality_max", name ); if ( ! zone->getPropertyByName( "deposit_min_quality_250", minQualityS ) ) return malformed( "deposit_min_quality_250", name ); if ( ! zone->getPropertyByName( "deposit_max_quality_250", maxQualityS ) ) return malformed( "deposit_max_quality_250", name ); _MinQuality = atoi( minQualityS.c_str() ); _MaxQuality = atoi( maxQualityS.c_str() ); // Read quantity constraints string qttyLimitS, qttyRespawnTimeS; if ( ! zone->getPropertyByName( "deposit_quantity_limit", qttyLimitS ) ) return malformed( "deposit_quantity_limit", name ); if ( ! zone->getPropertyByName( "deposit_quantity_respawn_time_ryzomdays", qttyRespawnTimeS ) ) return malformed( "deposit_quantity_respawn_time_ryzomdays", name ); sint qttyLimit = atoi( qttyLimitS.c_str() ); if ( qttyLimit > -1 ) { sint qttyRespawnTime = atoi( qttyRespawnTimeS.c_str() ); if ( (qttyLimit == 0) || (qttyLimit > 0xFFFF) || (qttyRespawnTime < 1) || (qttyRespawnTime > 0xFFFF) ) nlwarning( "Invalid limit or respawn time too high in %s", name.c_str() ); else { _QuantityConstraintsPt = new CQuantityConstraints(); _QuantityConstraintsPt->CurrentQuantity = (float)qttyLimit; _QuantityConstraintsPt->InitialQuantity = (uint16)qttyLimit; _QuantityConstraintsPt->RespawnTimeRyzomDays = qttyRespawnTime; } } // Get ecotype CEcotypeZone *ecotypeZone = getEcotypeZone( getBarycentre() ); _Ecotype = ecotypeZone ? ecotypeZone->ecotype() : ECOSYSTEM::common_ecosystem; if ( ! ecotypeZone ) nlwarning( "FG: Deposit %s has no ecotype", name.c_str() ); _FilterPhase = 0; // Read season(s) string s1s, s2s, s3s, s4s; if ( ! zone->getPropertyByName( "while_season_spring", s1s ) ) return malformed( "while_season_spring", name ); if ( ! zone->getPropertyByName( "while_season_summer", s2s ) ) return malformed( "while_season_summer", name ); if ( ! zone->getPropertyByName( "while_season_automn", s3s ) ) return malformed( "while_season_automn", name ); if ( ! zone->getPropertyByName( "while_season_winter", s4s ) ) return malformed( "while_season_winter", name ); bool s1 = (s1s=="true"), s2 = (s2s=="true"), s3 = (s3s=="true"), s4 = (s4s=="true"); if ( ! (s1 && s2 && s3 && s4) ) { if ( s1 ) { _SeasonFilter.push_back( EGSPD::CSeason::Spring ); _FilterPhase |= 0x1; } if ( s2 ) { _SeasonFilter.push_back( EGSPD::CSeason::Summer ); _FilterPhase |= 0x2; } if ( s3 ) { _SeasonFilter.push_back( EGSPD::CSeason::Autumn ); _FilterPhase |= 0x4; } if ( s4 ) { _SeasonFilter.push_back( EGSPD::CSeason::Winter ); _FilterPhase |= 0x8; } } // Read weather string w1s, w2s, w3s, w4s; if ( ! zone->getPropertyByName( "while_weather_0_best", w1s ) ) return malformed( "while_weather_0_best", name ); if ( ! zone->getPropertyByName( "while_weather_1_good", w2s ) ) return malformed( "while_weather_1_good", name ); if ( ! zone->getPropertyByName( "while_weather_2_bad", w3s ) ) return malformed( "while_weather_2_bad", name ); if ( ! zone->getPropertyByName( "while_weather_3_worst", w4s ) ) return malformed( "while_weather_3_worst", name ); bool w1 = (w1s=="true"), w2 = (w2s=="true"), w3 = (w3s=="true"), w4 = (w4s=="true"); if ( ! (w1 && w2 && w3 && w4) ) { if ( w1 ) { _WeatherFilter.push_back( CRyzomTime::best ); _FilterPhase |= 0x10; } if ( w2 ) { _WeatherFilter.push_back( CRyzomTime::good ); _FilterPhase |= 0x20; } if ( w3 ) { _WeatherFilter.push_back( CRyzomTime::bad ); _FilterPhase |= 0x40; } if ( w4 ) { _WeatherFilter.push_back( CRyzomTime::worst ); _FilterPhase |= 0x80; } } // Read time of day string t1s, t2s; if ( ! zone->getPropertyByName( "while_its_day", t1s ) ) return malformed ( "while_its_day", name ); if ( ! zone->getPropertyByName( "while_its_night", t2s ) ) return malformed ( "while_its_night", name ); bool t1 = (t1s=="true"), t2 = (t2s=="true"); if ( ! (t1 && t2) ) { if ( t1 ) { _TimeOfDayFilter.push_back( CRyzomTime::dawn ); _TimeOfDayFilter.push_back( CRyzomTime::day ); _TimeOfDayFilter.push_back( CRyzomTime::evening ); _FilterPhase |= 0x100; } if ( t2 ) { _TimeOfDayFilter.push_back( CRyzomTime::nightfall ); _TimeOfDayFilter.push_back( CRyzomTime::night ); _FilterPhase |= 0x200; } } // Read auto-spawn properties string assS; if ( ! zone->getPropertyByName( "auto_spawn_sources", assS ) ) return malformed( "auto_spawn_sources", name ); if ( assS=="true" ) { string aspS, asltS, asetS, amin; // not really 'average' anymore if ( ! zone->getPropertyByName( "auto_spawn_average_period_s", aspS ) ) return malformed( "auto_spawn_average_period", name ); if ( ! zone->getPropertyByName( "auto_spawn_lifetime_s", asltS ) ) return malformed( "auto_spawn_lifetime", name ); if ( ! zone->getPropertyByName( "auto_spawn_extraction_time_s", asetS ) ) return malformed( "auto_spawn_sources", name ); if ( ! zone->getPropertyByName( "auto_spawn_min_source", amin ) ) return malformed( "auto_spawn_min_source", name ); _AutoSpawnSourcePt = new CAutoSpawnProperties; _AutoSpawnSourcePt->SpawnPeriodGc = atoi( aspS.c_str() ) * 10; _AutoSpawnSourcePt->LifeTimeGc = atoi( asltS.c_str() ) * 10; _AutoSpawnSourcePt->ExtractionTimeGc = atoi( asetS.c_str() ) * 10; _AutoSpawnSourcePt->MinimumSpawnedSources = atoi( amin.c_str() ); // security! _AutoSpawnSourcePt->MinimumSpawnedSources = min(uint32(100), _AutoSpawnSourcePt->MinimumSpawnedSources); } else { _AutoSpawnSourcePt = NULL; } // Read source FX index string srcFXIndexS; if ( ! zone->getPropertyByName( "source_fx", srcFXIndexS ) ) return malformed( "source_fx", name ); _SourceFXIndex = (uint16)atoi( srcFXIndexS.c_str() ); // Read other initial properties string cpS, eS, ikaS, adpR; if ( ! zone->getPropertyByName( "can_prospect", cpS ) ) return malformed( "can_prospect", name ); if ( ! zone->getPropertyByName( "enabled", eS ) ) return malformed( "enabled", name ); if ( ! zone->getPropertyByName( "initial_kami_anger", ikaS ) ) return malformed( "initial_kami_anger", name ); if ( ! zone->getPropertyByName( "can_have_depletion_risk", adpR ) ) return malformed( "can_have_depletion_risk", name ); _CanProspect = (cpS=="true"); _Enabled = (eS=="true"); _AllowDepletionRisk = (adpR=="true"); _KamiAnger = (float)atof( ikaS.c_str() ); if ( (_KamiAnger != -1.0f) && (_KamiAnger < 0) ) nlwarning( "Invalid initial_kami_anger %.1f in %s", _KamiAnger, name.c_str() ); // Apply filters uint32 minEnergy = atoi( minEnergyS.c_str() ); uint32 maxEnergy = atoi( maxEnergyS.c_str() ); if ( exactRMCodesS->empty() && rmFamilyFilterS->empty() && itemPartsFilterS->empty() ) { nlwarning( "FG: Deposit %s: No RM, exactRms or item parts specified!", name.c_str() ); return false; } else selectRMsByFilters( *exactRMCodesS, *rmFamilyFilterS, *itemPartsFilterS, *craftCivS, minEnergy, maxEnergy ); nldebug( "FG: Built deposit %s %s %s %s %s", name.c_str(), _AutoSpawnSourcePt?"AUTO":"-", _CanProspect?"PRO":"-", _AllowDepletionRisk?"ADPR":"-", _Enabled?"ON":"OFF" ); return true; } /* * Select the raw materials, using the specified filters and _Ecotype */ void CDeposit::selectRMsByFilters( std::vector& exactRMCodesS, const std::vector& rmFamilyFilterS, const std::vector& itemPartsFilterS, const std::vector& craftCivS, uint minEnergy, uint maxEnergy ) { vector rmFamilyFilter; convertRMFamiliesNames( _Name, rmFamilyFilterS, rmFamilyFilter ); vector itemPartsFilter; convertItemPartsNames( _Name, itemPartsFilterS, itemPartsFilter ); vector craftCivFilter; convertCraftCivNames( craftCivS, craftCivFilter ); for_each( exactRMCodesS.begin(), exactRMCodesS.end(), makeFullSItemCode ); if ( VerboseDepositFiltering ) nldebug( "%s", _Name.c_str() ); // Sort the items by sheetId so that the deposits are not dependant on the hash map ordering const CAllStaticItems& allItems = CSheets::getItemMapForm(); vector< const CStaticItem* > sortedItems( allItems.size() ); uint i = 0; for ( CAllStaticItems::const_iterator it=allItems.begin(); it!=allItems.end(); ++it, ++i ) { sortedItems[i] = &((*it).second); } std::sort( sortedItems.begin(), sortedItems.end(), TCompareStaticItemPtrBySheetId() ); for ( vector< const CStaticItem* >::const_iterator it=sortedItems.begin(); it!=sortedItems.end(); ++it ) { // Eliminate non raw materials const CStaticItem& staticItem = *(*it); if ( ! staticItem.Mp ) continue; const string& sheetName = staticItem.SheetId.toString(); if ( (sheetName.size() < (CREATURE_OR_DEPOSIT_MP_CHAR+1)) || (sheetName[0] != 'm' ) ) continue; // Keep raw material directly if matching one of specified exact raw material codes (including creature's raw materials) if ( find( exactRMCodesS.begin(), exactRMCodesS.end(), sheetName ) == exactRMCodesS.end() ) { // Eliminate non 'forage' raw materials if ( VerboseDepositFiltering ) nldebug( "FG: Submitting %s", sheetName.c_str() ); if ( (sheetName[CREATURE_OR_DEPOSIT_MP_CHAR] != 'd') ) continue; // Eliminate raw materials of incompatible ecosystem if ( (_Ecotype != ECOSYSTEM::common_ecosystem) && (staticItem.Mp->Ecosystem != ECOSYSTEM::common_ecosystem) && (staticItem.Mp->Ecosystem != _Ecotype) ) { if ( VerboseDepositFiltering ) nldebug( "-Ecotype %s", ECOSYSTEM::toString( staticItem.Mp->Ecosystem ).c_str() ); continue; } // Match energy filter (TEMP: 25 is the minimum maxEnergy threshold) (include boundaries) if ( (staticItem.Mp->StatEnergy < minEnergy) || (staticItem.Mp->StatEnergy > maxEnergy /*max(maxEnergy,(uint)25)*/) ) { if ( VerboseDepositFiltering ) nldebug( "-StatEnergy %hu", staticItem.Mp->StatEnergy ); continue; } // Match rmFamilyFilter or itemPartsFilter if ( find( rmFamilyFilter.begin(), rmFamilyFilter.end(), staticItem.Mp->Family ) == rmFamilyFilter.end() ) { // If not found in rmFamilyFilter, try if one of itemPartsFilter is matched along with civ bool found = false; for ( vector::iterator ipf=itemPartsFilter.begin(); ipf!=itemPartsFilter.end(); ++ipf ) { uint itemPartIndex = (*ipf); // Find if the current RM has the item part matching one of the filter const CMP::TMpFaberParameters *mpFaberParam = staticItem.Mp->getMpFaberParameters( itemPartIndex ); if ( mpFaberParam && (mpFaberParam->Durability != 0) ) { if ( (craftCivFilter.empty()) || // no civ constraint (mpFaberParam->CraftCivSpec == ITEM_ORIGIN::COMMON) || // RM matches all civs (find( craftCivFilter.begin(), craftCivFilter.end(), mpFaberParam->CraftCivSpec ) != craftCivFilter.end()) ) // RM matches civ constraint { if ( VerboseDepositFiltering ) nldebug( "+StatEnergy %hu +ItemPart %c +CivSpec %s", staticItem.Mp->StatEnergy, 'A' + (char)itemPartIndex, ITEM_ORIGIN::enumToString( mpFaberParam->CraftCivSpec ).c_str() ); found = true; break; // RM is selected, no need to test other item parts } else if ( VerboseDepositFiltering ) nldebug( "-CivSpec %s", ITEM_ORIGIN::enumToString( mpFaberParam->CraftCivSpec ).c_str() ); } else { if ( VerboseDepositFiltering ) nldebug( "-ItemPart %c", 'A' + (char)itemPartIndex ); } } if ( ! found ) continue; } /*else { if ( VerboseDepositFiltering ) nldebug( "+rmFamilyFilter %u", staticItem.Mp->Family ); }*/ } /*else { if ( VerboseDepositFiltering ) nldebug( "+exact_mp_item %s", sheetName.c_str() ); }*/ // Select if matching (do not check duplicate, keep them if item part matches explicit family) CStaticDepositRawMaterial rm; rm.MaterialSheet = staticItem.SheetId; _RawMaterials.push_back( rm ); if ( VerboseDepositFiltering ) nldebug( "SELECTED %s", sheetName.c_str() ); // Export deposit contents report if requested if ( ExportDepositContents ) { static FILE *depositReportFile; static bool depositReportCreated = false; if ( ! depositReportCreated ) { depositReportCreated = true; depositReportFile = fopen( "deposit_contents.csv", "wt" ); // fclose() auto? if ( depositReportFile ) { fprintf( depositReportFile, "Deposit;RM;When in year;When in day;Weather;\n" ); } } if ( depositReportFile ) 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() ); } } if ( _RawMaterials.empty() ) nlwarning( "FG: Selected 0 items in deposit %s", _Name.c_str() ); else nldebug( "FG: Selected %u RM (on %u items) in deposit %s", _RawMaterials.size(), allItems.size(), _Name.c_str() ); } /** * Helper for CDeposit::hasFamily() */ struct CIsOfFamilyPred : public std::binary_function< CStaticDepositRawMaterial, RM_FAMILY::TRMFamily, bool > { /// Predicate bool operator() ( const CStaticDepositRawMaterial& rm, RM_FAMILY::TRMFamily family ) const { const CAllStaticItems& allItems = CSheets::getItemMapForm(); CAllStaticItems::const_iterator it = allItems.find( rm.MaterialSheet ); if ( it != allItems.end() ) { const CStaticItem& staticItem = (*it).second; return staticItem.Mp && (staticItem.Mp->Family == family); } else return false; } }; /** * Helper for CDeposit::hasFamily() */ struct CIsOfGroupPred : public std::binary_function< CStaticDepositRawMaterial, RM_GROUP::TRMGroup, bool > { /// Predicate bool operator() ( const CStaticDepositRawMaterial& rm, RM_GROUP::TRMGroup group ) const { const CAllStaticItems& allItems = CSheets::getItemMapForm(); CAllStaticItems::const_iterator it = allItems.find( rm.MaterialSheet ); if ( it != allItems.end() ) { const CStaticItem& staticItem = (*it).second; return staticItem.Mp && (staticItem.Mp->getGroup() == group); } else return false; } }; /** * Helper for CDeposit::hasRMForItemPart() */ struct CCanCraftItemPartPred : public std::binary_function< CStaticDepositRawMaterial, uint, bool > { /// Predicate bool operator() ( const CStaticDepositRawMaterial& rm, uint itemPartIndex ) const { const CAllStaticItems& allItems = CSheets::getItemMapForm(); CAllStaticItems::const_iterator it = allItems.find( rm.MaterialSheet ); if ( it != allItems.end() ) { const CStaticItem& staticItem = (*it).second; if ( staticItem.Mp ) { const CMP::TMpFaberParameters *mpFaberParam = staticItem.Mp->getMpFaberParameters( itemPartIndex ); return (mpFaberParam && (mpFaberParam->Durability != 0)); } else return false; } else return false; } }; /** * Helper for CDeposit::hasFamily() */ struct CMatchStatEnergyPred : public std::binary_function< CStaticDepositRawMaterial, uint8, bool > { /// Predicate bool operator() ( const CStaticDepositRawMaterial& rm, uint8 maxStatEnergy ) const { const CAllStaticItems& allItems = CSheets::getItemMapForm(); CAllStaticItems::const_iterator it = allItems.find( rm.MaterialSheet ); if ( it != allItems.end() ) { const CStaticItem& staticItem = (*it).second; return staticItem.Mp && (staticItem.Mp->StatEnergy <= maxStatEnergy); // include boundary } else { nlwarning( "%s not found", rm.MaterialSheet.toString().c_str() ); return true; } } }; /** * Helper for CDeposit::hasFamily() */ struct CMatchExactStatEnergyPred : public std::binary_function< CStaticDepositRawMaterial, uint8, bool > { /// Predicate bool operator() ( const CStaticDepositRawMaterial& rm, uint8 maxStatEnergy ) const { const CAllStaticItems& allItems = CSheets::getItemMapForm(); CAllStaticItems::const_iterator it = allItems.find( rm.MaterialSheet ); if ( it != allItems.end() ) { const CStaticItem& staticItem = (*it).second; return staticItem.Mp && (staticItem.Mp->StatEnergy == maxStatEnergy); } else return false; } }; /* * Return true if the deposit contains at least one RM of the specified family */ bool CDeposit::hasFamily( RM_FAMILY::TRMFamily family ) const { return find_if( _RawMaterials.begin(), _RawMaterials.end(), bind2nd( CIsOfFamilyPred(), family ) ) != _RawMaterials.end(); } /* * Return true if the deposit contains at least one RM of the specified group */ bool CDeposit::hasGroup( RM_GROUP::TRMGroup group ) const { return find_if( _RawMaterials.begin(), _RawMaterials.end(), bind2nd( CIsOfGroupPred(), group ) ) != _RawMaterials.end(); } /* * Return true if the deposit contains at least one RM than can craft the specified item part */ bool CDeposit::hasRMForItemPart( uint itemPartIndex ) const { return find_if( _RawMaterials.begin(), _RawMaterials.end(), bind2nd( CCanCraftItemPartPred(), itemPartIndex ) ) != _RawMaterials.end(); } /* * Return true if the deposit contains at least one RM with energy lower_eq than the specified value */ bool CDeposit::hasLowerStatEnergy( uint8 maxStatEnergy ) const { return find_if( _RawMaterials.begin(), _RawMaterials.end(), bind2nd( CMatchStatEnergyPred(), maxStatEnergy ) ) != _RawMaterials.end(); } /* * Return true if the deposit contains at least one RM with energy equalling the specifing value */ bool CDeposit::hasExactStatEnergy( uint8 statEnergy ) const { return find_if( _RawMaterials.begin(), _RawMaterials.end(), bind2nd( CMatchExactStatEnergyPred(), statEnergy ) ) != _RawMaterials.end(); } /* * Called by lowFreqUpdate and autoSpawnUpdate */ void CDeposit::autoSpawnSource(const CVector &cornerMin, const CVector &cornerMax) { /* Yoyo: This method may fail because it selects a random position in a box and then check if it's in the zone. We can do better by making a list of triangles of the concave polygon, then selecting randomly and carefully in this list of triangle. */ // Randomize a position in the deposit CVector pos; pos.z = 0; const uint NB_ATTEMPTS = 5; uint iAttempt; for ( iAttempt=0; iAttempt!=NB_ATTEMPTS; ++iAttempt ) { pos.x = cornerMin.x + RandomGenerator.frand( cornerMax.x - cornerMin.x ); pos.y = cornerMin.y + RandomGenerator.frand( cornerMax.y - cornerMin.y ); if ( contains( pos ) ) break; } // If a valid position could not be found, abort spawning of this source if ( iAttempt == NB_ATTEMPTS ) return; // Spawn a source CFgProspectionPhrase::autoSpawnSource( pos, this ); } /* * Update deposit (especially recent forage sites) */ void CDeposit::lowFreqUpdate() { if ( !HarvestSystemEnabled ) return; // Update recent forage sites for ( CRecentForageSites::iterator it=_RecentForageSites.begin(); it!=_RecentForageSites.end(); ) { CRecentForageSite& forageSite = (*it); if ( forageSite.lowFreqUpdate() ) { ++it; } else { --TotalNbRecentForageSites; it = _RecentForageSites.erase( it ); } } // Decrease kami anger level decKamiAnger( ForageKamiAngerDecreasePerHour.get() / 36000.0f * ((float)DepositUpdateFrequency.get()) ); // Auto-spawn a source from time to time (if the deposit has this flag) if ( _AutoSpawnSourcePt && _Enabled ) { // For Speed Test //TTime testYoyoLT0= CTime::getLocalTime(); //uint countBefore= _CurrentNbAutoSpawnedSources; uint spawnPeriodLFUpdates = (AutoSpawnForageSourcePeriodOverride.get() != 0) ? AutoSpawnForageSourcePeriodOverride.get() / DepositUpdateFrequency.get() : _AutoSpawnSourcePt->SpawnPeriodGc / DepositUpdateFrequency.get(); // get deposit bbox CVector cornerMin, cornerMax; getAABox( cornerMin, cornerMax ); // Previously, the rythm of spawning was randomized. Now it's constant. //TGameCycle spawnAvgPeriod = (AutoSpawnForageSourceAveragePeriodOverride.get() != 0) ? AutoSpawnForageSourceAveragePeriodOverride.get() : _AutoSpawnSourcePt->SpawnAveragePeriodGc; //sint32 r = RandomGenerator.rand( (uint16)(spawnAvgPeriod / DepositUpdateFrequency.get()) ); //if ( r == 0 ) bool spawnSourceBecauseOfFrequency= (CTickEventHandler::getGameCycle() / DepositUpdateFrequency.get()) % spawnPeriodLFUpdates == 0; // Avoid infinite loop if the spawn of source is too buggy: // count first the number of sources to force spawn then run this count, and forget the ones that fail. uint numSourceToForceSpawn= 0; if(_CurrentNbAutoSpawnedSources < _AutoSpawnSourcePt->MinimumSpawnedSources) numSourceToForceSpawn= _AutoSpawnSourcePt->MinimumSpawnedSources - _CurrentNbAutoSpawnedSources; uint numSourceSpawned= 0; while ( spawnSourceBecauseOfFrequency || numSourceSpawned%d): %d ms", CTickEventHandler::getGameCycle()%1000, countBefore, _CurrentNbAutoSpawnedSources, testYoyoLT1 - testYoyoLT0); } // Beware: can return before } /* * Update deposit that need to auto spawn because number of harvest sources is too low */ void CDeposit::autoSpawnUpdate() { if ( !HarvestSystemEnabled ) return; // Auto-spawn a source from time to time (if the deposit has this flag) if ( _AutoSpawnSourcePt && _Enabled && _CurrentNbAutoSpawnedSources < _AutoSpawnSourcePt->MinimumSpawnedSources) { // For Speed Test //TTime testYoyoLT0= CTime::getLocalTime(); //uint countBefore= _CurrentNbAutoSpawnedSources; // get deposit bbox CVector cornerMin, cornerMax; getAABox( cornerMin, cornerMax ); // Avoid infinite loop if the spawn of source is too buggy: // count first the number of sources to force spawn then run this count, and forget the ones that fail. uint numSourceToForceSpawn= _AutoSpawnSourcePt->MinimumSpawnedSources - _CurrentNbAutoSpawnedSources; // for all sources to spawn for( uint i= 0; i%d): %d ms", CTickEventHandler::getGameCycle()%1000, countBefore, _CurrentNbAutoSpawnedSources, testYoyoLT1 - testYoyoLT0); } // Beware: can return before } /* * Auto Spawn Deposit mgt */ void CDeposit::decreaseAutoSpawnedSources() { BOMB_IF(_CurrentNbAutoSpawnedSources==0, "nb auto spawned sources should be > 0!", return); _CurrentNbAutoSpawnedSources--; // If the deposit has not enough ressources, must refill at next tick update // NB: don't do it at next lowFreqUpdate(), to avoid big jump in CPU each 30 sec // as soon an autospawned harvest source is unspawned, the deposit will refill at next tick. if(_AutoSpawnSourcePt && _CurrentNbAutoSpawnedSources < _AutoSpawnSourcePt->MinimumSpawnedSources) { CZoneManager::getInstance().registerDepositToAutoSpawnUpdate(this); } } void CDeposit::increaseAutoSpawnedSources() { _CurrentNbAutoSpawnedSources++; } //----------------------------------------------------------------------------- // character take MP, kami eat this fool character ? //----------------------------------------------------------------------------- #if 0 //void CDeposit::characterTakeRM( const CEntityId& charId, uint32 depositIndexContent, uint16 quantity ) //{ // // check if depositIndexContent is in valid range // if( depositIndexContent < _DepositContent.size() ) // { // // process kami guardian reaction and fame impact // CDepositRawMaterialSeasonParameters * seasonParameters; // switch( CTimeDateSeasonManager::getRyzomTimeReference().getRyzomSeason() ) // { // case EGSPD::CSeason::Spring: // seasonParameters = &_DepositContent[ depositIndexContent ]->SpringParams; // break; // case EGSPD::CSeason::Summer: // seasonParameters = &_DepositContent[ depositIndexContent ]->SummerParams; // break; // case EGSPD::CSeason::Autumn: // seasonParameters = &_DepositContent[ depositIndexContent ]->AutumnParams; // break; // case EGSPD::CSeason::Winter: // seasonParameters = &_DepositContent[ depositIndexContent ]->WinterParams; // break; // default: // nlstop; //=> debug CTimeDateSeasonManager.... // } // // if( seasonParameters->AngryLevel >= _DepositContent[ depositIndexContent ]->CurrentQuantity ) // { // sint32 fameImpact; // uint32 kamiImpact; // // CMessage msgString("STATIC_STRING"); // msgString.serial( const_cast< CEntityId& > ( charId ) ); // // serial exclude set of empty exclude set // set< CEntityId > empty; // msgString.serialCont( empty ); // // if( seasonParameters->BlackKamiLevel >= _DepositContent[ depositIndexContent ]->CurrentQuantity ) // { // // kami impact for AIS when Kami expulse a black kami // kamiImpact = 300; // // // set fame impact // fameImpact = -15; // // string str("WOS_KAMI_BLACK_KAMI"); // msgString.serial(str); // } // else if( seasonParameters->FuryLevel >= _DepositContent[ depositIndexContent ]->CurrentQuantity ) // { // // kami impact for AIS when Kami is fury against harvester // kamiImpact = 200; // // // set fame impact // fameImpact = -10; // // string str("WOS_KAMI_FURY"); // msgString.serial( str ); // } // else if( seasonParameters->AngryLevel >= _DepositContent[ depositIndexContent ]->CurrentQuantity ) // { // // kami impact for AIS when Kami is angry against harvester // kamiImpact = 100; // // // set fame impact // fameImpact = -5; // // string str("WOS_KAMI_ANGRY"); // msgString.serial( str ); // } // sendMessageViaMirror( "IOS", msgString ); // // // Send message to AIS for kami impact // CMessage msgout("KAMI_IMPACT"); // // TODO : if this code reactivated, add the instance number and send to the corect AIS // msgout.serial( const_cast (charId) ); // msgout.serial( _Alias ); // msgout.serial( kamiImpact ); // sendMessageViaMirror( "AIS", msgout ); // // // // send message to EGS for fame impact // // TODO boris : update fame with new fame system ///* CCharacter * c = PlayerManager.getChar( charId ); // uint8 fameType = FAMES::kami; // if( c ) // { // c->fameChange( fameType, fameImpact ); // } //*/ } // // // update deposit quantity // if( _DepositContent[ depositIndexContent ]->CurrentQuantity < quantity ) // { // _DepositContent[ depositIndexContent ]->CurrentQuantity = 0; // } // else // { // _DepositContent[ depositIndexContent ]->CurrentQuantity -= quantity; // } // } // else // { // nlwarning("FG: Received invalid depositIndexContent %d (deposit content size id %d)", depositIndexContent, _DepositContent.size() ); // } //} #endif string CDeposit::getSeasonStr() const { string s; if ( _SeasonFilter.empty() ) return "All year"; for ( uint i=0; i!=_SeasonFilter.size(); ++i ) { if ( ! s.empty() ) s += " "; s += EGSPD::CSeason::toString( _SeasonFilter[i] ); } return s; } string CDeposit::getTimeOfDayStr() const { string s; if ( _TimeOfDayFilter.empty() ) return "Night & day"; for ( uint i=0; i!=_TimeOfDayFilter.size(); ++i ) { if ( ! s.empty() ) s += " "; s += toString( "%u", (uint)_TimeOfDayFilter[i] ); } return s; } string CDeposit::getWeatherStr() const { string s; if ( _WeatherFilter.empty() ) return "All weathers"; for ( uint i=0; i!=_WeatherFilter.size(); ++i ) { if ( ! s.empty() ) s += " "; s += toString( "%u", (uint)_WeatherFilter[i] ); } return s; } //----------------------------------------------------------------------------- // display deposit content //----------------------------------------------------------------------------- void CDeposit::displayContent( NLMISC::CLog * log, bool extendedInfo, NLMISC::CWordsDictionary *itemDictionary ) { const CAllStaticItems& allItems = CSheets::getItemMapForm(); uint32 materialNumber = 0; log->displayRawNL( "---- DEPOSIT %s ----", _Name.c_str() ); log->displayRawNL( "\t Centre: %s", getBarycentre().toString().c_str() ); if ( getContents().empty() ) log->displayRawNL( "\tNo RM was selected in this deposit" ); log->displayRawNL( "\t%u RMs:", getContents().size() ); for( std::vector< CStaticDepositRawMaterial >::const_iterator it = getContents().begin(); it != getContents().end(); ++it ) { if ( extendedInfo ) log->displayRawNL( "\tRM #%d", materialNumber ); ++materialNumber; CSString sheetCode = (*it).MaterialSheet.toString(); string fullNameInfo; if ( itemDictionary ) { CVectorSString result; sheetCode = sheetCode.splitTo( '.' ); itemDictionary->lookup( sheetCode, result ); if ( ! result.empty() ) fullNameInfo = string(" ") + result[0]; } if ( fullNameInfo.empty() ) log->displayRawNL( "\t\tRM Id: %s", sheetCode.c_str() ); else log->displayRawNL( "\t\tRM Id: %s", fullNameInfo.c_str() ); //log->displayNL( "\t\tMax Amount: %d", (*it).MaxAmount ); if ( extendedInfo ) { CAllStaticItems::const_iterator itItem = allItems.find( (*it).MaterialSheet ); if ( itItem != allItems.end() && (*itItem).second.Mp ) { const CStaticItem& staticItem = (*itItem).second; log->displayRawNL( "\t\tFamily: %u", staticItem.Mp->Family ); log->displayRawNL( "\t\tGroup: %u", staticItem.Mp->getGroup() ); log->displayRawNL( "\t\tEcosystem: %s", ECOSYSTEM::toString( staticItem.Mp->Ecosystem ).c_str() ); //log->displayRawNL( "\t\tRarity: %hu", staticItem.Mp->Rarity ); log->displayRawNL( "\t\tStatEnergy: %hu", staticItem.Mp->StatEnergy ); } } } log->displayRawNL( "\tEcotype: %s", ECOSYSTEM::toString( _Ecotype ).c_str() ); string ws; if ( _WeatherFilter.empty() ) ws = "all"; else { for( vector::const_iterator iwf=_WeatherFilter.begin(); iwf!=_WeatherFilter.end(); ++iwf ) ws += toString( "%u:", *iwf ); } log->displayRawNL( "\tWeathers: %s", ws.c_str() ); string ts; if ( _TimeOfDayFilter.empty() ) ts = "all"; else { for( vector::const_iterator itf=_TimeOfDayFilter.begin(); itf!=_TimeOfDayFilter.end(); ++itf ) ts += toString( "%u:", *itf ); } log->displayRawNL( "\tTimes of day: %s", ts.c_str() ); string ss; if ( _SeasonFilter.empty() ) ss = "all"; else { for( vector::const_iterator isf=_SeasonFilter.begin(); isf!=_SeasonFilter.end(); ++isf ) ws += toString( "%u:", *isf ); } log->displayRawNL( "\tSeasons: %s", ss.c_str() ); log->displayRaw( "\tKami anger: %.1f", _KamiAnger ); if ( _KamiAnger == -1.0f ) log->displayRawNL( " disabled" ); else log->displayRawNL( " on %.1f, %.1f", ForageKamiAngerThreshold1.get(), ForageKamiAngerThreshold2.get() ); if ( _AutoSpawnSourcePt ) log->displayRawNL( "\tAutoSpawnSource: %s", _AutoSpawnSourcePt?"ON":"OFF" ); if ( ! _CanProspect ) log->displayRawNL( "\tCanProspect: %d", _CanProspect ); if ( ! _Enabled ) log->displayRawNL( "\tEnabled: %d", _Enabled ); if ( ! _AllowDepletionRisk ) log->displayRawNL( "\tAllowDepletionRisk: %d", _AllowDepletionRisk ); } float GenRand = 1.0f; float GenFreq = 0.05f; NLMISC_COMMAND( setDepositRand, "Set CNoiseValue params for deposits", " " ) { if ( args.size() < 2 ) return false; GenRand = (float)atof( args[0].c_str() ); GenFreq = (float)atof( args[1].c_str() ); return true; } /* * */ class CIndexNoiseValue : public CNoiseValue { public: /// 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) {} /// Evaluate the value corresponding to the pos uint eval( const CVector& pos ) const { uint indexOfMax = 0; float maxNoise = 0; // 1 dephased noise per possible value, and take the max result (otherwise with '(CNoiseValue::eval( pos ) * _MaxIndex)', the result would be biased) for( uint i=0; i!=_MaxIndex; ++i ) { float f = CNoiseValue::eval( pos + _Phase + CVector(5245.346f*i, 785.67985f*i, 7842.783367f*i) ); if ( f > maxNoise ) { maxNoise = f; indexOfMax = i; } } return indexOfMax; } private: float _MaxIndex; CVector _Phase; }; /* * Compute freq according to deposit size (small deposits under 64x64 have a proportionally higher frequency) */ inline float getFreqFromDepositArea( float depositBBoxArea ) { if ( depositBBoxArea < SmallDepositAreaThreshold ) { const float gradient = (0.05f-0.3f)*2.0f/(SmallDepositAreaThreshold-SmallDepositAreaReference); return gradient*depositBBoxArea + (GenFreq - gradient*(SmallDepositAreaThreshold)); } else { return GenFreq; } } /* * Get a random RM from the neighbourhood of the specified position. * OptFastFloorBegin()/OptFastFloorEnd() must enclose one or more calls to this method. */ const CStaticDepositRawMaterial *CDeposit::getRandomRMAtPos( const NLMISC::CVector& pos, bool testIfSiteDepleted, bool& isDepleted ) { const float cellWidth = 1.0f; const uint16 nbNeighbours = 5; // center + 4 neighbour cells //nldebug( "Selecting RM in deposit" ); if ( getContents().empty() ) return NULL; // Test is local zone is depleted if ( testIfSiteDepleted ) { for ( CRecentForageSites::iterator ihs=_RecentForageSites.begin(); ihs!=_RecentForageSites.end(); ++ihs ) { if ( (*ihs).isDepleted() && (*ihs).contains( pos ) ) { //nldebug( "Harvest site depleted" ); isDepleted = true; return NULL; } } } // Compute freq according to deposit size float freq = getFreqFromDepositArea( getAreaOfAABox() ); // Get raw materials in neighbourhood uint neighbourhoud [nbNeighbours]; CVector cellPos = pos; CIndexNoiseValue noiseIndex( getContentSize(), _FilterPhase, freq ); // the phrase prevents to have the same map for two deposits with identical size but different seasons, etc. neighbourhoud[0] = noiseIndex.eval( cellPos ); cellPos.x -= cellWidth; cellPos.y -= cellWidth; neighbourhoud[1] = noiseIndex.eval( cellPos ); cellPos.x = pos.x + cellWidth; neighbourhoud[2] = noiseIndex.eval( cellPos ); cellPos.y = pos.y + cellWidth; neighbourhoud[3] = noiseIndex.eval( cellPos ); cellPos.x = pos.x - cellWidth; neighbourhoud[4] = noiseIndex.eval( cellPos ); //nldebug( "Indices: %u %u %u %u", neighbourhoud[0], neighbourhoud[1], neighbourhoud[2], neighbourhoud[3] ); // Select a random RM among neighbourhood uint rmIndex = neighbourhoud[RandomGenerator.rand( nbNeighbours-1 )]; return &(getContents()[rmIndex]); /* if( seasonParameters->AngryLevel >= _DepositContent[ infos.DepositIndexContent ]->CurrentQuantity ) { CMessage msgout("STATIC_STRING"); msgout.serial( const_cast< CEntityId& > ( charId ) ); // serial exclude set of empty exclude set set< CEntityId > empty; msgout.serialCont( empty ); string str("EGS_KAMI_ALERTE"); msgout.serial( str ); sendMessageViaMirror( "IOS", msgout ); } */ } /* * Get the RM at the specified position. * OptFastFloorBegin()/OptFastFloorEnd() must enclose one or more calls to this method. * \param testIfSiteDepleted Set it to false for map generation. */ const CStaticDepositRawMaterial *CDeposit::getRMAtPos( const NLMISC::CVector& pos, bool testIfSiteDepleted, bool& isDepleted ) { //const float cellWidth = 5.0f; //nldebug( "Selecting RM in deposit" ); if ( getContents().empty() ) return NULL; // Test is local zone is depleted if ( testIfSiteDepleted ) { for ( CRecentForageSites::iterator ihs=_RecentForageSites.begin(); ihs!=_RecentForageSites.end(); ++ihs ) { if ( (*ihs).isDepleted() && (*ihs).contains( pos ) ) { //nldebug( "Harvest site depleted" ); isDepleted = true; return NULL; } } } // Compute freq according to deposit size float freq = getFreqFromDepositArea( getAreaOfAABox() ); // Get raw material at position CIndexNoiseValue noiseIndex( getContentSize(), _FilterPhase, freq ); uint rmIndex = noiseIndex.eval( pos ); //nldebug( "Index: %u", rmIndex ); if ( rmIndex >= getContentSize() ) { nlwarning( "FG: CIndexNoiseValue returned a value out of range" ); rmIndex = getContentSize()-1; } return &(getContents()[rmIndex]); } /* * Return the current quantity. If 0 and the respawn time is elapsed, unlock. */ float CQuantityConstraints::getCurrentQuantity() { if ( CurrentQuantity == 0 ) { // Time to unlock? const CRyzomTime& ryzomTime = CTimeDateSeasonManager::getRyzomTimeReference(); if ( ryzomTime.getRyzomDay() < NextRespawnDay ) { // Still locked return 0; } else { // Unlock and retry CurrentQuantity = InitialQuantity; } } return CurrentQuantity; } /* * Consume * * Last possible consumption => lock deposit for RespawnTimeRyzomDays. * Does not unlock the deposit (see getCurrentQuantity() instead) * Argument by value. */ float CQuantityConstraints::consumeQuantity( float consumed ) { if ( CurrentQuantity > 0 ) { float newQuantity = CurrentQuantity - consumed; if ( newQuantity <= 0 ) { // Lock if the deposit is now empty (and wasn't before) const CRyzomTime& ryzomTime = CTimeDateSeasonManager::getRyzomTimeReference(); NextRespawnDay = ryzomTime.getRyzomDay() + (uint32)RespawnTimeRyzomDays; // Give only the remaining quantity consumed = CurrentQuantity; newQuantity = 0; } // Consume CurrentQuantity = newQuantity; return consumed; } else return 0; } /* * Display forage sites info */ void CDeposit::displayRecentForageSites( NLMISC::CLog& log ) const { if ( ! _RecentForageSites.empty() ) { log.displayNL( "Deposit %s:", _Name.c_str() ); for ( CRecentForageSites::const_iterator it=_RecentForageSites.begin(); it!=_RecentForageSites.end(); ++it ) { const CRecentForageSite& forageSite = (*it); forageSite.display( log ); } } } /* * Empty all forage sites */ void CDeposit::depleteAllRecentForageSites() { for ( CRecentForageSites::iterator it=_RecentForageSites.begin(); it!=_RecentForageSites.end(); ++it ) { CRecentForageSite& forageSite = (*it); forageSite.depleteAll(); } } /* * Increment the kami anger level (delta < 768). React if a threshold is reached. Return the kami anger level. */ float CDeposit::incKamiAnger( float delta, const std::vector& foragers ) { if ( _KamiAnger == -1.0f ) return -1.0f; if ( ForageKamiAngerOverride.get() != 0 ) _KamiAnger = ForageKamiAngerOverride.get(); else _KamiAnger += delta; if ( _KamiAnger > ForageKamiAngerThreshold1.get() ) { if ( _KamiAnger < ForageKamiAngerThreshold2.get() ) { // Only a warning (and small punishment ?) for ( vector::const_iterator itf=foragers.begin(); itf!=foragers.end(); ++itf ) { // Send a warning message const TDataSetRow& rowId = (*itf); TDataSetRow noRow; npcTellToPlayer( noRow, rowId, "FORAGE_KAMI_ANGER_WARNING", false ); } } else { // Full punishment if( HarvestAreaEffectOn ) { for ( vector::const_iterator itf=foragers.begin(); itf!=foragers.end(); ++itf ) { const TDataSetRow& rowId = (*itf); CEntityBase *entity = CEntityBaseManager::getEntityBasePtr( rowId ); if ( entity ) { // Send a warning message TDataSetRow noRow; npcTellToPlayer( noRow, rowId, "FORAGE_KAMI_ANGER_PUNISH", false ); // Set target list for FX (must be done before behaviour) CMirrorPropValueList targets( TheDataset, rowId, DSPropertyTARGET_LIST ); targets.clear(); targets.push_front( 0 ); // null distance (self) uint32 urowId = *((uint32*)&rowId); targets.push_front( urowId ); // Set behaviour for FX (must be done after target list) MBEHAV::CBehaviour behav( MBEHAV::CAST_OFF_SUCCESS ); behav.Spell.SpellMode = MAGICFX::Bomb; behav.Spell.SpellId = MAGICFX::Piercing;// + ForageKamiAngerPunishFX.get(); behav.Spell.SpellIntensity = 5; behav.Spell.Resist = 0; behav.Spell2.SelfSpell = 1; // 'self offensive cast' does not plays the cast anim, only impact & FX PHRASE_UTILITIES::sendUpdateBehaviour( rowId, behav ); // tmp nico : stats about projectiles projStatsIncrement(); // Damage CHarvestSource::hitEntity( RYZOMID::creature, entity, ForageKamiAngerPunishDamage.get(), ForageKamiAngerPunishDamage.get(), false ); //Bsi.append( StatPath, NLMISC::toString("[FKWP] %s '%s' %.1f", entity->getId().toString().c_str(), name().c_str(), _KamiAnger) ); //EgsStat.displayNL("[FKWP] %s '%s' %.1f", entity->getId().toString().c_str(), name().c_str(), _KamiAnger); // EGSPD::forageKamiWrathPunishment(entity->getId(), name(), _KamiAnger); } } } // Don't reset the kami anger level to the min value, but nearly to the threshold 2 value! _KamiAnger = ForageKamiAngerThreshold2.get() - 1.0f; // +2 will reach the threshold 2 again } } return _KamiAnger; } //NLMISC_VARIABLE( bool, VerboseDeposits, "Verbose info for deposits" ); NLMISC_COMMAND( forageDisplayRecentForageSitesNb, "Display the number of recent forage sites", "" ) { log.displayNL( "%u forage sites", TotalNbRecentForageSites ); return true; } NLMISC_COMMAND( forageDisplayRecentForageSitesInfo, "Display the info on all recent forage sites", "" ) { const std::vector& deposits = CZoneManager::getInstance().getDeposits(); std::vector::const_iterator it; for ( it=deposits.begin(); it!=deposits.end(); ++it ) { CDeposit *deposit = (*it); deposit->displayRecentForageSites( log ); } return true; } NLMISC_COMMAND( forageEmptyAllRecentForageSites, "Empty all forage sites", "" ) { const std::vector& deposits = CZoneManager::getInstance().getDeposits(); std::vector::const_iterator it; for ( it=deposits.begin(); it!=deposits.end(); ++it ) { CDeposit *deposit = (*it); deposit->depleteAllRecentForageSites(); } return true; } NLMISC_COMMAND( forageDisplayDeposit, "Display info about one or all the deposits", " " ) { string depName = "ALL"; bool extendedInfo = false; if ( args.size() > 0 ) { depName = args[0]; if ( args.size() > 1 ) extendedInfo = (args[1] == "1"); } CZoneManager::getInstance().dumpDeposits( log, depName, extendedInfo ); return true; } struct TCompareDepositsByHighestKamiAnger : public std::binary_function { bool operator() ( const CDeposit* d1, const CDeposit* d2 ) { return d1->kamiAnger() > d2->kamiAnger(); } }; NLMISC_COMMAND( forageDisplayKamiAngerLevels, "Display the N deposits with the highest anger levels", "" ) { uint nb = 10; if ( args.size() > 0 ) nb = atoi( args[0].c_str() ); std::vector newDepositList = CZoneManager::getInstance().getDeposits(); std::sort( newDepositList.begin(), newDepositList.end(), TCompareDepositsByHighestKamiAnger() ); log.displayNL( "%u first deposits by kami anger:", nb ); for ( uint i=0; i!=nb && ikamiAnger(), newDepositList[i]->name().c_str() ); } return true; } NLMISC_VARIABLE( bool, VerboseDepositFiltering, "Verbose the items parsed when filtering deposits" ); NLMISC_VARIABLE( bool, ExportDepositContents, "When loading the deposit primitives, export contents report" );