2022-08-09 09:54:32 +04:00
/*
* TreasurePlacer . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
# include "StdInc.h"
# include "TreasurePlacer.h"
2024-08-24 15:12:22 +02:00
# include "../CRmgTemplate.h"
2023-05-20 10:17:37 +02:00
# include "../CMapGenerator.h"
# include "../Functions.h"
2022-08-09 09:54:32 +04:00
# include "ObjectManager.h"
# include "RoadPlacer.h"
# include "ConnectionsPlacer.h"
2023-05-20 10:17:37 +02:00
# include "../RmgMap.h"
# include "../TileInfo.h"
# include "../CZonePlacer.h"
2023-12-11 07:37:23 +01:00
# include "PrisonHeroPlacer.h"
2023-04-23 10:08:16 +02:00
# include "QuestArtifactPlacer.h"
2023-06-05 18:53:17 +03:00
# include "../../ArtifactUtils.h"
2023-06-02 21:47:37 +03:00
# include "../../mapObjectConstructors/AObjectTypeHandler.h"
# include "../../mapObjectConstructors/CObjectClassesHandler.h"
2023-06-08 17:29:29 +03:00
# include "../../mapObjectConstructors/DwellingInstanceConstructor.h"
2024-08-24 20:18:36 +02:00
# include "../../rewardable/Info.h"
2023-06-08 00:42:47 +03:00
# include "../../mapObjects/CGHeroInstance.h"
2023-06-02 21:47:37 +03:00
# include "../../mapObjects/CGPandoraBox.h"
2024-01-09 16:43:36 +02:00
# include "../../mapObjects/CQuest.h"
# include "../../mapObjects/MiscObjects.h"
2023-05-20 10:17:37 +02:00
# include "../../CCreatureHandler.h"
# include "../../spells/CSpellHandler.h" //for choosing random spells
# include "../../mapping/CMap.h"
# include "../../mapping/CMapEditManager.h"
2022-08-09 09:54:32 +04:00
2024-06-01 15:28:17 +00:00
# include <vstd/RNG.h>
2022-07-26 16:07:42 +03:00
VCMI_LIB_NAMESPACE_BEGIN
2022-08-09 09:54:32 +04:00
void TreasurePlacer : : process ( )
{
2024-08-21 20:16:41 +02:00
if ( zone . getMaxTreasureValue ( ) = = 0 )
{
//No treasures at all
return ;
}
2024-09-14 09:23:28 +02:00
tierValues = generator . getConfig ( ) . pandoraCreatureValues ;
// Add all native creatures
for ( auto const & cre : VLC - > creh - > objects )
{
2024-10-05 19:37:52 +00:00
if ( ! cre - > special & & cre - > getFactionID ( ) = = zone . getTownType ( ) )
2024-09-14 09:23:28 +02:00
{
creatures . push_back ( cre . get ( ) ) ;
}
}
2024-08-21 20:16:41 +02:00
// Get default objects
2022-08-09 09:54:32 +04:00
addAllPossibleObjects ( ) ;
2024-08-21 20:16:41 +02:00
// Override with custom objects
2024-09-12 21:04:27 +02:00
objects . patchWithZoneConfig ( zone , this ) ;
2024-08-21 20:16:41 +02:00
2022-08-09 09:54:32 +04:00
auto * m = zone . getModificator < ObjectManager > ( ) ;
if ( m )
createTreasures ( * m ) ;
}
void TreasurePlacer : : init ( )
{
2023-06-07 15:08:50 +02:00
maxPrisons = 0 ; //Should be in the constructor, but we use macro for that
2022-08-09 09:54:32 +04:00
DEPENDENCY ( ObjectManager ) ;
DEPENDENCY ( ConnectionsPlacer ) ;
2023-12-11 07:37:23 +01:00
DEPENDENCY_ALL ( PrisonHeroPlacer ) ;
2024-05-01 07:58:17 +02:00
DEPENDENCY ( RoadPlacer ) ;
2022-08-09 09:54:32 +04:00
}
2023-03-27 09:09:58 +02:00
void TreasurePlacer : : addObjectToRandomPool ( const ObjectInfo & oi )
{
2024-08-21 20:16:41 +02:00
if ( oi . templates . empty ( ) )
{
logGlobal - > error ( " Attempt to add ObjectInfo with no templates! Value: %d " , oi . value ) ;
return ;
}
if ( ! oi . generateObject )
{
logGlobal - > error ( " Attempt to add ObjectInfo with no generateObject function! Value: %d " , oi . value ) ;
return ;
}
if ( ! oi . maxPerZone )
{
logGlobal - > warn ( " Attempt to add ObjectInfo with 0 maxPerZone! Value: %d " , oi . value ) ;
return ;
}
2023-05-19 20:30:15 +02:00
RecursiveLock lock ( externalAccessMutex ) ;
2024-08-21 20:16:41 +02:00
objects . addObject ( oi ) ;
2023-03-27 09:09:58 +02:00
}
2022-08-09 09:54:32 +04:00
void TreasurePlacer : : addAllPossibleObjects ( )
2024-08-21 20:16:41 +02:00
{
addCommonObjects ( ) ;
addDwellings ( ) ;
addPandoraBoxes ( ) ;
addSeerHuts ( ) ;
addPrisons ( ) ;
addScrolls ( ) ;
}
void TreasurePlacer : : addCommonObjects ( )
2022-08-09 09:54:32 +04:00
{
for ( auto primaryID : VLC - > objtypeh - > knownObjects ( ) )
{
for ( auto secondaryID : VLC - > objtypeh - > knownSubObjects ( primaryID ) )
{
auto handler = VLC - > objtypeh - > getHandlerFor ( primaryID , secondaryID ) ;
if ( ! handler - > isStaticObject ( ) & & handler - > getRMGInfo ( ) . value )
{
2023-03-27 09:09:58 +02:00
auto rmgInfo = handler - > getRMGInfo ( ) ;
2023-03-27 17:29:46 +02:00
if ( rmgInfo . mapLimit | | rmgInfo . value > zone . getMaxTreasureValue ( ) )
2023-03-27 09:09:58 +02:00
{
//Skip objects with per-map limit here
continue ;
}
2024-09-14 09:23:28 +02:00
ObjectInfo oi ( primaryID , secondaryID ) ;
2024-09-12 21:04:27 +02:00
setBasicProperties ( oi , CompoundMapObjectID ( primaryID , secondaryID ) ) ;
2023-03-27 09:09:58 +02:00
2023-03-28 17:53:08 +02:00
oi . value = rmgInfo . value ;
oi . probability = rmgInfo . rarity ;
oi . maxPerZone = rmgInfo . zoneLimit ;
2024-09-14 09:23:28 +02:00
2023-09-30 23:06:38 +02:00
if ( ! oi . templates . empty ( ) )
addObjectToRandomPool ( oi ) ;
2022-08-09 09:54:32 +04:00
}
}
}
2024-08-21 20:16:41 +02:00
}
2023-06-07 13:00:24 +02:00
2024-09-12 21:04:27 +02:00
void TreasurePlacer : : setBasicProperties ( ObjectInfo & oi , CompoundMapObjectID objid ) const
{
oi . generateObject = [ this , objid ] ( ) - > CGObjectInstance *
{
return VLC - > objtypeh - > getHandlerFor ( objid ) - > create ( map . mapInstance - > cb , nullptr ) ;
} ;
oi . setTemplates ( objid . primaryID , objid . secondaryID , zone . getTerrainType ( ) ) ;
}
2024-08-21 20:16:41 +02:00
void TreasurePlacer : : addPrisons ( )
{
2023-06-07 13:00:24 +02:00
//Generate Prison on water only if it has a template
auto prisonTemplates = VLC - > objtypeh - > getHandlerFor ( Obj : : PRISON , 0 ) - > getTemplates ( zone . getTerrainType ( ) ) ;
if ( ! prisonTemplates . empty ( ) )
2022-08-09 09:54:32 +04:00
{
2023-12-11 07:37:23 +01:00
PrisonHeroPlacer * prisonHeroPlacer = nullptr ;
for ( auto & z : map . getZones ( ) )
{
2023-12-11 08:27:57 +01:00
prisonHeroPlacer = z . second - > getModificator < PrisonHeroPlacer > ( ) ;
if ( prisonHeroPlacer )
2023-12-11 07:37:23 +01:00
{
break ;
}
}
2023-06-07 13:00:24 +02:00
//prisons
//levels 1, 5, 10, 20, 30
2024-01-09 16:43:36 +02:00
static const int prisonsLevels = std : : min ( generator . getConfig ( ) . prisonExperience . size ( ) , generator . getConfig ( ) . prisonValues . size ( ) ) ;
2023-05-05 10:30:36 +02:00
2023-06-07 13:00:24 +02:00
size_t prisonsLeft = getMaxPrisons ( ) ;
for ( int i = prisonsLevels - 1 ; i > = 0 ; i - - )
2022-08-09 09:54:32 +04:00
{
2024-09-14 09:23:28 +02:00
ObjectInfo oi ( Obj : : PRISON , 0 ) ; // Create new instance which will hold destructor operation
2023-12-24 09:36:26 +01:00
2023-06-07 13:00:24 +02:00
oi . value = generator . getConfig ( ) . prisonValues [ i ] ;
if ( oi . value > zone . getMaxTreasureValue ( ) )
{
continue ;
}
2024-01-19 16:17:59 +02:00
oi . generateObject = [ i , this , prisonHeroPlacer ] ( ) - > CGObjectInstance *
2023-06-07 13:00:24 +02:00
{
2023-12-11 07:37:23 +01:00
HeroTypeID hid = prisonHeroPlacer - > drawRandomHero ( ) ;
2023-06-07 13:00:24 +02:00
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : PRISON , 0 ) ;
2024-01-01 16:37:48 +02:00
auto * obj = dynamic_cast < CGHeroInstance * > ( factory - > create ( map . mapInstance - > cb , nullptr ) ) ;
2023-06-07 13:00:24 +02:00
2023-10-28 12:27:10 +03:00
obj - > setHeroType ( hid ) ; //will be initialized later
2023-06-07 13:00:24 +02:00
obj - > exp = generator . getConfig ( ) . prisonExperience [ i ] ;
obj - > setOwner ( PlayerColor : : NEUTRAL ) ;
return obj ;
} ;
2024-01-19 16:17:59 +02:00
oi . destroyObject = [ prisonHeroPlacer ] ( CGObjectInstance * obj )
{
// Hero can be used again
auto * hero = dynamic_cast < CGHeroInstance * > ( obj ) ;
2024-10-05 19:37:52 +00:00
prisonHeroPlacer - > restoreDrawnHero ( hero - > getHeroTypeID ( ) ) ;
2024-01-19 16:17:59 +02:00
} ;
2023-09-30 23:06:38 +02:00
oi . setTemplates ( Obj : : PRISON , 0 , zone . getTerrainType ( ) ) ;
2023-06-07 13:00:24 +02:00
oi . value = generator . getConfig ( ) . prisonValues [ i ] ;
oi . probability = 30 ;
//Distribute all allowed prisons, starting from the most valuable
oi . maxPerZone = ( std : : ceil ( ( float ) prisonsLeft / ( i + 1 ) ) ) ;
prisonsLeft - = oi . maxPerZone ;
2023-09-30 23:06:38 +02:00
if ( ! oi . templates . empty ( ) )
addObjectToRandomPool ( oi ) ;
2023-06-07 13:00:24 +02:00
}
2022-08-09 09:54:32 +04:00
}
2024-08-21 20:16:41 +02:00
}
2023-06-07 13:00:24 +02:00
2024-08-21 20:16:41 +02:00
void TreasurePlacer : : addDwellings ( )
{
2023-06-07 13:00:24 +02:00
if ( zone . getType ( ) = = ETemplateZoneType : : WATER )
return ;
2024-09-14 09:23:28 +02:00
2022-08-09 09:54:32 +04:00
//dwellings
auto dwellingTypes = { Obj : : CREATURE_GENERATOR1 , Obj : : CREATURE_GENERATOR4 } ;
for ( auto dwellingType : dwellingTypes )
{
auto subObjects = VLC - > objtypeh - > knownSubObjects ( dwellingType ) ;
if ( dwellingType = = Obj : : CREATURE_GENERATOR1 )
{
//don't spawn original "neutral" dwellings that got replaced by Conflux dwellings in AB
2024-01-09 16:43:36 +02:00
static const MapObjectSubID elementalConfluxROE [ ] = { 7 , 13 , 16 , 47 } ;
2023-11-05 19:13:18 +02:00
for ( auto const & i : elementalConfluxROE )
2023-02-11 19:05:02 +03:00
vstd : : erase_if_present ( subObjects , i ) ;
2022-08-09 09:54:32 +04:00
}
for ( auto secondaryID : subObjects )
{
2023-06-08 17:29:29 +03:00
const auto * dwellingHandler = dynamic_cast < const DwellingInstanceConstructor * > ( VLC - > objtypeh - > getHandlerFor ( dwellingType , secondaryID ) . get ( ) ) ;
2022-08-09 09:54:32 +04:00
auto creatures = dwellingHandler - > getProducedCreatures ( ) ;
if ( creatures . empty ( ) )
continue ;
2023-02-11 19:05:02 +03:00
const auto * cre = creatures . front ( ) ;
2024-10-05 19:37:52 +00:00
if ( cre - > getFactionID ( ) = = zone . getTownType ( ) )
2022-08-09 09:54:32 +04:00
{
2024-10-05 19:37:52 +00:00
auto nativeZonesCount = static_cast < float > ( map . getZoneCount ( cre - > getFactionID ( ) ) ) ;
2024-09-14 09:23:28 +02:00
ObjectInfo oi ( dwellingType , secondaryID ) ;
setBasicProperties ( oi , CompoundMapObjectID ( dwellingType , secondaryID ) ) ;
2023-04-05 03:26:29 +03:00
oi . value = static_cast < ui32 > ( cre - > getAIValue ( ) * cre - > getGrowth ( ) * ( 1 + ( nativeZonesCount / map . getTotalZoneCount ( ) ) + ( nativeZonesCount / 2 ) ) ) ;
2022-08-09 09:54:32 +04:00
oi . probability = 40 ;
2023-09-30 23:06:38 +02:00
2024-01-01 16:37:48 +02:00
oi . generateObject = [ this , secondaryID , dwellingType ] ( ) - > CGObjectInstance *
2022-08-09 09:54:32 +04:00
{
2024-01-01 16:37:48 +02:00
auto * obj = VLC - > objtypeh - > getHandlerFor ( dwellingType , secondaryID ) - > create ( map . mapInstance - > cb , nullptr ) ;
2023-09-30 23:06:38 +02:00
obj - > tempOwner = PlayerColor : : NEUTRAL ;
return obj ;
} ;
if ( ! oi . templates . empty ( ) )
addObjectToRandomPool ( oi ) ;
2022-08-09 09:54:32 +04:00
}
}
}
2024-08-21 20:16:41 +02:00
}
void TreasurePlacer : : addScrolls ( )
{
if ( zone . getType ( ) = = ETemplateZoneType : : WATER )
return ;
2024-09-14 09:23:28 +02:00
ObjectInfo oi ( Obj : : SPELL_SCROLL , 0 ) ;
2024-08-21 20:16:41 +02:00
2022-08-09 09:54:32 +04:00
for ( int i = 0 ; i < generator . getConfig ( ) . scrollValues . size ( ) ; i + + )
{
oi . generateObject = [ i , this ] ( ) - > CGObjectInstance *
{
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : SPELL_SCROLL , 0 ) ;
2024-01-01 16:37:48 +02:00
auto * obj = dynamic_cast < CGArtifact * > ( factory - > create ( map . mapInstance - > cb , nullptr ) ) ;
2022-08-09 09:54:32 +04:00
std : : vector < SpellID > out ;
2024-05-16 22:05:51 +00:00
for ( auto spellID : VLC - > spellh - > getDefaultAllowed ( ) )
2022-08-09 09:54:32 +04:00
{
2024-05-16 22:05:51 +00:00
if ( map . isAllowedSpell ( spellID ) & & spellID . toSpell ( ) - > getLevel ( ) = = i + 1 )
out . push_back ( spellID ) ;
2022-08-09 09:54:32 +04:00
}
2023-06-05 18:53:17 +03:00
auto * a = ArtifactUtils : : createScroll ( * RandomGeneratorUtil : : nextItem ( out , zone . getRand ( ) ) ) ;
2022-08-09 09:54:32 +04:00
obj - > storedArtifact = a ;
return obj ;
} ;
2023-09-30 23:06:38 +02:00
oi . setTemplates ( Obj : : SPELL_SCROLL , 0 , zone . getTerrainType ( ) ) ;
2022-08-09 09:54:32 +04:00
oi . value = generator . getConfig ( ) . scrollValues [ i ] ;
oi . probability = 30 ;
2023-09-30 23:06:38 +02:00
if ( ! oi . templates . empty ( ) )
addObjectToRandomPool ( oi ) ;
2022-08-09 09:54:32 +04:00
}
2024-08-21 20:16:41 +02:00
}
void TreasurePlacer : : addPandoraBoxes ( )
{
if ( zone . getType ( ) = = ETemplateZoneType : : WATER )
return ;
addPandoraBoxesWithGold ( ) ;
addPandoraBoxesWithExperience ( ) ;
addPandoraBoxesWithCreatures ( ) ;
addPandoraBoxesWithSpells ( ) ;
}
void TreasurePlacer : : addPandoraBoxesWithGold ( )
{
2024-09-14 09:23:28 +02:00
ObjectInfo oi ( Obj : : PANDORAS_BOX , 0 ) ;
2022-08-09 09:54:32 +04:00
for ( int i = 1 ; i < 5 ; i + + )
{
2024-01-01 16:37:48 +02:00
oi . generateObject = [ this , i ] ( ) - > CGObjectInstance *
2022-08-09 09:54:32 +04:00
{
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : PANDORAS_BOX , 0 ) ;
2024-01-01 16:37:48 +02:00
auto * obj = dynamic_cast < CGPandoraBox * > ( factory - > create ( map . mapInstance - > cb , nullptr ) ) ;
2023-09-15 15:29:41 +02:00
Rewardable : : VisitInfo reward ;
reward . reward . resources [ EGameResID : : GOLD ] = i * 5000 ;
reward . visitType = Rewardable : : EEventType : : EVENT_FIRST_VISIT ;
obj - > configuration . info . push_back ( reward ) ;
2022-08-09 09:54:32 +04:00
return obj ;
} ;
2023-09-30 23:06:38 +02:00
oi . setTemplates ( Obj : : PANDORAS_BOX , 0 , zone . getTerrainType ( ) ) ;
2022-08-09 09:54:32 +04:00
oi . value = i * generator . getConfig ( ) . pandoraMultiplierGold ;
oi . probability = 5 ;
2023-09-30 23:06:38 +02:00
if ( ! oi . templates . empty ( ) )
addObjectToRandomPool ( oi ) ;
2022-08-09 09:54:32 +04:00
}
2024-08-21 20:16:41 +02:00
}
void TreasurePlacer : : addPandoraBoxesWithExperience ( )
{
2024-09-14 09:23:28 +02:00
ObjectInfo oi ( Obj : : PANDORAS_BOX , 0 ) ;
2022-08-09 09:54:32 +04:00
for ( int i = 1 ; i < 5 ; i + + )
{
2024-01-01 16:37:48 +02:00
oi . generateObject = [ this , i ] ( ) - > CGObjectInstance *
2022-08-09 09:54:32 +04:00
{
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : PANDORAS_BOX , 0 ) ;
2024-01-01 16:37:48 +02:00
auto * obj = dynamic_cast < CGPandoraBox * > ( factory - > create ( map . mapInstance - > cb , nullptr ) ) ;
2023-09-15 15:29:41 +02:00
Rewardable : : VisitInfo reward ;
reward . reward . heroExperience = i * 5000 ;
reward . visitType = Rewardable : : EEventType : : EVENT_FIRST_VISIT ;
obj - > configuration . info . push_back ( reward ) ;
2022-08-09 09:54:32 +04:00
return obj ;
} ;
2023-09-30 23:06:38 +02:00
oi . setTemplates ( Obj : : PANDORAS_BOX , 0 , zone . getTerrainType ( ) ) ;
2022-08-09 09:54:32 +04:00
oi . value = i * generator . getConfig ( ) . pandoraMultiplierExperience ;
oi . probability = 20 ;
2023-09-30 23:06:38 +02:00
if ( ! oi . templates . empty ( ) )
addObjectToRandomPool ( oi ) ;
2022-08-09 09:54:32 +04:00
}
2024-08-21 20:16:41 +02:00
}
2023-04-07 21:23:34 +02:00
2024-08-21 20:16:41 +02:00
void TreasurePlacer : : addPandoraBoxesWithCreatures ( )
{
2023-02-11 19:05:02 +03:00
for ( auto * creature : creatures )
2022-08-09 09:54:32 +04:00
{
int creaturesAmount = creatureToCount ( creature ) ;
if ( ! creaturesAmount )
continue ;
2024-09-14 10:19:22 +02:00
ObjectInfo oi ( Obj : : PANDORAS_BOX , 0 ) ;
2022-08-09 09:54:32 +04:00
2024-01-01 16:37:48 +02:00
oi . generateObject = [ this , creature , creaturesAmount ] ( ) - > CGObjectInstance *
2022-08-09 09:54:32 +04:00
{
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : PANDORAS_BOX , 0 ) ;
2024-01-01 16:37:48 +02:00
auto * obj = dynamic_cast < CGPandoraBox * > ( factory - > create ( map . mapInstance - > cb , nullptr ) ) ;
2023-09-15 15:29:41 +02:00
Rewardable : : VisitInfo reward ;
reward . reward . creatures . emplace_back ( creature , creaturesAmount ) ;
reward . visitType = Rewardable : : EEventType : : EVENT_FIRST_VISIT ;
obj - > configuration . info . push_back ( reward ) ;
2022-08-09 09:54:32 +04:00
return obj ;
} ;
2023-09-30 23:06:38 +02:00
oi . setTemplates ( Obj : : PANDORAS_BOX , 0 , zone . getTerrainType ( ) ) ;
2024-10-05 19:37:52 +00:00
oi . value = static_cast < ui32 > ( creature - > getAIValue ( ) * creaturesAmount * ( 1 + static_cast < float > ( map . getZoneCount ( creature - > getFactionID ( ) ) ) / map . getTotalZoneCount ( ) ) ) ;
2022-08-09 09:54:32 +04:00
oi . probability = 3 ;
2023-09-30 23:06:38 +02:00
if ( ! oi . templates . empty ( ) )
addObjectToRandomPool ( oi ) ;
2022-08-09 09:54:32 +04:00
}
2024-08-21 20:16:41 +02:00
}
void TreasurePlacer : : addPandoraBoxesWithSpells ( )
{
2024-09-14 09:23:28 +02:00
ObjectInfo oi ( Obj : : PANDORAS_BOX , 0 ) ;
2022-08-09 09:54:32 +04:00
//Pandora with 12 spells of certain level
for ( int i = 1 ; i < = GameConstants : : SPELL_LEVELS ; i + + )
{
oi . generateObject = [ i , this ] ( ) - > CGObjectInstance *
{
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : PANDORAS_BOX , 0 ) ;
2024-01-01 16:37:48 +02:00
auto * obj = dynamic_cast < CGPandoraBox * > ( factory - > create ( map . mapInstance - > cb , nullptr ) ) ;
2023-02-11 19:05:02 +03:00
2023-12-31 23:43:35 +02:00
std : : vector < const CSpell * > spells ;
2024-05-16 22:05:51 +00:00
for ( auto spellID : VLC - > spellh - > getDefaultAllowed ( ) )
2022-08-09 09:54:32 +04:00
{
2024-05-16 22:05:51 +00:00
if ( map . isAllowedSpell ( spellID ) & & spellID . toSpell ( ) - > getLevel ( ) = = i )
spells . push_back ( spellID . toSpell ( ) ) ;
2022-08-09 09:54:32 +04:00
}
2023-05-20 11:46:32 +02:00
RandomGeneratorUtil : : randomShuffle ( spells , zone . getRand ( ) ) ;
2023-09-15 15:29:41 +02:00
Rewardable : : VisitInfo reward ;
2023-02-11 19:05:02 +03:00
for ( int j = 0 ; j < std : : min ( 12 , static_cast < int > ( spells . size ( ) ) ) ; j + + )
2022-08-09 09:54:32 +04:00
{
2023-09-15 15:29:41 +02:00
reward . reward . spells . push_back ( spells [ j ] - > id ) ;
2022-08-09 09:54:32 +04:00
}
2023-09-15 15:29:41 +02:00
reward . visitType = Rewardable : : EEventType : : EVENT_FIRST_VISIT ;
obj - > configuration . info . push_back ( reward ) ;
2022-08-09 09:54:32 +04:00
return obj ;
} ;
2023-09-30 23:06:38 +02:00
oi . setTemplates ( Obj : : PANDORAS_BOX , 0 , zone . getTerrainType ( ) ) ;
2022-08-09 09:54:32 +04:00
oi . value = ( i + 1 ) * generator . getConfig ( ) . pandoraMultiplierSpells ; //5000 - 15000
oi . probability = 2 ;
2023-09-30 23:06:38 +02:00
if ( ! oi . templates . empty ( ) )
addObjectToRandomPool ( oi ) ;
2022-08-09 09:54:32 +04:00
}
//Pandora with 15 spells of certain school
for ( int i = 0 ; i < 4 ; i + + )
{
oi . generateObject = [ i , this ] ( ) - > CGObjectInstance *
{
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : PANDORAS_BOX , 0 ) ;
2024-01-01 16:37:48 +02:00
auto * obj = dynamic_cast < CGPandoraBox * > ( factory - > create ( map . mapInstance - > cb , nullptr ) ) ;
2023-02-11 19:05:02 +03:00
2023-12-31 23:43:35 +02:00
std : : vector < const CSpell * > spells ;
2024-05-16 22:05:51 +00:00
for ( auto spellID : VLC - > spellh - > getDefaultAllowed ( ) )
2022-08-09 09:54:32 +04:00
{
2024-05-16 22:05:51 +00:00
if ( map . isAllowedSpell ( spellID ) & & spellID . toSpell ( ) - > hasSchool ( SpellSchool ( i ) ) )
spells . push_back ( spellID . toSpell ( ) ) ;
2022-08-09 09:54:32 +04:00
}
2023-05-20 11:46:32 +02:00
RandomGeneratorUtil : : randomShuffle ( spells , zone . getRand ( ) ) ;
2023-09-15 15:29:41 +02:00
Rewardable : : VisitInfo reward ;
2023-02-11 19:05:02 +03:00
for ( int j = 0 ; j < std : : min ( 15 , static_cast < int > ( spells . size ( ) ) ) ; j + + )
2022-08-09 09:54:32 +04:00
{
2023-09-15 15:29:41 +02:00
reward . reward . spells . push_back ( spells [ j ] - > id ) ;
2022-08-09 09:54:32 +04:00
}
2023-09-15 15:29:41 +02:00
reward . visitType = Rewardable : : EEventType : : EVENT_FIRST_VISIT ;
obj - > configuration . info . push_back ( reward ) ;
2022-08-09 09:54:32 +04:00
return obj ;
} ;
2023-09-30 23:06:38 +02:00
oi . setTemplates ( Obj : : PANDORAS_BOX , 0 , zone . getTerrainType ( ) ) ;
2022-08-09 09:54:32 +04:00
oi . value = generator . getConfig ( ) . pandoraSpellSchool ;
oi . probability = 2 ;
2023-09-30 23:06:38 +02:00
if ( ! oi . templates . empty ( ) )
addObjectToRandomPool ( oi ) ;
2022-08-09 09:54:32 +04:00
}
// Pandora box with 60 random spells
oi . generateObject = [ this ] ( ) - > CGObjectInstance *
{
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : PANDORAS_BOX , 0 ) ;
2024-01-01 16:37:48 +02:00
auto * obj = dynamic_cast < CGPandoraBox * > ( factory - > create ( map . mapInstance - > cb , nullptr ) ) ;
2023-02-11 19:05:02 +03:00
2023-12-31 23:43:35 +02:00
std : : vector < const CSpell * > spells ;
2024-05-16 22:05:51 +00:00
for ( auto spellID : VLC - > spellh - > getDefaultAllowed ( ) )
2022-08-09 09:54:32 +04:00
{
2024-05-16 22:05:51 +00:00
if ( map . isAllowedSpell ( spellID ) )
spells . push_back ( spellID . toSpell ( ) ) ;
2022-08-09 09:54:32 +04:00
}
2023-05-20 11:46:32 +02:00
RandomGeneratorUtil : : randomShuffle ( spells , zone . getRand ( ) ) ;
2023-09-15 15:29:41 +02:00
Rewardable : : VisitInfo reward ;
2023-02-11 19:05:02 +03:00
for ( int j = 0 ; j < std : : min ( 60 , static_cast < int > ( spells . size ( ) ) ) ; j + + )
2022-08-09 09:54:32 +04:00
{
2023-09-15 15:29:41 +02:00
reward . reward . spells . push_back ( spells [ j ] - > id ) ;
2022-08-09 09:54:32 +04:00
}
2023-09-15 15:29:41 +02:00
reward . visitType = Rewardable : : EEventType : : EVENT_FIRST_VISIT ;
obj - > configuration . info . push_back ( reward ) ;
2022-08-09 09:54:32 +04:00
return obj ;
} ;
2023-09-30 23:06:38 +02:00
oi . setTemplates ( Obj : : PANDORAS_BOX , 0 , zone . getTerrainType ( ) ) ;
2022-08-09 09:54:32 +04:00
oi . value = generator . getConfig ( ) . pandoraSpell60 ;
oi . probability = 2 ;
2023-09-30 23:06:38 +02:00
if ( ! oi . templates . empty ( ) )
addObjectToRandomPool ( oi ) ;
2024-08-21 20:16:41 +02:00
}
void TreasurePlacer : : addSeerHuts ( )
{
2023-04-29 11:46:03 +02:00
//Seer huts with creatures or generic rewards
2023-04-23 10:08:16 +02:00
2024-09-14 09:23:28 +02:00
ObjectInfo oi ( Obj : : SEER_HUT , 0 ) ;
2024-08-21 20:16:41 +02:00
2023-06-17 19:09:38 +02:00
if ( zone . getConnectedZoneIds ( ) . size ( ) ) //Unlikely, but...
2022-08-09 09:54:32 +04:00
{
2023-04-29 11:46:03 +02:00
auto * qap = zone . getModificator < QuestArtifactPlacer > ( ) ;
if ( ! qap )
2022-08-09 09:54:32 +04:00
{
2023-04-29 11:46:03 +02:00
return ; //TODO: throw?
2022-08-09 09:54:32 +04:00
}
2023-04-29 11:46:03 +02:00
const int questArtsRemaining = qap - > getMaxQuestArtifactCount ( ) ;
2023-06-14 20:50:14 +02:00
if ( ! questArtsRemaining )
{
return ;
}
2023-04-29 11:46:03 +02:00
//Generate Seer Hut one by one. Duplicated oi possible and should work fine.
oi . maxPerZone = 1 ;
2023-02-11 19:05:02 +03:00
2023-04-29 11:46:03 +02:00
std : : vector < ObjectInfo > possibleSeerHuts ;
//14 creatures per town + 4 for each of gold / exp reward
possibleSeerHuts . reserve ( 14 + 4 + 4 ) ;
2023-05-20 11:46:32 +02:00
RandomGeneratorUtil : : randomShuffle ( creatures , zone . getRand ( ) ) ;
2023-02-11 19:05:02 +03:00
2024-01-19 16:17:59 +02:00
auto setRandomArtifact = [ qap ] ( CGSeerHut * obj )
2024-01-17 07:18:14 +01:00
{
ArtifactID artid = qap - > drawRandomArtifact ( ) ;
obj - > quest - > mission . artifacts . push_back ( artid ) ;
qap - > addQuestArtifact ( artid ) ;
} ;
2024-01-19 16:17:59 +02:00
auto destroyObject = [ qap ] ( CGObjectInstance * obj )
{
auto * seer = dynamic_cast < CGSeerHut * > ( obj ) ;
// Artifact can be used again
ArtifactID artid = seer - > quest - > mission . artifacts . front ( ) ;
qap - > addRandomArtifact ( artid ) ;
qap - > removeQuestArtifact ( artid ) ;
} ;
2024-01-17 07:18:14 +01:00
2023-04-29 11:46:03 +02:00
for ( int i = 0 ; i < static_cast < int > ( creatures . size ( ) ) ; i + + )
2022-08-09 09:54:32 +04:00
{
2023-02-11 19:05:02 +03:00
auto * creature = creatures [ i ] ;
2022-08-09 09:54:32 +04:00
int creaturesAmount = creatureToCount ( creature ) ;
if ( ! creaturesAmount )
continue ;
2023-05-20 11:46:32 +02:00
int randomAppearance = chooseRandomAppearance ( zone . getRand ( ) , Obj : : SEER_HUT , zone . getTerrainType ( ) ) ;
2022-08-09 09:54:32 +04:00
2024-01-16 17:15:35 +01:00
// FIXME: Remove duplicated code for gold, exp and creaure reward
2024-01-16 20:47:09 +02:00
oi . generateObject = [ cb = map . mapInstance - > cb , creature , creaturesAmount , randomAppearance , setRandomArtifact ] ( ) - > CGObjectInstance *
2022-08-09 09:54:32 +04:00
{
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : SEER_HUT , randomAppearance ) ;
2024-01-16 20:47:09 +02:00
auto * obj = dynamic_cast < CGSeerHut * > ( factory - > create ( cb , nullptr ) ) ;
2023-09-13 01:40:07 +02:00
2023-09-15 15:29:41 +02:00
Rewardable : : VisitInfo reward ;
reward . reward . creatures . emplace_back ( creature - > getId ( ) , creaturesAmount ) ;
reward . visitType = Rewardable : : EEventType : : EVENT_FIRST_VISIT ;
obj - > configuration . info . push_back ( reward ) ;
2023-10-09 19:15:34 +02:00
2024-01-17 07:18:14 +01:00
setRandomArtifact ( obj ) ;
2022-08-09 09:54:32 +04:00
return obj ;
} ;
2024-01-19 16:17:59 +02:00
oi . destroyObject = destroyObject ;
2023-04-29 11:46:03 +02:00
oi . probability = 3 ;
2023-09-30 23:06:38 +02:00
oi . setTemplates ( Obj : : SEER_HUT , randomAppearance , zone . getTerrainType ( ) ) ;
2024-10-05 19:37:52 +00:00
oi . value = static_cast < ui32 > ( ( ( 2 * ( creature - > getAIValue ( ) ) * creaturesAmount * ( 1 + static_cast < float > ( map . getZoneCount ( creature - > getFactionID ( ) ) ) / map . getTotalZoneCount ( ) ) ) - 4000 ) / 3 ) ;
2023-04-29 11:46:03 +02:00
if ( oi . value > zone . getMaxTreasureValue ( ) )
{
continue ;
}
else
{
2023-09-30 23:06:38 +02:00
if ( ! oi . templates . empty ( ) )
possibleSeerHuts . push_back ( oi ) ;
2023-04-29 11:46:03 +02:00
}
2022-08-09 09:54:32 +04:00
}
2024-01-09 16:43:36 +02:00
static const int seerLevels = std : : min ( generator . getConfig ( ) . questValues . size ( ) , generator . getConfig ( ) . questRewardValues . size ( ) ) ;
2024-06-24 03:23:26 +02:00
for ( int i = 0 ; i < seerLevels ; i + + ) //seems that code for exp and gold reward is similar
2022-08-09 09:54:32 +04:00
{
2023-05-20 11:46:32 +02:00
int randomAppearance = chooseRandomAppearance ( zone . getRand ( ) , Obj : : SEER_HUT , zone . getTerrainType ( ) ) ;
2022-08-09 09:54:32 +04:00
2023-09-30 23:06:38 +02:00
oi . setTemplates ( Obj : : SEER_HUT , randomAppearance , zone . getTerrainType ( ) ) ;
2022-08-09 09:54:32 +04:00
oi . value = generator . getConfig ( ) . questValues [ i ] ;
2023-04-29 11:46:03 +02:00
if ( oi . value > zone . getMaxTreasureValue ( ) )
{
//Both variants have same value
continue ;
}
2022-08-09 09:54:32 +04:00
oi . probability = 10 ;
2023-06-17 08:45:10 +02:00
oi . maxPerZone = 1 ;
2022-08-09 09:54:32 +04:00
2024-01-17 07:45:53 +01:00
oi . generateObject = [ i , randomAppearance , this , setRandomArtifact ] ( ) - > CGObjectInstance *
2022-08-09 09:54:32 +04:00
{
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : SEER_HUT , randomAppearance ) ;
2024-01-01 16:37:48 +02:00
auto * obj = dynamic_cast < CGSeerHut * > ( factory - > create ( map . mapInstance - > cb , nullptr ) ) ;
2023-09-13 01:40:07 +02:00
2023-09-15 15:29:41 +02:00
Rewardable : : VisitInfo reward ;
reward . reward . heroExperience = generator . getConfig ( ) . questRewardValues [ i ] ;
reward . visitType = Rewardable : : EEventType : : EVENT_FIRST_VISIT ;
obj - > configuration . info . push_back ( reward ) ;
2024-01-17 07:18:14 +01:00
setRandomArtifact ( obj ) ;
2022-08-09 09:54:32 +04:00
return obj ;
} ;
2024-01-19 16:17:59 +02:00
oi . destroyObject = destroyObject ;
2022-08-09 09:54:32 +04:00
2023-09-30 23:06:38 +02:00
if ( ! oi . templates . empty ( ) )
possibleSeerHuts . push_back ( oi ) ;
2022-08-09 09:54:32 +04:00
2024-01-17 07:45:53 +01:00
oi . generateObject = [ i , randomAppearance , this , setRandomArtifact ] ( ) - > CGObjectInstance *
2022-08-09 09:54:32 +04:00
{
auto factory = VLC - > objtypeh - > getHandlerFor ( Obj : : SEER_HUT , randomAppearance ) ;
2024-01-01 16:37:48 +02:00
auto * obj = dynamic_cast < CGSeerHut * > ( factory - > create ( map . mapInstance - > cb , nullptr ) ) ;
2023-09-13 01:40:07 +02:00
2023-09-15 15:29:41 +02:00
Rewardable : : VisitInfo reward ;
reward . reward . resources [ EGameResID : : GOLD ] = generator . getConfig ( ) . questRewardValues [ i ] ;
reward . visitType = Rewardable : : EEventType : : EVENT_FIRST_VISIT ;
obj - > configuration . info . push_back ( reward ) ;
2022-08-09 09:54:32 +04:00
2024-01-17 07:18:14 +01:00
setRandomArtifact ( obj ) ;
2022-08-09 09:54:32 +04:00
return obj ;
} ;
2024-01-19 16:17:59 +02:00
oi . destroyObject = destroyObject ;
2022-08-09 09:54:32 +04:00
2023-09-30 23:06:38 +02:00
if ( ! oi . templates . empty ( ) )
possibleSeerHuts . push_back ( oi ) ;
2023-04-29 11:46:03 +02:00
}
2023-06-14 20:50:14 +02:00
if ( possibleSeerHuts . empty ( ) )
{
return ;
}
2023-04-29 11:46:03 +02:00
for ( size_t i = 0 ; i < questArtsRemaining ; i + + )
{
2023-05-20 11:46:32 +02:00
addObjectToRandomPool ( * RandomGeneratorUtil : : nextItem ( possibleSeerHuts , zone . getRand ( ) ) ) ;
2022-08-09 09:54:32 +04:00
}
}
}
2023-05-05 10:30:36 +02:00
void TreasurePlacer : : setMaxPrisons ( size_t count )
{
2023-05-19 20:30:15 +02:00
RecursiveLock lock ( externalAccessMutex ) ;
2023-05-05 10:30:36 +02:00
maxPrisons = count ;
}
size_t TreasurePlacer : : getMaxPrisons ( ) const
{
2023-05-19 20:30:15 +02:00
RecursiveLock lock ( externalAccessMutex ) ;
2023-05-05 10:30:36 +02:00
return maxPrisons ;
}
2024-08-21 20:16:41 +02:00
int TreasurePlacer : : creatureToCount ( const CCreature * creature ) const
{
if ( ! creature - > getAIValue ( ) | | tierValues . empty ( ) ) //bug #2681
return 0 ; //this box won't be generated
//Follow the rules from https://heroes.thelazy.net/index.php/Pandora%27s_Box
int actualTier = creature - > getLevel ( ) > tierValues . size ( ) ?
tierValues . size ( ) - 1 :
creature - > getLevel ( ) - 1 ;
float creaturesAmount = std : : floor ( ( static_cast < float > ( tierValues [ actualTier ] ) ) / creature - > getAIValue ( ) ) ;
if ( creaturesAmount < 1 )
{
return 0 ;
}
else if ( creaturesAmount < = 5 )
{
//No change
}
else if ( creaturesAmount < = 12 )
{
creaturesAmount = std : : ceil ( creaturesAmount / 2 ) * 2 ;
}
else if ( creaturesAmount < = 50 )
{
creaturesAmount = std : : round ( creaturesAmount / 5 ) * 5 ;
}
else
{
creaturesAmount = std : : round ( creaturesAmount / 10 ) * 10 ;
}
return static_cast < int > ( creaturesAmount ) ;
} ;
2022-08-09 09:54:32 +04:00
bool TreasurePlacer : : isGuardNeededForTreasure ( int value )
2023-05-31 00:24:52 +02:00
{ // no guard in a zone with "monsters: none" and for small treasures; water zones cen get monster strength ZONE_NONE elsewhere if needed
return zone . monsterStrength ! = EMonsterStrength : : ZONE_NONE & & value > minGuardedValue ;
2022-08-09 09:54:32 +04:00
}
std : : vector < ObjectInfo * > TreasurePlacer : : prepareTreasurePile ( const CTreasureInfo & treasureInfo )
{
std : : vector < ObjectInfo * > objectInfos ;
int maxValue = treasureInfo . max ;
int minValue = treasureInfo . min ;
2023-12-19 09:24:43 +01:00
const ui32 desiredValue = zone . getRand ( ) . nextInt ( minValue , maxValue ) ;
2022-08-09 09:54:32 +04:00
int currentValue = 0 ;
bool hasLargeObject = false ;
2023-02-11 19:05:02 +03:00
while ( currentValue < = static_cast < int > ( desiredValue ) - 100 ) //no objects with value below 100 are available
2022-08-09 09:54:32 +04:00
{
2024-08-21 20:16:41 +02:00
// FIXME: Pointer might be invalidated after this
2023-06-08 19:23:23 +02:00
auto * oi = getRandomObject ( desiredValue , currentValue , ! hasLargeObject ) ;
2022-08-09 09:54:32 +04:00
if ( ! oi ) //fail
break ;
2023-09-30 23:06:38 +02:00
bool visitableFromTop = true ;
for ( auto & t : oi - > templates )
if ( ! t - > isVisitableFromTop ( ) )
visitableFromTop = false ;
if ( visitableFromTop )
2022-08-09 09:54:32 +04:00
{
objectInfos . push_back ( oi ) ;
}
else
{
objectInfos . insert ( objectInfos . begin ( ) , oi ) ; //large object shall at first place
hasLargeObject = true ;
}
//remove from possible objects
assert ( oi - > maxPerZone ) ;
oi - > maxPerZone - - ;
currentValue + = oi - > value ;
2023-12-19 09:24:43 +01:00
if ( currentValue > = minValue )
{
// 50% chance to end right here
2024-06-01 15:28:17 +00:00
if ( zone . getRand ( ) . nextInt ( 0 , 1 ) = = 1 )
2023-12-19 09:24:43 +01:00
break ;
}
2022-08-09 09:54:32 +04:00
}
return objectInfos ;
}
2022-08-20 14:17:27 +04:00
rmg : : Object TreasurePlacer : : constructTreasurePile ( const std : : vector < ObjectInfo * > & treasureInfos , bool densePlacement )
2022-08-09 09:54:32 +04:00
{
rmg : : Object rmgObject ;
2023-02-11 19:05:02 +03:00
for ( const auto & oi : treasureInfos )
2022-08-09 09:54:32 +04:00
{
auto blockedArea = rmgObject . getArea ( ) ;
2023-12-06 20:49:28 +01:00
auto entrableArea = rmgObject . getEntrableArea ( ) ;
2023-12-21 12:29:45 +01:00
auto accessibleArea = rmgObject . getAccessibleArea ( ) ;
2023-12-06 20:49:28 +01:00
2022-08-09 09:54:32 +04:00
if ( rmgObject . instances ( ) . empty ( ) )
2023-12-21 12:29:45 +01:00
{
2024-05-01 11:34:34 +02:00
rmgObject . setValue ( 0 ) ;
2023-12-21 12:29:45 +01:00
accessibleArea . add ( int3 ( ) ) ;
}
2022-08-09 09:54:32 +04:00
2024-08-21 20:16:41 +02:00
CGObjectInstance * object = nullptr ;
if ( oi - > generateObject )
2023-12-11 07:37:23 +01:00
{
2024-08-21 20:16:41 +02:00
object = oi - > generateObject ( ) ;
if ( oi - > templates . empty ( ) )
{
logGlobal - > warn ( " Deleting randomized object with no templates: %s " , object - > getObjectName ( ) ) ;
oi - > destroyObject ( object ) ;
delete object ;
continue ;
}
}
else
{
logGlobal - > error ( " ObjectInfo has no generateObject function! Templates: %d " , oi - > templates . size ( ) ) ;
2023-09-30 23:06:38 +02:00
continue ;
2023-12-11 07:37:23 +01:00
}
2023-09-30 23:06:38 +02:00
2023-12-21 09:58:39 +01:00
auto templates = object - > getObjectHandler ( ) - > getMostSpecificTemplates ( zone . getTerrainType ( ) ) ;
if ( templates . empty ( ) )
{
throw rmgException ( boost : : str ( boost : : format ( " Did not find template for object (%d,%d) at % s " ) % object->ID % object->subID % zone.getTerrainType().encode(zone.getTerrainType()))) ;
}
2023-12-06 20:49:28 +01:00
2023-12-21 09:58:39 +01:00
object - > appearance = * RandomGeneratorUtil : : nextItem ( templates , zone . getRand ( ) ) ;
2023-12-06 20:49:28 +01:00
2023-12-21 12:29:45 +01:00
//Put object in accessible area next to entrable area (excluding blockvis tiles)
if ( ! entrableArea . empty ( ) )
2023-12-06 20:49:28 +01:00
{
2023-12-21 12:29:45 +01:00
auto entrableBorder = entrableArea . getBorderOutside ( ) ;
accessibleArea . erase_if ( [ & ] ( const int3 & tile )
{
return ! entrableBorder . count ( tile ) ;
} ) ;
2023-12-06 20:49:28 +01:00
}
2022-08-09 09:54:32 +04:00
auto & instance = rmgObject . addInstance ( * object ) ;
2024-05-01 10:24:21 +02:00
rmgObject . setValue ( rmgObject . getValue ( ) + oi - > value ) ;
2024-01-19 17:26:51 +02:00
instance . onCleared = oi - > destroyObject ;
2022-08-09 09:54:32 +04:00
do
{
2023-12-21 12:29:45 +01:00
if ( accessibleArea . empty ( ) )
2022-08-09 09:54:32 +04:00
{
//fail - fallback
rmgObject . clear ( ) ;
return rmgObject ;
}
2022-08-20 14:17:27 +04:00
std : : vector < int3 > bestPositions ;
2023-12-21 12:29:45 +01:00
if ( densePlacement & & ! entrableArea . empty ( ) )
2022-08-20 14:17:27 +04:00
{
2023-12-21 12:29:45 +01:00
// Choose positon which has access to as many entrable tiles as possible
2022-08-20 14:17:27 +04:00
int bestPositionsWeight = std : : numeric_limits < int > : : max ( ) ;
2023-12-21 12:29:45 +01:00
for ( const auto & t : accessibleArea . getTilesVector ( ) )
2022-08-20 14:17:27 +04:00
{
instance . setPosition ( t ) ;
2023-12-06 20:49:28 +01:00
2023-12-21 12:29:45 +01:00
auto currentAccessibleArea = rmgObject . getAccessibleArea ( ) ;
auto currentEntrableBorder = rmgObject . getEntrableArea ( ) . getBorderOutside ( ) ;
currentAccessibleArea . erase_if ( [ & ] ( const int3 & tile )
{
return ! currentEntrableBorder . count ( tile ) ;
} ) ;
size_t w = currentAccessibleArea . getTilesVector ( ) . size ( ) ;
if ( w > bestPositionsWeight )
2022-08-20 14:17:27 +04:00
{
2023-12-06 20:49:28 +01:00
// Minimum 1 position must be entrable
2022-08-20 14:17:27 +04:00
bestPositions . clear ( ) ;
bestPositions . push_back ( t ) ;
bestPositionsWeight = w ;
}
else if ( w = = bestPositionsWeight )
{
bestPositions . push_back ( t ) ;
}
}
}
2023-12-06 20:49:28 +01:00
if ( bestPositions . empty ( ) )
2022-08-20 14:17:27 +04:00
{
2023-12-21 12:29:45 +01:00
bestPositions = accessibleArea . getTilesVector ( ) ;
2022-08-20 14:17:27 +04:00
}
2023-05-20 11:46:32 +02:00
int3 nextPos = * RandomGeneratorUtil : : nextItem ( bestPositions , zone . getRand ( ) ) ;
2022-08-09 09:54:32 +04:00
instance . setPosition ( nextPos - rmgObject . getPosition ( ) ) ;
auto instanceAccessibleArea = instance . getAccessibleArea ( ) ;
if ( instance . getBlockedArea ( ) . getTilesVector ( ) . size ( ) = = 1 )
{
2023-12-06 09:49:41 +01:00
if ( instance . object ( ) . appearance - > isVisitableFromTop ( ) & & ! instance . object ( ) . isBlockedVisitable ( ) )
2022-08-09 09:54:32 +04:00
instanceAccessibleArea . add ( instance . getVisitablePosition ( ) ) ;
}
2023-12-11 07:37:23 +01:00
//Do not clean up after first object
2022-08-09 09:54:32 +04:00
if ( rmgObject . instances ( ) . size ( ) = = 1 )
break ;
2023-12-06 09:49:41 +01:00
2023-12-21 12:29:45 +01:00
if ( ! blockedArea . overlap ( instance . getBlockedArea ( ) ) & & accessibleArea . overlap ( instanceAccessibleArea ) )
2022-08-09 09:54:32 +04:00
break ;
2023-12-06 20:49:28 +01:00
2022-08-09 09:54:32 +04:00
//fail - new position
2023-12-21 12:29:45 +01:00
accessibleArea . erase ( nextPos ) ;
2022-08-09 09:54:32 +04:00
} while ( true ) ;
}
return rmgObject ;
}
2023-06-08 19:23:23 +02:00
ObjectInfo * TreasurePlacer : : getRandomObject ( ui32 desiredValue , ui32 currentValue , bool allowLargeObjects )
2022-08-09 09:54:32 +04:00
{
std : : vector < std : : pair < ui32 , ObjectInfo * > > thresholds ; //handle complex object via pointer
ui32 total = 0 ;
//calculate actual treasure value range based on remaining value
2023-06-08 19:23:23 +02:00
ui32 maxVal = desiredValue - currentValue ;
2022-08-09 09:54:32 +04:00
ui32 minValue = static_cast < ui32 > ( 0.25f * ( desiredValue - currentValue ) ) ;
2024-08-21 20:16:41 +02:00
for ( ObjectInfo & oi : objects . getPossibleObjects ( ) ) //copy constructor turned out to be costly
2022-08-09 09:54:32 +04:00
{
if ( oi . value > maxVal )
break ; //this assumes values are sorted in ascending order
2023-09-30 23:06:38 +02:00
bool visitableFromTop = true ;
for ( auto & t : oi . templates )
if ( ! t - > isVisitableFromTop ( ) )
visitableFromTop = false ;
if ( ! visitableFromTop & & ! allowLargeObjects )
2022-08-09 09:54:32 +04:00
continue ;
if ( oi . value > = minValue & & oi . maxPerZone > 0 )
{
total + = oi . probability ;
2023-02-11 19:05:02 +03:00
thresholds . emplace_back ( total , & oi ) ;
2022-08-09 09:54:32 +04:00
}
}
if ( thresholds . empty ( ) )
{
return nullptr ;
}
else
{
2023-05-20 11:46:32 +02:00
int r = zone . getRand ( ) . nextInt ( 1 , total ) ;
2023-02-11 19:05:02 +03:00
auto sorter = [ ] ( const std : : pair < ui32 , ObjectInfo * > & rhs , const ui32 lhs ) - > bool
{
return static_cast < int > ( rhs . first ) < lhs ;
} ;
2022-08-09 09:54:32 +04:00
//binary search = fastest
2023-02-11 19:05:02 +03:00
auto it = std : : lower_bound ( thresholds . begin ( ) , thresholds . end ( ) , r , sorter ) ;
2022-08-09 09:54:32 +04:00
return it - > second ;
}
}
2023-06-08 19:51:21 +02:00
void TreasurePlacer : : createTreasures ( ObjectManager & manager )
2022-08-09 09:54:32 +04:00
{
2022-08-20 14:17:27 +04:00
const int maxAttempts = 2 ;
2023-06-08 19:51:21 +02:00
2022-08-09 09:54:32 +04:00
int mapMonsterStrength = map . getMapGenOptions ( ) . getMonsterStrength ( ) ;
2023-06-08 19:51:21 +02:00
int monsterStrength = ( zone . monsterStrength = = EMonsterStrength : : ZONE_NONE ? 0 : zone . monsterStrength + mapMonsterStrength - 1 ) ; //array index from 0 to 4; pick any correct value for ZONE_NONE, minGuardedValue won't be used in this case anyway
2024-01-09 16:43:36 +02:00
static const int minGuardedValues [ ] = { 6500 , 4167 , 3000 , 1833 , 1333 } ;
2022-08-09 09:54:32 +04:00
minGuardedValue = minGuardedValues [ monsterStrength ] ;
2024-05-01 10:24:21 +02:00
const auto blockingGuardMaxValue = zone . getMaxTreasureValue ( ) / 3 ;
2023-06-08 19:51:21 +02:00
auto valueComparator = [ ] ( const CTreasureInfo & lhs , const CTreasureInfo & rhs ) - > bool
2022-08-09 09:54:32 +04:00
{
return lhs . max > rhs . max ;
} ;
2023-06-08 19:51:21 +02:00
auto restoreZoneLimits = [ ] ( const std : : vector < ObjectInfo * > & treasurePile )
2022-08-09 09:54:32 +04:00
{
2023-06-08 19:51:21 +02:00
for ( auto * oi : treasurePile )
2022-08-09 09:54:32 +04:00
{
oi - > maxPerZone + + ;
}
} ;
2024-05-01 07:58:17 +02:00
rmg : : Area roads ;
auto rp = zone . getModificator < RoadPlacer > ( ) ;
if ( rp )
{
roads = rp - > getRoads ( ) ;
}
rmg : : Area nextToRoad ( roads . getBorderOutside ( ) ) ;
2022-08-09 09:54:32 +04:00
//place biggest treasures first at large distance, place smaller ones inbetween
auto treasureInfo = zone . getTreasureInfo ( ) ;
boost : : sort ( treasureInfo , valueComparator ) ;
2023-06-08 19:51:21 +02:00
2022-08-09 09:54:32 +04:00
//sort treasures by ascending value so we can stop checking treasures with too high value
2024-08-21 20:16:41 +02:00
objects . sortPossibleObjects ( ) ;
2023-06-08 19:51:21 +02:00
2024-03-27 06:16:48 +01:00
const size_t size = zone . area ( ) - > getTilesVector ( ) . size ( ) ;
2023-06-08 19:51:21 +02:00
2022-08-09 09:54:32 +04:00
int totalDensity = 0 ;
2023-06-08 19:51:21 +02:00
2024-08-21 20:16:41 +02:00
// FIXME: No need to use iterator here
2023-06-12 22:15:59 +02:00
for ( auto t = treasureInfo . begin ( ) ; t ! = treasureInfo . end ( ) ; t + + )
2022-08-09 09:54:32 +04:00
{
2023-06-08 19:51:21 +02:00
std : : vector < rmg : : Object > treasures ;
2022-08-09 09:54:32 +04:00
//discard objects with too high value to be ever placed
2024-08-21 20:16:41 +02:00
objects . discardObjectsAboveValue ( t - > max ) ;
2023-06-08 19:51:21 +02:00
2023-06-12 22:15:59 +02:00
totalDensity + = t - > density ;
2023-06-08 19:51:21 +02:00
2024-03-20 12:39:07 +01:00
const int DENSITY_CONSTANT = 400 ;
2023-12-12 12:50:27 +01:00
size_t count = ( size * t - > density ) / DENSITY_CONSTANT ;
2023-06-12 22:15:59 +02:00
2024-03-20 15:35:06 +01:00
const float minDistance = std : : max < float > ( std : : sqrt ( std : : min < ui32 > ( t - > min , 30000 ) / 10.0f / totalDensity ) , 1.0f ) ;
2023-06-08 19:51:21 +02:00
2023-06-12 22:15:59 +02:00
size_t emergencyLoopFinish = 0 ;
while ( treasures . size ( ) < count & & emergencyLoopFinish < count )
2022-08-09 09:54:32 +04:00
{
2023-06-12 22:15:59 +02:00
auto treasurePileInfos = prepareTreasurePile ( * t ) ;
2023-06-08 19:51:21 +02:00
if ( treasurePileInfos . empty ( ) )
2022-08-28 12:54:06 +04:00
{
2023-06-12 22:15:59 +02:00
emergencyLoopFinish + + ; //Exit potentially infinite loop for bad settings
2022-08-28 12:54:06 +04:00
continue ;
}
2023-06-08 19:51:21 +02:00
2024-08-21 20:16:41 +02:00
int value = std : : accumulate ( treasurePileInfos . begin ( ) , treasurePileInfos . end ( ) , 0 ,
[ ] ( int v , const ObjectInfo * oi )
{
return v + oi - > value ;
} ) ;
2023-06-08 19:51:21 +02:00
2024-06-24 03:23:26 +02:00
const ui32 maxPileGenerationAttempts = 2 ;
for ( ui32 attempt = 0 ; attempt < maxPileGenerationAttempts ; attempt + + )
2022-08-09 09:54:32 +04:00
{
2023-06-08 19:51:21 +02:00
auto rmgObject = constructTreasurePile ( treasurePileInfos , attempt = = maxAttempts ) ;
2023-12-07 17:41:09 +01:00
if ( rmgObject . instances ( ) . empty ( ) )
2022-08-09 09:54:32 +04:00
{
2024-06-24 03:23:26 +02:00
// Restore once if all attempts failed
if ( attempt = = ( maxPileGenerationAttempts - 1 ) )
2023-12-07 17:41:09 +01:00
{
restoreZoneLimits ( treasurePileInfos ) ;
}
2023-06-08 19:51:21 +02:00
continue ;
}
2023-02-11 19:05:02 +03:00
2023-06-08 19:51:21 +02:00
//guard treasure pile
bool guarded = isGuardNeededForTreasure ( value ) ;
if ( guarded )
guarded = manager . addGuard ( rmgObject , value ) ;
treasures . push_back ( rmgObject ) ;
break ;
2022-08-09 09:54:32 +04:00
}
2023-06-08 19:51:21 +02:00
}
for ( auto & rmgObject : treasures )
{
const bool guarded = rmgObject . isGuarded ( ) ;
2023-12-12 07:40:54 +01:00
auto path = rmg : : Path : : invalid ( ) ;
2023-06-08 19:51:21 +02:00
2023-12-12 07:40:54 +01:00
{
2024-03-27 06:16:48 +01:00
Zone : : Lock lock ( zone . areaMutex ) ; //We are going to subtract this area
2024-03-27 08:03:19 +01:00
2024-03-27 06:16:48 +01:00
auto searchArea = zone . areaPossible ( ) . get ( ) ;
searchArea . erase_if ( [ this , & minDistance ] ( const int3 & tile ) - > bool
{
auto ti = map . getTileInfo ( tile ) ;
return ( ti . getNearestObjectDistance ( ) < minDistance ) ;
} ) ;
2023-06-08 19:51:21 +02:00
2024-03-27 06:16:48 +01:00
if ( guarded )
{
2024-05-01 07:58:17 +02:00
searchArea . subtract ( roads ) ;
2024-05-01 10:24:21 +02:00
path = manager . placeAndConnectObject ( searchArea , rmgObject , [ this , & rmgObject , & minDistance , & manager , blockingGuardMaxValue , & roads , & nextToRoad ] ( const int3 & tile )
2023-06-08 19:51:21 +02:00
{
2024-03-27 06:16:48 +01:00
float bestDistance = 10e9 ;
for ( const auto & t : rmgObject . getArea ( ) . getTilesVector ( ) )
{
float distance = map . getTileInfo ( t ) . getNearestObjectDistance ( ) ;
if ( distance < minDistance )
return - 1.f ;
else
vstd : : amin ( bestDistance , distance ) ;
}
2024-05-01 10:24:21 +02:00
// Guard cannot be adjacent to road, but blocked side of an object could be
if ( rmgObject . getValue ( ) > blockingGuardMaxValue & & nextToRoad . contains ( rmgObject . getGuardPos ( ) ) )
2024-05-01 07:58:17 +02:00
{
return - 1.f ;
}
2024-03-27 06:16:48 +01:00
const auto & guardedArea = rmgObject . instances ( ) . back ( ) - > getAccessibleArea ( ) ;
const auto areaToBlock = rmgObject . getAccessibleArea ( true ) - guardedArea ;
2024-05-01 10:24:21 +02:00
if ( zone . freePaths ( ) - > overlap ( areaToBlock ) | | roads . overlap ( areaToBlock ) | | manager . getVisitableArea ( ) . overlap ( areaToBlock ) )
2023-06-08 19:51:21 +02:00
return - 1.f ;
2024-05-01 12:15:07 +02:00
// Add huge penalty for objects hiding roads
if ( rmgObject . getBorderAbove ( ) . overlap ( roads ) )
bestDistance / = 10.0f ;
2024-03-27 06:16:48 +01:00
return bestDistance ;
} , guarded , false , ObjectManager : : OptimizeType : : BOTH ) ;
}
else
{
2024-05-01 09:16:10 +02:00
// Do not place non-removable objects on roads
if ( ! rmgObject . getRemovableArea ( ) . contains ( rmgObject . getArea ( ) ) )
{
searchArea . subtract ( roads ) ;
}
2024-03-27 06:16:48 +01:00
path = manager . placeAndConnectObject ( searchArea , rmgObject , minDistance , guarded , false , ObjectManager : : OptimizeType : : DISTANCE ) ;
}
2023-12-12 07:40:54 +01:00
}
2023-06-08 19:51:21 +02:00
2023-12-12 07:40:54 +01:00
if ( path . valid ( ) )
{
2024-03-20 13:51:16 +01:00
# ifdef TREASURE_PLACER_LOG
2023-12-12 07:40:54 +01:00
treasureArea . unite ( rmgObject . getArea ( ) ) ;
if ( guarded )
2023-06-08 19:51:21 +02:00
{
2023-12-12 07:40:54 +01:00
guards . unite ( rmgObject . instances ( ) . back ( ) - > getBlockedArea ( ) ) ;
auto guardedArea = rmgObject . instances ( ) . back ( ) - > getAccessibleArea ( ) ;
2023-12-18 13:52:03 +01:00
auto areaToBlock = rmgObject . getAccessibleArea ( true ) - guardedArea ;
2023-12-12 07:40:54 +01:00
treasureBlockArea . unite ( areaToBlock ) ;
2022-08-09 09:54:32 +04:00
}
2024-03-20 13:51:16 +01:00
# endif
2023-12-12 07:40:54 +01:00
zone . connectPath ( path ) ;
manager . placeObject ( rmgObject , guarded , true ) ;
2022-08-09 09:54:32 +04:00
}
}
}
}
char TreasurePlacer : : dump ( const int3 & t )
{
if ( guards . contains ( t ) )
return ' ! ' ;
if ( treasureArea . contains ( t ) )
return ' $ ' ;
if ( treasureBlockArea . contains ( t ) )
return ' * ' ;
return Modificator : : dump ( t ) ;
}
2024-08-21 20:16:41 +02:00
void TreasurePlacer : : ObjectPool : : addObject ( const ObjectInfo & info )
2022-08-09 09:54:32 +04:00
{
2024-08-21 20:16:41 +02:00
possibleObjects . push_back ( info ) ;
}
void TreasurePlacer : : ObjectPool : : updateObject ( MapObjectID id , MapObjectSubID subid , ObjectInfo info )
{
/*
Handle separately :
- Dwellings
- Prisons
- Seer huts ( quests )
- Pandora Boxes
*/
// FIXME: This will drop all templates
2024-09-14 09:23:28 +02:00
customObjects . insert ( std : : make_pair ( CompoundMapObjectID ( id , subid ) , info ) ) ;
2024-08-21 20:16:41 +02:00
}
2024-09-12 21:04:27 +02:00
void TreasurePlacer : : ObjectPool : : patchWithZoneConfig ( const Zone & zone , TreasurePlacer * tp )
2024-08-21 20:16:41 +02:00
{
// FIXME: Wycina wszystkie obiekty poza pandorami i dwellami :?
// Copy standard objects if they are not already modified
/*
for ( const auto & object : possibleObjects )
{
for ( const auto & templ : object . templates )
{
// FIXME: Objects with same temmplates (Pandora boxes) are not added
CompoundMapObjectID key ( templ - > id , templ - > subid ) ;
if ( ! vstd : : contains ( customObjects , key ) )
{
customObjects [ key ] = object ;
}
}
}
*/
2024-08-24 15:12:22 +02:00
auto bannedObjectCategories = zone . getBannedObjectCategories ( ) ;
auto categoriesSet = std : : unordered_set < ObjectConfig : : EObjectCategory > ( bannedObjectCategories . begin ( ) , bannedObjectCategories . end ( ) ) ;
2024-09-21 13:36:28 +02:00
if ( categoriesSet . count ( ObjectConfig : : EObjectCategory : : ALL ) )
2024-08-24 15:12:22 +02:00
{
2024-09-21 13:36:28 +02:00
possibleObjects . clear ( ) ;
}
else
2024-08-21 20:16:41 +02:00
{
2024-09-21 13:36:28 +02:00
vstd : : erase_if ( possibleObjects , [ this , & categoriesSet ] ( const ObjectInfo & oi ) - > bool
2024-08-21 20:16:41 +02:00
{
2024-09-21 13:36:28 +02:00
auto category = getObjectCategory ( oi . getCompoundID ( ) ) ;
if ( categoriesSet . count ( category ) )
2024-08-21 20:16:41 +02:00
{
2024-09-21 13:36:28 +02:00
logGlobal - > info ( " Removing object %s from possible objects " , oi . templates . front ( ) - > stringID ) ;
2024-08-21 20:16:41 +02:00
return true ;
}
2024-09-21 13:36:28 +02:00
return false ;
} ) ;
auto bannedObjects = zone . getBannedObjects ( ) ;
auto bannedObjectsSet = std : : set < CompoundMapObjectID > ( bannedObjects . begin ( ) , bannedObjects . end ( ) ) ;
vstd : : erase_if ( possibleObjects , [ & bannedObjectsSet ] ( const ObjectInfo & object )
{
for ( const auto & templ : object . templates )
{
CompoundMapObjectID key = object . getCompoundID ( ) ;
if ( bannedObjectsSet . count ( key ) )
{
// FIXME: Stopped working, nothing is banned
logGlobal - > info ( " Banning object %s from possible objects " , templ - > stringID ) ;
return true ;
}
}
return false ;
} ) ;
}
2024-08-21 20:16:41 +02:00
2024-08-24 20:18:36 +02:00
auto configuredObjects = zone . getConfiguredObjects ( ) ;
2024-09-12 21:04:27 +02:00
// FIXME: Access TreasurePlacer from ObjectPool
for ( auto & object : configuredObjects )
{
2024-09-14 09:23:28 +02:00
tp - > setBasicProperties ( object , object . getCompoundID ( ) ) ;
2024-09-12 21:04:27 +02:00
addObject ( object ) ;
2024-09-14 09:23:28 +02:00
logGlobal - > info ( " Added custom object of type %d.%d " , object . primaryID , object . secondaryID ) ;
2024-09-12 21:04:27 +02:00
}
2024-08-24 20:18:36 +02:00
// TODO: Overwrite or add to possibleObjects
2024-08-21 20:16:41 +02:00
// FIXME: Protect with mutex as well?
/*
for ( const auto & customObject : customObjects )
{
addObject ( customObject . second ) ;
}
*/
// TODO: Consider adding custom Pandora boxes with arbitrary content
}
std : : vector < ObjectInfo > & TreasurePlacer : : ObjectPool : : getPossibleObjects ( )
{
return possibleObjects ;
}
void TreasurePlacer : : ObjectPool : : sortPossibleObjects ( )
{
boost : : sort ( possibleObjects , [ ] ( const ObjectInfo & oi1 , const ObjectInfo & oi2 ) - > bool
{
return oi1 . value < oi2 . value ;
} ) ;
}
void TreasurePlacer : : ObjectPool : : discardObjectsAboveValue ( ui32 value )
{
vstd : : erase_if ( possibleObjects , [ value ] ( const ObjectInfo & oi ) - > bool
{
return oi . value > value ;
} ) ;
2022-08-09 09:54:32 +04:00
}
2022-07-26 16:07:42 +03:00
2024-08-24 20:18:36 +02:00
ObjectConfig : : EObjectCategory TreasurePlacer : : ObjectPool : : getObjectCategory ( CompoundMapObjectID id )
2024-08-24 15:12:22 +02:00
{
2024-08-24 20:18:36 +02:00
auto name = VLC - > objtypeh - > getObjectHandlerName ( id . primaryID ) ;
2024-08-24 15:12:22 +02:00
if ( name = = " configurable " )
{
2024-08-24 20:18:36 +02:00
auto handler = VLC - > objtypeh - > getHandlerFor ( id . primaryID , id . secondaryID ) ;
if ( ! handler )
{
return ObjectConfig : : EObjectCategory : : NONE ;
}
2024-09-21 13:36:28 +02:00
2024-08-24 20:18:36 +02:00
auto temp = handler - > getTemplates ( ) . front ( ) ;
auto info = handler - > getObjectInfo ( temp ) ;
2024-09-21 13:36:28 +02:00
if ( info - > hasGuards ( ) )
{
return ObjectConfig : : EObjectCategory : : CREATURE_BANK ;
}
else if ( info - > givesResources ( ) )
2024-08-24 20:18:36 +02:00
{
return ObjectConfig : : EObjectCategory : : RESOURCE ;
}
else if ( info - > givesArtifacts ( ) )
{
return ObjectConfig : : EObjectCategory : : RANDOM_ARTIFACT ;
}
else if ( info - > givesBonuses ( ) )
{
return ObjectConfig : : EObjectCategory : : BONUS ;
}
return ObjectConfig : : EObjectCategory : : OTHER ;
2024-08-24 15:12:22 +02:00
}
else if ( name = = " dwelling " | | name = = " randomDwelling " )
{
// TODO: Special handling for different tiers
return ObjectConfig : : EObjectCategory : : DWELLING ;
}
else if ( name = = " bank " )
return ObjectConfig : : EObjectCategory : : CREATURE_BANK ;
else if ( name = = " market " )
return ObjectConfig : : EObjectCategory : : OTHER ;
else if ( name = = " hillFort " )
return ObjectConfig : : EObjectCategory : : OTHER ;
else if ( name = = " resource " | | name = = " randomResource " )
return ObjectConfig : : EObjectCategory : : RESOURCE ;
else if ( name = = " randomArtifact " ) //"artifact"
return ObjectConfig : : EObjectCategory : : RANDOM_ARTIFACT ;
2024-09-23 17:09:46 +02:00
else if ( name = = " artifact " )
{
if ( id . primaryID = = Obj : : SPELL_SCROLL ) // randomArtifactTreasure
{
return ObjectConfig : : EObjectCategory : : SPELL_SCROLL ;
}
else
{
return ObjectConfig : : EObjectCategory : : QUEST_ARTIFACT ;
}
}
2024-08-24 15:12:22 +02:00
else if ( name = = " denOfThieves " )
return ObjectConfig : : EObjectCategory : : OTHER ;
else if ( name = = " lighthouse " )
{
2024-08-24 20:18:36 +02:00
return ObjectConfig : : EObjectCategory : : BONUS ;
2024-08-24 15:12:22 +02:00
}
else if ( name = = " magi " )
2024-08-24 20:18:36 +02:00
{
// TODO: By default, both eye and hut are banned in every zone
2024-08-24 15:12:22 +02:00
return ObjectConfig : : EObjectCategory : : OTHER ;
2024-08-24 20:18:36 +02:00
}
2024-08-24 15:12:22 +02:00
else if ( name = = " mine " )
return ObjectConfig : : EObjectCategory : : RESOURCE_GENERATOR ;
else if ( name = = " pandora " )
return ObjectConfig : : EObjectCategory : : PANDORAS_BOX ;
else if ( name = = " prison " )
{
2024-08-24 20:18:36 +02:00
// TODO: Prisons should be configurable
2024-08-24 15:12:22 +02:00
return ObjectConfig : : EObjectCategory : : OTHER ;
}
2024-08-24 20:18:36 +02:00
else if ( name = = " questArtifact " )
2024-08-24 15:12:22 +02:00
{
2024-08-24 20:18:36 +02:00
// TODO: There are no dedicated quest artifacts, needs extra logic
2024-08-24 15:12:22 +02:00
return ObjectConfig : : EObjectCategory : : QUEST_ARTIFACT ;
}
2024-08-24 20:18:36 +02:00
else if ( name = = " seerHut " )
{
return ObjectConfig : : EObjectCategory : : SEER_HUT ;
}
2024-08-24 15:12:22 +02:00
else if ( name = = " siren " )
2024-08-24 20:18:36 +02:00
return ObjectConfig : : EObjectCategory : : BONUS ;
2024-08-24 15:12:22 +02:00
else if ( name = = " obelisk " )
return ObjectConfig : : EObjectCategory : : OTHER ;
// TODO: ObjectConfig::EObjectCategory::SPELL_SCROLL
// Not interesting for us
return ObjectConfig : : EObjectCategory : : NONE ;
}
2022-07-26 16:07:42 +03:00
VCMI_LIB_NAMESPACE_END