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 18:07:15 +03: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 02:37:19 +03: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);
}*/
static void showInfoDialog ( const CGHeroInstance * h , const ui32 txtID , const ui16 soundID )
{
const PlayerColor playerID = h -> getOwner ();
showInfoDialog ( playerID , txtID , soundID );
}
///IObjectInterface
void IObjectInterface :: onHeroVisit ( const CGHeroInstance * h ) const
{}
void IObjectInterface :: onHeroLeave ( const CGHeroInstance * h ) const
{}
void IObjectInterface :: newTurn () const
{}
IObjectInterface ::~ IObjectInterface ()
{}
IObjectInterface :: IObjectInterface ()
{}
void IObjectInterface :: initObj ()
{}
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 ()
{
2016-03-12 04:41:27 +03:00
logGlobal -> traceStream () << " \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 ());
}
2016-03-12 04:41:27 +03:00
logGlobal -> traceStream () << " \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 16:24:35 +03: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 09:46:32 +02: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 16:24:35 +03:00
if ( ! handler )
{
logGlobal -> errorStream () << boost :: format (
"Unknown object type %d:%d at %s" ) % ID % subID % visitablePos ();
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 );
}
void CGObjectInstance :: initObj ()
{
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 18:01:58 +03: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 20:13:51 +02: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
}
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.
showInfoDialog ( h , 114 , soundBase :: GETPROTECTION );
}
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 02:37:19 +03:00
void CGObjectInstance :: serializeJson ( JsonSerializeFormat & handler )
2015-11-13 17:47:47 +03:00
{
2016-02-23 23:46:33 +03:00
//only save here, loading is handled by map loader
2016-02-22 02:37:19 +03:00
if ( handler . saving )
2015-11-13 17:47:47 +03:00
{
2016-02-22 02:37:19 +03:00
handler . serializeString ( "type" , typeName );
2016-02-22 23:43:57 +03:00
handler . serializeString ( "subtype" , subTypeName );
2015-11-14 16:50:29 +03:00
2016-02-23 23:46:33 +03:00
handler . serializeNumeric ( "x" , pos . x );
handler . serializeNumeric ( "y" , pos . y );
handler . serializeNumeric ( "l" , pos . z );
2016-02-22 02:37:19 +03:00
appearance . writeJson ( handler . getCurrent ()[ "template" ], false );
}
{
auto options = handler . enterStruct ( "options" );
serializeJsonOptions ( handler );
}
2016-02-23 23:46:33 +03:00
if ( handler . saving && handler . getCurrent ()[ "options" ]. Struct (). empty ())
2016-02-22 02:37:19 +03:00
{
handler . getCurrent (). Struct (). erase ( "options" );
2015-11-15 18:07:15 +03:00
}
2015-11-13 17:47:47 +03:00
}
2016-02-22 02:37:19 +03:00
void CGObjectInstance :: serializeJsonOptions ( JsonSerializeFormat & handler )
2015-11-13 17:47:47 +03:00
{
2016-02-22 02:37:19 +03:00
}
void CGObjectInstance :: serializeJsonOwner ( JsonSerializeFormat & handler )
{
std :: string temp ;
//todo: use enum serialize
if ( handler . saving )
2016-02-10 08:59:24 +03:00
{
2016-02-22 02:37:19 +03:00
if ( tempOwner . isValidPlayer ())
{
temp = GameConstants :: PLAYER_COLOR_NAMES [ tempOwner . getNum ()];
handler . serializeString ( "owner" , temp );
}
}
else
{
tempOwner = PlayerColor :: NEUTRAL ; //this method assumes that object is ownable
handler . serializeString ( "owner" , temp );
if ( temp != "" )
{
auto rawOwner = vstd :: find_pos ( GameConstants :: PLAYER_COLOR_NAMES , temp );
if ( rawOwner >= 0 )
tempOwner = PlayerColor ( rawOwner );
else
logGlobal -> errorStream () << "Invalid owner :" << temp ;
}
2016-02-10 08:59:24 +03:00
}
2015-11-13 17:47:47 +03: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 16:24:35 +03: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 16:24:35 +03: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 :
logGlobal -> errorStream () << "Shipyard without water!!! " << o -> pos << " \t " << o -> id ;
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 ));
}