2022-08-09 07:54:32 +02:00
/*
* RmgObject . 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 "RmgObject.h"
# include "RmgMap.h"
# include "../mapping/CMapEditManager.h"
# include "../mapping/CMap.h"
# include "../VCMI_Lib.h"
2023-06-02 20:47:37 +02:00
# include "../mapObjectConstructors/AObjectTypeHandler.h"
# include "../mapObjectConstructors/CObjectClassesHandler.h"
# include "../mapObjects/ObjectTemplate.h"
2022-08-09 07:54:32 +02:00
# include "Functions.h"
2023-01-09 01:17:37 +02:00
# include "../TerrainHandler.h"
2022-08-09 07:54:32 +02:00
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_BEGIN
2022-08-09 07:54:32 +02:00
using namespace rmg ;
Object : : Instance : : Instance ( const Object & parent , CGObjectInstance & object ) : dParent ( parent ) , dObject ( object )
{
setPosition ( dPosition ) ;
}
Object : : Instance : : Instance ( const Object & parent , CGObjectInstance & object , const int3 & position ) : Instance ( parent , object )
{
setPosition ( position ) ;
}
const Area & Object : : Instance : : getBlockedArea ( ) const
{
if ( dBlockedAreaCache . empty ( ) )
{
dBlockedAreaCache . assign ( dObject . getBlockedPos ( ) ) ;
if ( dObject . isVisitable ( ) | | dBlockedAreaCache . empty ( ) )
dBlockedAreaCache . add ( dObject . visitablePos ( ) ) ;
}
return dBlockedAreaCache ;
}
2023-04-10 19:26:53 +02:00
int3 Object : : Instance : : getTopTile ( ) const
{
return object ( ) . getTopVisiblePos ( ) ;
}
2022-08-09 07:54:32 +02:00
int3 Object : : Instance : : getPosition ( bool isAbsolute ) const
{
if ( isAbsolute )
return dPosition + dParent . getPosition ( ) ;
else
return dPosition ;
}
int3 Object : : Instance : : getVisitablePosition ( ) const
{
return dObject . visitablePos ( ) ;
}
const rmg : : Area & Object : : Instance : : getAccessibleArea ( ) const
{
if ( dAccessibleAreaCache . empty ( ) )
{
auto neighbours = rmg : : Area ( { getVisitablePosition ( ) } ) . getBorderOutside ( ) ;
rmg : : Area visitable = rmg : : Area ( neighbours ) - getBlockedArea ( ) ;
2023-02-11 18:05:02 +02:00
for ( const auto & from : visitable . getTiles ( ) )
2022-08-09 07:54:32 +02:00
{
if ( isVisitableFrom ( from ) )
dAccessibleAreaCache . add ( from ) ;
}
}
return dAccessibleAreaCache ;
}
void Object : : Instance : : setPosition ( const int3 & position )
{
dPosition = position ;
dObject . pos = dPosition + dParent . getPosition ( ) ;
dBlockedAreaCache . clear ( ) ;
dAccessibleAreaCache . clear ( ) ;
dParent . dAccessibleAreaCache . clear ( ) ;
dParent . dAccessibleAreaFullCache . clear ( ) ;
dParent . dFullAreaCache . clear ( ) ;
}
void Object : : Instance : : setPositionRaw ( const int3 & position )
{
if ( ! dObject . pos . valid ( ) )
{
dObject . pos = dPosition + dParent . getPosition ( ) ;
dBlockedAreaCache . clear ( ) ;
dAccessibleAreaCache . clear ( ) ;
dParent . dAccessibleAreaCache . clear ( ) ;
dParent . dAccessibleAreaFullCache . clear ( ) ;
dParent . dFullAreaCache . clear ( ) ;
}
auto shift = position + dParent . getPosition ( ) - dObject . pos ;
dAccessibleAreaCache . translate ( shift ) ;
dBlockedAreaCache . translate ( shift ) ;
dPosition = position ;
dObject . pos = dPosition + dParent . getPosition ( ) ;
}
2022-09-29 19:07:56 +02:00
void Object : : Instance : : setAnyTemplate ( )
2022-08-09 07:54:32 +02:00
{
2022-09-29 19:07:56 +02:00
auto templates = VLC - > objtypeh - > getHandlerFor ( dObject . ID , dObject . subID ) - > getTemplates ( ) ;
if ( templates . empty ( ) )
throw rmgException ( boost : : to_string ( boost : : format ( " Did not find any graphics for object (%d,%d) " ) % dObject.ID % dObject.subID)) ;
dObject . appearance = templates . front ( ) ;
dAccessibleAreaCache . clear ( ) ;
setPosition ( getPosition ( false ) ) ;
}
void Object : : Instance : : setTemplate ( TerrainId terrain )
{
auto templates = VLC - > objtypeh - > getHandlerFor ( dObject . ID , dObject . subID ) - > getTemplates ( terrain ) ;
if ( templates . empty ( ) )
2022-08-09 07:54:32 +02:00
{
2023-01-01 22:42:28 +02:00
auto terrainName = VLC - > terrainTypeHandler - > getById ( terrain ) - > getNameTranslated ( ) ;
2022-09-29 19:07:56 +02:00
throw rmgException ( boost : : to_string ( boost : : format ( " Did not find graphics for object (%d,%d) at % s " ) % dObject.ID % dObject.subID % terrainName)) ;
2022-08-09 07:54:32 +02:00
}
2022-09-29 19:07:56 +02:00
dObject . appearance = templates . front ( ) ;
2022-08-09 07:54:32 +02:00
dAccessibleAreaCache . clear ( ) ;
setPosition ( getPosition ( false ) ) ;
}
void Object : : Instance : : clear ( )
{
delete & dObject ;
dBlockedAreaCache . clear ( ) ;
dAccessibleAreaCache . clear ( ) ;
dParent . dAccessibleAreaCache . clear ( ) ;
dParent . dAccessibleAreaFullCache . clear ( ) ;
dParent . dFullAreaCache . clear ( ) ;
}
bool Object : : Instance : : isVisitableFrom ( const int3 & position ) const
{
auto relPosition = position - getPosition ( true ) ;
2022-09-11 15:12:35 +02:00
return dObject . appearance - > isVisitableFrom ( relPosition . x , relPosition . y ) ;
2022-08-09 07:54:32 +02:00
}
CGObjectInstance & Object : : Instance : : object ( )
{
return dObject ;
}
const CGObjectInstance & Object : : Instance : : object ( ) const
{
return dObject ;
}
2023-06-08 19:51:21 +02:00
Object : : Object ( CGObjectInstance & object , const int3 & position ) :
guarded ( false )
2022-08-09 07:54:32 +02:00
{
addInstance ( object , position ) ;
}
2023-06-08 19:51:21 +02:00
Object : : Object ( CGObjectInstance & object ) :
guarded ( false )
2022-08-09 07:54:32 +02:00
{
addInstance ( object ) ;
}
2023-06-08 19:51:21 +02:00
Object : : Object ( const Object & object ) :
dStrength ( object . dStrength ) ,
guarded ( false )
2022-08-09 07:54:32 +02:00
{
2023-02-11 18:05:02 +02:00
for ( const auto & i : object . dInstances )
2022-08-09 07:54:32 +02:00
addInstance ( const_cast < CGObjectInstance & > ( i . object ( ) ) , i . getPosition ( ) ) ;
setPosition ( object . getPosition ( ) ) ;
}
std : : list < Object : : Instance * > Object : : instances ( )
{
std : : list < Object : : Instance * > result ;
for ( auto & i : dInstances )
2023-05-20 17:08:44 +02:00
result . push_back ( & i ) ;
2022-08-09 07:54:32 +02:00
return result ;
}
std : : list < const Object : : Instance * > Object : : instances ( ) const
{
std : : list < const Object : : Instance * > result ;
for ( const auto & i : dInstances )
result . push_back ( & i ) ;
return result ;
}
void Object : : addInstance ( Instance & object )
{
//assert(object.dParent == *this);
2023-06-08 19:51:21 +02:00
setGuardedIfMonster ( object ) ;
2022-08-09 07:54:32 +02:00
dInstances . push_back ( object ) ;
2023-06-08 19:51:21 +02:00
2022-08-09 07:54:32 +02:00
dFullAreaCache . clear ( ) ;
dAccessibleAreaCache . clear ( ) ;
dAccessibleAreaFullCache . clear ( ) ;
}
Object : : Instance & Object : : addInstance ( CGObjectInstance & object )
{
dInstances . emplace_back ( * this , object ) ;
2023-06-08 19:51:21 +02:00
setGuardedIfMonster ( dInstances . back ( ) ) ;
2022-08-09 07:54:32 +02:00
dFullAreaCache . clear ( ) ;
dAccessibleAreaCache . clear ( ) ;
dAccessibleAreaFullCache . clear ( ) ;
return dInstances . back ( ) ;
}
Object : : Instance & Object : : addInstance ( CGObjectInstance & object , const int3 & position )
{
dInstances . emplace_back ( * this , object , position ) ;
2023-06-08 19:51:21 +02:00
setGuardedIfMonster ( dInstances . back ( ) ) ;
2022-08-09 07:54:32 +02:00
dFullAreaCache . clear ( ) ;
dAccessibleAreaCache . clear ( ) ;
dAccessibleAreaFullCache . clear ( ) ;
return dInstances . back ( ) ;
}
const int3 & Object : : getPosition ( ) const
{
return dPosition ;
}
int3 Object : : getVisitablePosition ( ) const
{
assert ( ! dInstances . empty ( ) ) ;
2023-02-11 18:05:02 +02:00
for ( const auto & instance : dInstances )
2022-08-09 07:54:32 +02:00
if ( ! getArea ( ) . contains ( instance . getVisitablePosition ( ) ) )
return instance . getVisitablePosition ( ) ;
return dInstances . back ( ) . getVisitablePosition ( ) ; //fallback - return position of last object
}
const rmg : : Area & Object : : getAccessibleArea ( bool exceptLast ) const
{
if ( dInstances . empty ( ) )
return dAccessibleAreaFullCache ;
if ( exceptLast & & ! dAccessibleAreaCache . empty ( ) )
return dAccessibleAreaCache ;
if ( ! exceptLast & & ! dAccessibleAreaFullCache . empty ( ) )
return dAccessibleAreaFullCache ;
for ( auto i = dInstances . begin ( ) ; i ! = std : : prev ( dInstances . end ( ) ) ; + + i )
dAccessibleAreaCache . unite ( i - > getAccessibleArea ( ) ) ;
dAccessibleAreaFullCache = dAccessibleAreaCache ;
dAccessibleAreaFullCache . unite ( dInstances . back ( ) . getAccessibleArea ( ) ) ;
dAccessibleAreaCache . subtract ( getArea ( ) ) ;
dAccessibleAreaFullCache . subtract ( getArea ( ) ) ;
if ( exceptLast )
return dAccessibleAreaCache ;
else
return dAccessibleAreaFullCache ;
}
void Object : : setPosition ( const int3 & position )
{
dAccessibleAreaCache . translate ( position - dPosition ) ;
dAccessibleAreaFullCache . translate ( position - dPosition ) ;
dFullAreaCache . translate ( position - dPosition ) ;
dPosition = position ;
for ( auto & i : dInstances )
i . setPositionRaw ( i . getPosition ( ) ) ;
}
2022-09-29 11:44:46 +02:00
void Object : : setTemplate ( const TerrainId & terrain )
2022-08-09 07:54:32 +02:00
{
for ( auto & i : dInstances )
i . setTemplate ( terrain ) ;
}
const Area & Object : : getArea ( ) const
{
if ( ! dFullAreaCache . empty ( ) | | dInstances . empty ( ) )
return dFullAreaCache ;
for ( const auto & instance : dInstances )
{
dFullAreaCache . unite ( instance . getBlockedArea ( ) ) ;
}
return dFullAreaCache ;
}
2023-04-10 19:26:53 +02:00
const int3 Object : : getVisibleTop ( ) const
{
int3 topTile ( - 1 , 10000 , - 1 ) ; //Start at the bottom
for ( const auto & i : dInstances )
{
if ( i . getTopTile ( ) . y < topTile . y )
{
topTile = i . getTopTile ( ) ;
}
}
return topTile ;
}
2023-06-08 19:51:21 +02:00
bool rmg : : Object : : isGuarded ( ) const
{
return guarded ;
}
void rmg : : Object : : setGuardedIfMonster ( const Instance & object )
{
if ( object . object ( ) . ID = = Obj : : MONSTER )
{
guarded = true ;
}
}
2022-08-09 07:54:32 +02:00
void Object : : Instance : : finalize ( RmgMap & map )
{
if ( ! map . isOnMap ( getPosition ( true ) ) )
throw rmgException ( boost : : to_string ( boost : : format ( " Position of object %d at %s is outside the map " ) % dObject . id % getPosition ( true ) . toString ( ) ) ) ;
2022-09-29 19:07:56 +02:00
//If no specific template was defined for this object, select any matching
if ( ! dObject . appearance )
{
2023-05-19 20:30:15 +02:00
const auto * terrainType = map . getTile ( getPosition ( true ) ) . terType ;
2023-01-01 17:10:47 +02:00
auto templates = VLC - > objtypeh - > getHandlerFor ( dObject . ID , dObject . subID ) - > getTemplates ( terrainType - > getId ( ) ) ;
2022-09-29 19:07:56 +02:00
if ( templates . empty ( ) )
{
throw rmgException ( boost : : to_string ( boost : : format ( " Did not find graphics for object (%d,%d) at % s ( terrain % d ) " ) % dObject.ID % dObject.subID % getPosition(true).toString() % terrainType)) ;
}
else
{
2023-01-01 17:10:47 +02:00
setTemplate ( terrainType - > getId ( ) ) ;
2022-09-29 19:07:56 +02:00
}
}
2023-07-18 09:53:14 +02:00
if ( dObject . ID = = Obj : : MONSTER )
{
//Make up for extra offset in HotA creature templates
auto visitableOffset = dObject . getVisitableOffset ( ) ;
auto fixedPos = getPosition ( true ) + visitableOffset ;
vstd : : abetween ( fixedPos . x , visitableOffset . x , map . width ( ) - 1 ) ;
vstd : : abetween ( fixedPos . y , visitableOffset . y , map . height ( ) - 1 ) ;
int3 parentPos = getPosition ( true ) - getPosition ( false ) ;
setPosition ( fixedPos - parentPos ) ;
}
2022-09-29 19:07:56 +02:00
2022-08-09 07:54:32 +02:00
if ( dObject . isVisitable ( ) & & ! map . isOnMap ( dObject . visitablePos ( ) ) )
throw rmgException ( boost : : to_string ( boost : : format ( " Visitable tile %s of object %d at %s is outside the map " ) % dObject . visitablePos ( ) . toString ( ) % dObject . id % dObject . pos . toString ( ) ) ) ;
2023-02-11 18:05:02 +02:00
for ( const auto & tile : dObject . getBlockedPos ( ) )
2022-08-09 07:54:32 +02:00
{
if ( ! map . isOnMap ( tile ) )
throw rmgException ( boost : : to_string ( boost : : format ( " Tile %s of object %d at %s is outside the map " ) % tile . toString ( ) % dObject . id % dObject . pos . toString ( ) ) ) ;
}
2023-02-11 18:05:02 +02:00
for ( const auto & tile : getBlockedArea ( ) . getTilesVector ( ) )
2022-08-09 07:54:32 +02:00
{
map . setOccupied ( tile , ETileType : : ETileType : : USED ) ;
}
2023-05-19 20:30:15 +02:00
map . getMapProxy ( ) - > insertObject ( & dObject ) ;
2022-08-09 07:54:32 +02:00
}
void Object : : finalize ( RmgMap & map )
{
if ( dInstances . empty ( ) )
throw rmgException ( " Cannot finalize object without instances " ) ;
2023-02-11 18:05:02 +02:00
for ( auto & dInstance : dInstances )
2022-08-09 07:54:32 +02:00
{
2023-02-11 18:05:02 +02:00
dInstance . finalize ( map ) ;
2022-08-09 07:54:32 +02:00
}
}
void Object : : clear ( )
{
for ( auto & instance : dInstances )
instance . clear ( ) ;
dInstances . clear ( ) ;
dFullAreaCache . clear ( ) ;
dAccessibleAreaCache . clear ( ) ;
dAccessibleAreaFullCache . clear ( ) ;
}
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_END