2023-06-02 20:47:37 +02:00
/*
* CObjectHandler . 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 "CGObjectInstance.h"
# include "CGHeroInstance.h"
# include "ObjectTemplate.h"
2023-06-23 17:02:48 +02:00
# include "../gameState/CGameState.h"
2024-07-20 14:55:17 +02:00
# include "../texts/CGeneralTextHandler.h"
2023-06-02 20:47:37 +02:00
# include "../IGameCallback.h"
2023-08-19 23:22:31 +02:00
# include "../constants/StringConstants.h"
2023-06-02 20:47:37 +02:00
# include "../TerrainHandler.h"
# include "../mapObjectConstructors/AObjectTypeHandler.h"
# include "../mapObjectConstructors/CObjectClassesHandler.h"
# include "../mapping/CMap.h"
2023-10-23 12:59:15 +02:00
# include "../networkPacks/PacksForClient.h"
2023-06-02 20:47:37 +02:00
# include "../serializer/JsonSerializeFormat.h"
2024-06-01 17:28:17 +02:00
# include <vstd/RNG.h>
2023-06-02 21:00:44 +02:00
VCMI_LIB_NAMESPACE_BEGIN
2023-06-02 20:47:37 +02:00
//TODO: remove constructor
2024-01-01 16:37:48 +02:00
CGObjectInstance : : CGObjectInstance ( IGameCallback * cb ) :
IObjectInterface ( cb ) ,
2023-06-02 20:47:37 +02:00
pos ( - 1 , - 1 , - 1 ) ,
ID ( Obj : : NO_OBJ ) ,
subID ( - 1 ) ,
tempOwner ( PlayerColor : : UNFLAGGABLE ) ,
2023-12-06 21:49:28 +02:00
blockVisit ( false ) ,
removable ( false )
2023-06-02 20:47:37 +02:00
{
}
//must be instantiated in .cpp file for access to complete types of all member fields
CGObjectInstance : : ~ CGObjectInstance ( ) = default ;
2023-10-24 16:11:25 +02:00
MapObjectID CGObjectInstance : : getObjGroupIndex ( ) const
2023-06-02 20:47:37 +02:00
{
2023-10-24 16:11:25 +02:00
return ID ;
2023-06-02 20:47:37 +02:00
}
2023-10-24 16:11:25 +02:00
MapObjectSubID CGObjectInstance : : getObjTypeIndex ( ) const
2023-06-02 20:47:37 +02:00
{
return subID ;
}
2024-10-02 18:40:06 +02:00
int3 CGObjectInstance : : anchorPos ( ) const
2023-06-02 20:47:37 +02:00
{
return pos ;
}
int3 CGObjectInstance : : getTopVisiblePos ( ) const
{
2024-10-02 18:40:06 +02:00
return anchorPos ( ) - appearance - > getTopVisibleOffset ( ) ;
2023-06-02 20:47:37 +02:00
}
void CGObjectInstance : : setOwner ( const PlayerColor & ow )
{
tempOwner = ow ;
}
2023-09-08 17:02:36 +02:00
2024-10-02 18:40:06 +02:00
void CGObjectInstance : : setAnchorPos ( int3 newPos )
{
pos = newPos ;
}
2023-09-08 17:02:36 +02:00
int CGObjectInstance : : getWidth ( ) const
2023-06-02 20:47:37 +02:00
{
return appearance - > getWidth ( ) ;
}
2023-09-08 17:02:36 +02:00
int CGObjectInstance : : getHeight ( ) const
2023-06-02 20:47:37 +02:00
{
return appearance - > getHeight ( ) ;
}
2023-09-08 17:02:36 +02:00
bool CGObjectInstance : : visitableAt ( const int3 & testPos ) const
{
2024-10-02 18:40:06 +02:00
return anchorPos ( ) . z = = testPos . z & & appearance - > isVisitableAt ( anchorPos ( ) . x - testPos . x , anchorPos ( ) . y - testPos . y ) ;
2023-09-08 17:02:36 +02:00
}
2024-10-02 18:40:06 +02:00
2023-09-08 17:02:36 +02:00
bool CGObjectInstance : : blockingAt ( const int3 & testPos ) const
{
2024-10-02 18:40:06 +02:00
return anchorPos ( ) . z = = testPos . z & & appearance - > isBlockedAt ( anchorPos ( ) . x - testPos . x , anchorPos ( ) . y - testPos . y ) ;
2023-09-08 17:02:36 +02:00
}
bool CGObjectInstance : : coveringAt ( const int3 & testPos ) const
{
2024-10-02 18:40:06 +02:00
return anchorPos ( ) . z = = testPos . z & & appearance - > isVisibleAt ( anchorPos ( ) . x - testPos . x , anchorPos ( ) . y - testPos . y ) ;
2023-09-08 17:02:36 +02:00
}
2023-06-02 20:47:37 +02:00
std : : set < int3 > CGObjectInstance : : getBlockedPos ( ) const
{
std : : set < int3 > ret ;
for ( int w = 0 ; w < getWidth ( ) ; + + w )
{
for ( int h = 0 ; h < getHeight ( ) ; + + h )
{
if ( appearance - > isBlockedAt ( w , h ) )
2024-10-02 18:40:06 +02:00
ret . insert ( int3 ( anchorPos ( ) . x - w , anchorPos ( ) . y - h , anchorPos ( ) . z ) ) ;
2023-06-02 20:47:37 +02:00
}
}
return ret ;
}
2023-11-07 17:30:16 +02:00
const std : : set < int3 > & CGObjectInstance : : getBlockedOffsets ( ) const
2023-06-02 20:47:37 +02:00
{
return appearance - > getBlockedOffsets ( ) ;
}
2023-10-26 15:50:29 +02:00
void CGObjectInstance : : setType ( MapObjectID newID , MapObjectSubID newSubID )
2023-06-02 20:47:37 +02:00
{
auto position = visitablePos ( ) ;
auto oldOffset = getVisitableOffset ( ) ;
auto & tile = cb - > gameState ( ) - > map - > getTile ( position ) ;
//recalculate blockvis tiles - new appearance might have different blockmap than before
cb - > gameState ( ) - > map - > removeBlockVisTiles ( this , true ) ;
2023-06-17 17:07:25 +02:00
auto handler = VLC - > objtypeh - > getHandlerFor ( newID , newSubID ) ;
2024-04-22 15:40:43 +02:00
2024-07-13 20:37:13 +02:00
if ( ! handler - > getTemplates ( tile . getTerrainID ( ) ) . empty ( ) )
2023-06-02 20:47:37 +02:00
{
2024-07-13 20:37:13 +02:00
appearance = handler - > getTemplates ( tile . getTerrainID ( ) ) [ 0 ] ;
2023-06-02 20:47:37 +02:00
}
else
{
2024-07-13 20:37:13 +02:00
logGlobal - > warn ( " Object %d:%d at %s has no templates suitable for terrain %s " , newID , newSubID , visitablePos ( ) . toString ( ) , tile . getTerrain ( ) - > getNameTranslated ( ) ) ;
2023-06-02 20:47:37 +02:00
appearance = handler - > getTemplates ( ) [ 0 ] ; // get at least some appearance since alternative is crash
}
2023-06-17 17:07:25 +02:00
bool needToAdjustOffset = false ;
// FIXME: potentially unused code - setType is NOT called when releasing hero from prison
// instead, appearance update & pos adjustment occurs in GiveHero::applyGs
needToAdjustOffset | = this - > ID = = Obj : : PRISON & & newID = = Obj : : HERO ;
needToAdjustOffset | = newID = = Obj : : MONSTER ;
if ( needToAdjustOffset )
2023-06-02 20:47:37 +02:00
{
2023-06-17 17:07:25 +02:00
// adjust position since object visitable offset might have changed
2023-06-02 20:47:37 +02:00
auto newOffset = getVisitableOffset ( ) ;
pos = pos - oldOffset + newOffset ;
}
2023-06-17 17:07:25 +02:00
this - > ID = Obj ( newID ) ;
this - > subID = newSubID ;
2023-06-02 20:47:37 +02:00
cb - > gameState ( ) - > map - > addBlockVisTiles ( this ) ;
}
2024-06-01 17:28:17 +02:00
void CGObjectInstance : : pickRandomObject ( vstd : : RNG & rand )
2023-10-25 12:50:11 +02:00
{
// no-op
}
2024-06-01 17:28:17 +02:00
void CGObjectInstance : : initObj ( vstd : : RNG & rand )
2023-06-02 20:47:37 +02:00
{
2023-12-06 21:49:28 +02:00
// no-op
2023-06-02 20:47:37 +02:00
}
2023-11-06 18:27:16 +02:00
void CGObjectInstance : : setProperty ( ObjProperty what , ObjPropertyID identifier )
2023-06-02 20:47:37 +02:00
{
2023-11-06 18:27:16 +02:00
setPropertyDer ( what , identifier ) ; // call this before any actual changes (needed at least for dwellings)
2023-06-02 20:47:37 +02:00
switch ( what )
{
case ObjProperty : : OWNER :
2023-11-06 18:27:16 +02:00
tempOwner = identifier . as < PlayerColor > ( ) ;
2023-06-02 20:47:37 +02:00
break ;
case ObjProperty : : BLOCKVIS :
2023-12-06 21:49:28 +02:00
// Never actually used in code, but possible in ERM
2023-11-06 18:27:16 +02:00
blockVisit = identifier . getNum ( ) ;
2023-06-02 20:47:37 +02:00
break ;
case ObjProperty : : ID :
2023-11-06 18:27:16 +02:00
ID = identifier . as < MapObjectID > ( ) ;
2023-06-02 20:47:37 +02:00
break ;
}
}
2023-10-24 16:11:25 +02:00
TObjectTypeHandler CGObjectInstance : : getObjectHandler ( ) const
{
return VLC - > objtypeh - > getHandlerFor ( ID , subID ) ;
}
2024-10-13 15:05:50 +02:00
std : : string CGObjectInstance : : getTypeName ( ) const
{
return getObjectHandler ( ) - > getTypeName ( ) ;
}
std : : string CGObjectInstance : : getSubtypeName ( ) const
{
return getObjectHandler ( ) - > getSubTypeName ( ) ;
}
2023-11-06 18:27:16 +02:00
void CGObjectInstance : : setPropertyDer ( ObjProperty what , ObjPropertyID identifier )
2023-06-02 20:47:37 +02:00
{ }
int3 CGObjectInstance : : getSightCenter ( ) const
{
return visitablePos ( ) ;
}
int CGObjectInstance : : getSightRadius ( ) const
{
return 3 ;
}
int3 CGObjectInstance : : getVisitableOffset ( ) const
{
2024-10-02 18:40:06 +02:00
if ( ! isVisitable ( ) )
2024-10-11 17:02:14 +02:00
logGlobal - > debug ( " Attempt to access visitable offset on a non-visitable object! " ) ;
2023-06-02 20:47:37 +02:00
return appearance - > getVisitableOffset ( ) ;
}
void CGObjectInstance : : giveDummyBonus ( const ObjectInstanceID & heroID , BonusDuration : : Type duration ) const
{
GiveBonus gbonus ;
gbonus . bonus . type = BonusType : : NONE ;
2023-11-06 18:27:16 +02:00
gbonus . id = heroID ;
2023-06-02 20:47:37 +02:00
gbonus . bonus . duration = duration ;
2023-10-21 14:06:18 +02:00
gbonus . bonus . source = BonusSource : : OBJECT_TYPE ;
2023-10-21 13:50:42 +02:00
gbonus . bonus . sid = BonusSourceID ( ID ) ;
2023-06-02 20:47:37 +02:00
cb - > giveHeroBonus ( & gbonus ) ;
}
std : : string CGObjectInstance : : getObjectName ( ) const
{
return VLC - > objtypeh - > getObjectName ( ID , subID ) ;
}
2024-06-01 17:28:17 +02:00
std : : optional < AudioPath > CGObjectInstance : : getAmbientSound ( vstd : : RNG & rng ) const
2023-06-02 20:47:37 +02:00
{
const auto & sounds = VLC - > objtypeh - > getObjectSounds ( ID , subID ) . ambient ;
if ( ! sounds . empty ( ) )
return sounds . front ( ) ; // TODO: Support randomization of ambient sounds
return std : : nullopt ;
}
2024-06-01 17:28:17 +02:00
std : : optional < AudioPath > CGObjectInstance : : getVisitSound ( vstd : : RNG & rng ) const
2023-06-02 20:47:37 +02:00
{
const auto & sounds = VLC - > objtypeh - > getObjectSounds ( ID , subID ) . visit ;
if ( ! sounds . empty ( ) )
2024-06-01 17:28:17 +02:00
return * RandomGeneratorUtil : : nextItem ( sounds , rng ) ;
2023-06-02 20:47:37 +02:00
return std : : nullopt ;
}
2024-06-01 17:28:17 +02:00
std : : optional < AudioPath > CGObjectInstance : : getRemovalSound ( vstd : : RNG & rng ) const
2023-06-02 20:47:37 +02:00
{
const auto & sounds = VLC - > objtypeh - > getObjectSounds ( ID , subID ) . removal ;
if ( ! sounds . empty ( ) )
2024-06-01 17:28:17 +02:00
return * RandomGeneratorUtil : : nextItem ( sounds , rng ) ;
2023-06-02 20:47:37 +02:00
return std : : nullopt ;
}
std : : string CGObjectInstance : : getHoverText ( PlayerColor player ) const
{
auto text = getObjectName ( ) ;
if ( tempOwner . isValidPlayer ( ) )
text + = " \n " + VLC - > generaltexth - > arraytxt [ 23 + tempOwner . getNum ( ) ] ;
return text ;
}
std : : string CGObjectInstance : : getHoverText ( const CGHeroInstance * hero ) const
{
return getHoverText ( hero - > tempOwner ) ;
}
2023-10-19 13:36:11 +02:00
std : : string CGObjectInstance : : getPopupText ( PlayerColor player ) const
{
return getHoverText ( player ) ;
}
std : : string CGObjectInstance : : getPopupText ( const CGHeroInstance * hero ) const
{
return getHoverText ( hero ) ;
}
2023-10-16 22:55:37 +02:00
std : : vector < Component > CGObjectInstance : : getPopupComponents ( PlayerColor player ) const
{
return { } ;
}
std : : vector < Component > CGObjectInstance : : getPopupComponents ( const CGHeroInstance * hero ) const
{
2023-10-19 14:41:59 +02:00
return getPopupComponents ( hero - > getOwner ( ) ) ;
2023-10-16 22:55:37 +02:00
}
2023-06-02 20:47:37 +02:00
void CGObjectInstance : : onHeroVisit ( const CGHeroInstance * h ) const
{
2023-11-02 16:29:59 +02:00
switch ( ID . toEnum ( ) )
2023-06-02 20:47:37 +02:00
{
case Obj : : SANCTUARY :
{
//You enter the sanctuary and immediately feel as if a great weight has been lifted off your shoulders. You feel safe here.
h - > showInfoDialog ( 114 ) ;
}
break ;
case Obj : : TAVERN :
{
2023-09-28 00:17:05 +02:00
cb - > showObjectWindow ( this , EOpenWindowMode : : TAVERN_WINDOW , h , true ) ;
2023-06-02 20:47:37 +02:00
}
break ;
}
}
int3 CGObjectInstance : : visitablePos ( ) const
{
2024-10-02 18:40:06 +02:00
if ( ! isVisitable ( ) )
2024-10-11 17:02:14 +02:00
logGlobal - > debug ( " Attempt to access visitable position on a non-visitable object! " ) ;
2024-10-02 18:40:06 +02:00
2023-06-02 20:47:37 +02:00
return pos - getVisitableOffset ( ) ;
}
bool CGObjectInstance : : isVisitable ( ) const
{
return appearance - > isVisitable ( ) ;
}
2023-06-21 15:49:44 +02:00
bool CGObjectInstance : : isBlockedVisitable ( ) const
{
2023-12-06 21:49:28 +02:00
// TODO: Read from json
2023-06-21 15:49:44 +02:00
return blockVisit ;
}
2023-12-06 21:49:28 +02:00
bool CGObjectInstance : : isRemovable ( ) const
{
// TODO: Read from json
return removable ;
}
2023-06-21 15:49:44 +02:00
bool CGObjectInstance : : isCoastVisitable ( ) const
{
return false ;
}
2023-06-02 20:47:37 +02:00
bool CGObjectInstance : : passableFor ( PlayerColor color ) const
{
return false ;
}
void CGObjectInstance : : updateFrom ( const JsonNode & data )
{
}
void CGObjectInstance : : serializeJson ( JsonSerializeFormat & handler )
{
//only save here, loading is handled by map loader
if ( handler . saving )
{
2024-10-13 15:06:31 +02:00
std : : string ourTypeName = getTypeName ( ) ;
std : : string ourSubtypeName = getSubtypeName ( ) ;
handler . serializeString ( " type " , ourTypeName ) ;
handler . serializeString ( " subtype " , ourSubtypeName ) ;
2023-06-02 20:47:37 +02:00
handler . serializeInt ( " x " , pos . x ) ;
handler . serializeInt ( " y " , pos . y ) ;
handler . serializeInt ( " l " , pos . z ) ;
JsonNode app ;
appearance - > writeJson ( app , false ) ;
handler . serializeRaw ( " template " , app , std : : nullopt ) ;
}
{
auto options = handler . enterStruct ( " options " ) ;
serializeJsonOptions ( handler ) ;
}
}
void CGObjectInstance : : afterAddToMap ( CMap * map )
{
//nothing here
}
void CGObjectInstance : : afterRemoveFromMap ( CMap * map )
{
//nothing here
}
void CGObjectInstance : : serializeJsonOptions ( JsonSerializeFormat & handler )
{
//nothing here
}
void CGObjectInstance : : serializeJsonOwner ( JsonSerializeFormat & handler )
{
2023-09-04 21:21:57 +02:00
handler . serializeId ( " owner " , tempOwner , PlayerColor : : NEUTRAL ) ;
2023-06-02 20:47:37 +02:00
}
BattleField CGObjectInstance : : getBattlefield ( ) const
{
return VLC - > objtypeh - > getHandlerFor ( ID , subID ) - > getBattlefield ( ) ;
}
2023-06-02 21:00:44 +02:00
2024-08-24 22:42:19 +02:00
const IOwnableObject * CGObjectInstance : : asOwnable ( ) const
{
return nullptr ;
}
2023-06-02 21:00:44 +02:00
VCMI_LIB_NAMESPACE_END