2015-12-02 21:05:10 +02:00
/*
2014-06-05 19:57:43 +03: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 "CObjectHandler.h"
2014-06-05 20:26:50 +03:00
# include "../NetPacks.h"
# include "../CGeneralTextHandler.h"
# include "../CHeroHandler.h"
2014-06-05 23:51:24 +03:00
# include "../CSoundBase.h"
2014-06-25 17:11:07 +03:00
# include "../filesystem/ResourceID.h"
# include "../IGameCallback.h"
# include "../CGameState.h"
2015-11-15 17:07:15 +02:00
# include "../StringConstants.h"
2015-12-02 21:05:10 +02:00
# include "../mapping/CMap.h"
2014-06-05 19:57:43 +03:00
# include "CObjectClassesHandler.h"
2015-12-02 21:05:10 +02:00
# include "CGTownInstance.h"
2014-06-05 19:57:43 +03:00
2016-02-22 01:37:19 +02:00
# include "../serializer/JsonSerializeFormat.h"
2014-06-05 19:57:43 +03:00
IGameCallback * IObjectInterface : : cb = nullptr ;
///helpers
static void openWindow ( const OpenWindow : : EWindow type , const int id1 , const int id2 = - 1 )
{
OpenWindow ow ;
ow . window = type ;
ow . id1 = id1 ;
ow . id2 = id2 ;
IObjectInterface : : cb - > sendAndApply ( & ow ) ;
}
static void showInfoDialog ( const PlayerColor playerID , const ui32 txtID , const ui16 soundID )
{
InfoWindow iw ;
iw . soundID = soundID ;
iw . player = playerID ;
iw . text . addTxt ( MetaString : : ADVOB_TXT , txtID ) ;
IObjectInterface : : cb - > sendAndApply ( & iw ) ;
}
/*static void showInfoDialog(const ObjectInstanceID heroID, const ui32 txtID, const ui16 soundID)
{
const PlayerColor playerID = IObjectInterface : : cb - > getOwner ( heroID ) ;
showInfoDialog ( playerID , txtID , soundID ) ;
} */
2017-09-13 02:35:58 +02:00
static void showInfoDialog ( const CGHeroInstance * h , const ui32 txtID , const ui16 soundID = 0 )
2014-06-05 19:57:43 +03:00
{
const PlayerColor playerID = h - > getOwner ( ) ;
showInfoDialog ( playerID , txtID , soundID ) ;
}
///IObjectInterface
void IObjectInterface : : onHeroVisit ( const CGHeroInstance * h ) const
{ }
void IObjectInterface : : onHeroLeave ( const CGHeroInstance * h ) const
{ }
2016-09-09 19:30:36 +02:00
void IObjectInterface : : newTurn ( CRandomGenerator & rand ) const
2014-06-05 19:57:43 +03:00
{ }
IObjectInterface : : ~ IObjectInterface ( )
{ }
IObjectInterface : : IObjectInterface ( )
{ }
2016-09-09 19:30:36 +02:00
void IObjectInterface : : initObj ( CRandomGenerator & rand )
2014-06-05 19:57:43 +03:00
{ }
void IObjectInterface : : setProperty ( ui8 what , ui32 val )
{ }
bool IObjectInterface : : wasVisited ( PlayerColor player ) const
{
return false ;
}
bool IObjectInterface : : wasVisited ( const CGHeroInstance * h ) const
{
return false ;
}
void IObjectInterface : : postInit ( )
{ }
void IObjectInterface : : preInit ( )
{ }
void IObjectInterface : : battleFinished ( const CGHeroInstance * hero , const BattleResult & result ) const
{ }
void IObjectInterface : : blockingDialogAnswered ( const CGHeroInstance * hero , ui32 answer ) const
{ }
void IObjectInterface : : garrisonDialogClosed ( const CGHeroInstance * hero ) const
{ }
void IObjectInterface : : heroLevelUpDone ( const CGHeroInstance * hero ) const
{ }
CObjectHandler : : CObjectHandler ( )
{
2017-08-10 18:39:27 +02:00
logGlobal - > trace ( " \t \t Reading resources prices " ) ;
2014-06-05 19:57:43 +03:00
const JsonNode config2 ( ResourceID ( " config/resources.json " ) ) ;
for ( const JsonNode & price : config2 [ " resources_prices " ] . Vector ( ) )
{
resVals . push_back ( price . Float ( ) ) ;
}
2017-08-10 18:39:27 +02:00
logGlobal - > trace ( " \t \t Done loading resource prices! " ) ;
2014-06-05 19:57:43 +03:00
}
PlayerColor CGObjectInstance : : getOwner ( ) const
{
//if (state)
// return state->owner;
//else
return tempOwner ; //won't have owner
}
CGObjectInstance : : CGObjectInstance ( ) :
pos ( - 1 , - 1 , - 1 ) ,
ID ( Obj : : NO_OBJ ) ,
subID ( - 1 ) ,
tempOwner ( PlayerColor : : UNFLAGGABLE ) ,
blockVisit ( false )
{
}
CGObjectInstance : : ~ CGObjectInstance ( )
{
}
void CGObjectInstance : : setOwner ( PlayerColor ow )
{
2014-06-24 02:26:36 +03:00
tempOwner = ow ;
2014-06-05 19:57:43 +03:00
}
int CGObjectInstance : : getWidth ( ) const //returns width of object graphic in tiles
{
return appearance . getWidth ( ) ;
}
int CGObjectInstance : : getHeight ( ) const //returns height of object graphic in tiles
{
return appearance . getHeight ( ) ;
}
bool CGObjectInstance : : visitableAt ( int x , int y ) const //returns true if object is visitable at location (x, y) form left top tile of image (x, y in tiles)
{
return appearance . isVisitableAt ( pos . x - x , pos . y - y ) ;
}
bool CGObjectInstance : : blockingAt ( int x , int y ) const
{
return appearance . isBlockedAt ( pos . x - x , pos . y - y ) ;
}
bool CGObjectInstance : : coveringAt ( int x , int y ) const
{
return appearance . isVisibleAt ( pos . x - x , pos . y - y ) ;
}
std : : set < int3 > CGObjectInstance : : getBlockedPos ( ) const
{
std : : set < int3 > ret ;
for ( int w = 0 ; w < getWidth ( ) ; + + w )
{
for ( int h = 0 ; h < getHeight ( ) ; + + h )
{
2016-01-18 15:24:35 +02:00
if ( appearance . isBlockedAt ( w , h ) )
2014-06-05 19:57:43 +03:00
ret . insert ( int3 ( pos . x - w , pos . y - h , pos . z ) ) ;
}
}
return ret ;
}
std : : set < int3 > CGObjectInstance : : getBlockedOffsets ( ) const
{
2014-06-28 10:46:32 +03:00
return appearance . getBlockedOffsets ( ) ;
2014-06-05 19:57:43 +03:00
}
void CGObjectInstance : : setType ( si32 ID , si32 subID )
{
const TerrainTile & tile = cb - > gameState ( ) - > map - > getTile ( visitablePos ( ) ) ;
this - > ID = Obj ( ID ) ;
this - > subID = subID ;
//recalculate blockvis tiles - new appearance might have different blockmap than before
cb - > gameState ( ) - > map - > removeBlockVisTiles ( this , true ) ;
2014-06-17 14:57:47 +03:00
auto handler = VLC - > objtypeh - > getHandlerFor ( ID , subID ) ;
2016-01-18 15:24:35 +02:00
if ( ! handler )
{
2017-08-11 19:03:05 +02:00
logGlobal - > error ( " Unknown object type %d:%d at %s " , ID , subID , visitablePos ( ) . toString ( ) ) ;
2016-01-18 15:24:35 +02:00
return ;
}
if ( ! handler - > getTemplates ( tile . terType ) . empty ( ) )
2014-08-09 13:48:38 +03:00
appearance = handler - > getTemplates ( tile . terType ) [ 0 ] ;
else
appearance = handler - > getTemplates ( ) [ 0 ] ; // get at least some appearance since alternative is crash
2014-06-05 19:57:43 +03:00
cb - > gameState ( ) - > map - > addBlockVisTiles ( this ) ;
}
2016-09-09 19:30:36 +02:00
void CGObjectInstance : : initObj ( CRandomGenerator & rand )
2014-06-05 19:57:43 +03:00
{
switch ( ID )
{
case Obj : : TAVERN :
blockVisit = true ;
break ;
}
}
void CGObjectInstance : : setProperty ( ui8 what , ui32 val )
{
2014-06-24 20:39:36 +03:00
setPropertyDer ( what , val ) ; // call this before any actual changes (needed at least for dwellings)
2014-06-05 19:57:43 +03:00
switch ( what )
{
case ObjProperty : : OWNER :
tempOwner = PlayerColor ( val ) ;
break ;
case ObjProperty : : BLOCKVIS :
blockVisit = val ;
break ;
case ObjProperty : : ID :
ID = Obj ( val ) ;
break ;
case ObjProperty : : SUBID :
subID = val ;
break ;
}
}
void CGObjectInstance : : setPropertyDer ( ui8 what , ui32 val )
{ }
int3 CGObjectInstance : : getSightCenter ( ) const
{
2014-06-24 14:50:27 +03:00
return visitablePos ( ) ;
2014-06-05 19:57:43 +03:00
}
2016-01-31 17:01:58 +02:00
int CGObjectInstance : : getSightRadius ( ) const
2014-06-05 19:57:43 +03:00
{
return 3 ;
}
2014-06-24 02:26:36 +03:00
2014-06-05 19:57:43 +03:00
int3 CGObjectInstance : : getVisitableOffset ( ) const
{
2014-07-08 21:13:51 +03:00
return appearance . getVisitableOffset ( ) ;
2014-06-05 19:57:43 +03:00
}
void CGObjectInstance : : giveDummyBonus ( ObjectInstanceID heroID , ui8 duration ) const
{
GiveBonus gbonus ;
gbonus . bonus . type = Bonus : : NONE ;
gbonus . id = heroID . getNum ( ) ;
gbonus . bonus . duration = duration ;
gbonus . bonus . source = Bonus : : OBJECT ;
gbonus . bonus . sid = ID ;
cb - > giveHeroBonus ( & gbonus ) ;
}
2014-06-24 20:39:36 +03:00
std : : string CGObjectInstance : : getObjectName ( ) const
{
2014-06-30 00:16:45 +03:00
return VLC - > objtypeh - > getObjectName ( ID , subID ) ;
2014-06-24 20:39:36 +03:00
}
2017-09-13 02:35:58 +02:00
boost : : optional < std : : string > CGObjectInstance : : getAmbientSound ( ) const
{
const auto & sounds = VLC - > objtypeh - > getObjectSounds ( ID , subID ) . ambient ;
if ( sounds . size ( ) )
return sounds . front ( ) ; // TODO: Support randomization of ambient sounds
return boost : : none ;
}
boost : : optional < std : : string > CGObjectInstance : : getVisitSound ( ) const
{
const auto & sounds = VLC - > objtypeh - > getObjectSounds ( ID , subID ) . visit ;
if ( sounds . size ( ) )
return * RandomGeneratorUtil : : nextItem ( sounds , CRandomGenerator : : getDefault ( ) ) ;
return boost : : none ;
}
boost : : optional < std : : string > CGObjectInstance : : getRemovalSound ( ) const
{
const auto & sounds = VLC - > objtypeh - > getObjectSounds ( ID , subID ) . removal ;
if ( sounds . size ( ) )
return * RandomGeneratorUtil : : nextItem ( sounds , CRandomGenerator : : getDefault ( ) ) ;
return boost : : none ;
}
2014-06-24 20:39:36 +03:00
std : : string CGObjectInstance : : getHoverText ( PlayerColor player ) const
{
return getObjectName ( ) ;
}
std : : string CGObjectInstance : : getHoverText ( const CGHeroInstance * hero ) const
{
return getHoverText ( hero - > tempOwner ) ;
}
2014-06-05 19:57:43 +03:00
void CGObjectInstance : : onHeroVisit ( const CGHeroInstance * h ) const
{
switch ( ID )
{
case Obj : : HILL_FORT :
{
openWindow ( OpenWindow : : HILL_FORT_WINDOW , id . getNum ( ) , h - > id . getNum ( ) ) ;
}
break ;
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.
2017-09-13 02:35:58 +02:00
showInfoDialog ( h , 114 ) ;
2014-06-05 19:57:43 +03:00
}
break ;
case Obj : : TAVERN :
{
openWindow ( OpenWindow : : TAVERN_WINDOW , h - > id . getNum ( ) , id . getNum ( ) ) ;
}
break ;
}
}
int3 CGObjectInstance : : visitablePos ( ) const
{
return pos - getVisitableOffset ( ) ;
}
bool CGObjectInstance : : isVisitable ( ) const
{
return appearance . isVisitable ( ) ;
}
bool CGObjectInstance : : passableFor ( PlayerColor color ) const
{
2014-06-24 02:26:36 +03:00
return false ;
2014-06-05 19:57:43 +03:00
}
2016-02-22 01:37:19 +02:00
void CGObjectInstance : : serializeJson ( JsonSerializeFormat & handler )
2015-11-13 16:47:47 +02:00
{
2016-02-23 22:46:33 +02:00
//only save here, loading is handled by map loader
2016-02-22 01:37:19 +02:00
if ( handler . saving )
{
handler . serializeString ( " type " , typeName ) ;
2016-02-22 22:43:57 +02:00
handler . serializeString ( " subtype " , subTypeName ) ;
2015-11-13 16:47:47 +02:00
2016-11-13 12:38:42 +02:00
handler . serializeInt ( " x " , pos . x ) ;
handler . serializeInt ( " y " , pos . y ) ;
handler . serializeInt ( " l " , pos . z ) ;
2016-02-22 01:37:19 +02:00
appearance . writeJson ( handler . getCurrent ( ) [ " template " ] , false ) ;
}
2015-11-14 15:50:29 +02:00
2016-02-22 01:37:19 +02:00
{
auto options = handler . enterStruct ( " options " ) ;
serializeJsonOptions ( handler ) ;
}
2015-11-14 15:50:29 +02:00
2016-02-23 22:46:33 +02:00
if ( handler . saving & & handler . getCurrent ( ) [ " options " ] . Struct ( ) . empty ( ) )
2016-02-22 01:37:19 +02:00
{
handler . getCurrent ( ) . Struct ( ) . erase ( " options " ) ;
}
2015-11-13 16:47:47 +02:00
}
2017-05-28 15:23:42 +02:00
void CGObjectInstance : : afterAddToMap ( CMap * map )
2015-11-13 16:47:47 +02:00
{
2017-05-28 15:23:42 +02:00
//nothing here
}
2016-01-23 18:53:02 +02:00
2017-05-28 15:23:42 +02:00
void CGObjectInstance : : serializeJsonOptions ( JsonSerializeFormat & handler )
{
//nothing here
2016-01-23 18:53:02 +02:00
}
2016-02-22 01:37:19 +02:00
void CGObjectInstance : : serializeJsonOwner ( JsonSerializeFormat & handler )
2016-01-23 18:53:02 +02:00
{
2016-11-13 12:38:42 +02:00
ui8 temp = tempOwner . getNum ( ) ;
2016-02-22 01:37:19 +02:00
2016-11-13 12:38:42 +02:00
handler . serializeEnum ( " owner " , temp , PlayerColor : : NEUTRAL . getNum ( ) , GameConstants : : PLAYER_COLOR_NAMES ) ;
2016-02-22 01:37:19 +02:00
2016-11-13 12:38:42 +02:00
if ( ! handler . saving )
tempOwner = PlayerColor ( temp ) ;
2015-11-13 16:47:47 +02:00
}
2014-06-05 19:57:43 +03:00
CGObjectInstanceBySubIdFinder : : CGObjectInstanceBySubIdFinder ( CGObjectInstance * obj ) : obj ( obj )
{
}
bool CGObjectInstanceBySubIdFinder : : operator ( ) ( CGObjectInstance * obj ) const
{
return this - > obj - > subID = = obj - > subID ;
}
int3 IBoatGenerator : : bestLocation ( ) const
{
std : : vector < int3 > offsets ;
getOutOffsets ( offsets ) ;
for ( auto & offset : offsets )
{
2016-01-18 15:24:35 +02:00
if ( const TerrainTile * tile = IObjectInterface : : cb - > getTile ( o - > pos + offset , false ) ) //tile is in the map
2014-06-05 19:57:43 +03:00
{
2016-01-18 15:24:35 +02:00
if ( tile - > terType = = ETerrainType : : WATER & & ( ! tile - > blocked | | tile - > blockingObjects . front ( ) - > ID = = Obj : : BOAT ) ) //and is water and is not blocked or is blocked by boat
2014-06-05 19:57:43 +03:00
return o - > pos + offset ;
}
}
return int3 ( - 1 , - 1 , - 1 ) ;
}
IBoatGenerator : : EGeneratorState IBoatGenerator : : shipyardStatus ( ) const
{
int3 tile = bestLocation ( ) ;
const TerrainTile * t = IObjectInterface : : cb - > getTile ( tile ) ;
if ( ! t )
return TILE_BLOCKED ; //no available water
else if ( ! t - > blockingObjects . size ( ) )
return GOOD ; //OK
else if ( t - > blockingObjects . front ( ) - > ID = = Obj : : BOAT )
return BOAT_ALREADY_BUILT ; //blocked with boat
else
return TILE_BLOCKED ; //blocked
}
int IBoatGenerator : : getBoatType ( ) const
{
//We make good ships by default
return 1 ;
}
IBoatGenerator : : IBoatGenerator ( const CGObjectInstance * O )
: o ( O )
{
}
void IBoatGenerator : : getProblemText ( MetaString & out , const CGHeroInstance * visitor ) const
{
switch ( shipyardStatus ( ) )
{
case BOAT_ALREADY_BUILT :
out . addTxt ( MetaString : : GENERAL_TXT , 51 ) ;
break ;
case TILE_BLOCKED :
if ( visitor )
{
out . addTxt ( MetaString : : GENERAL_TXT , 134 ) ;
out . addReplacement ( visitor - > name ) ;
}
else
out . addTxt ( MetaString : : ADVOB_TXT , 189 ) ;
break ;
case NO_WATER :
2017-08-11 19:03:05 +02:00
logGlobal - > error ( " Shipyard without water! %s \t %d " , o - > pos . toString ( ) , o - > id . getNum ( ) ) ;
2014-06-05 19:57:43 +03:00
return ;
}
}
void IShipyard : : getBoatCost ( std : : vector < si32 > & cost ) const
{
cost . resize ( GameConstants : : RESOURCE_QUANTITY ) ;
cost [ Res : : WOOD ] = 10 ;
cost [ Res : : GOLD ] = 1000 ;
}
IShipyard : : IShipyard ( const CGObjectInstance * O )
: IBoatGenerator ( O )
{
}
IShipyard * IShipyard : : castFrom ( CGObjectInstance * obj )
{
if ( ! obj )
return nullptr ;
if ( obj - > ID = = Obj : : TOWN )
{
return static_cast < CGTownInstance * > ( obj ) ;
}
else if ( obj - > ID = = Obj : : SHIPYARD )
{
return static_cast < CGShipyard * > ( obj ) ;
}
else
{
return nullptr ;
}
}
const IShipyard * IShipyard : : castFrom ( const CGObjectInstance * obj )
{
return castFrom ( const_cast < CGObjectInstance * > ( obj ) ) ;
}