2014-06-05 19:52:14 +03:00
/*
2014-06-05 20:26:50 +03:00
* MiscObjects . cpp , part of VCMI engine
2014-06-05 19:52:14 +03:00
*
* 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 "MiscObjects.h"
2016-01-23 18:53:02 +02:00
# include "../StringConstants.h"
2014-06-05 20:26:50 +03:00
# include "../NetPacks.h"
# include "../CGeneralTextHandler.h"
2014-06-05 23:51:24 +03:00
# include "../CSoundBase.h"
2014-06-25 17:11:07 +03:00
# include "../CModHandler.h"
2014-06-05 19:52:14 +03:00
# include "CObjectClassesHandler.h"
2015-02-02 10:25:26 +02:00
# include "../spells/CSpellHandler.h"
2014-06-25 17:11:07 +03:00
# include "../IGameCallback.h"
# include "../CGameState.h"
2015-12-02 21:05:10 +02:00
# include "../mapping/CMap.h"
2015-12-02 21:39:53 +02:00
# include "../CPlayerState.h"
2014-06-05 19:52:14 +03:00
std : : map < si32 , std : : vector < ObjectInstanceID > > CGMagi : : eyelist ;
2016-01-20 11:43:07 +02:00
ui8 CGObelisk : : obeliskCount = 0 ; //how many obelisks are on map
2014-06-05 19:52:14 +03:00
std : : map < TeamID , ui8 > CGObelisk : : visited ; //map: team_id => how many obelisks has been visited
///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 CGHeroInstance * h , const ui32 txtID , const ui16 soundID )
{
const PlayerColor playerID = h - > getOwner ( ) ;
showInfoDialog ( playerID , txtID , soundID ) ;
}
static std : : string & visitedTxt ( const bool visited )
{
int id = visited ? 352 : 353 ;
return VLC - > generaltexth - > allTexts [ id ] ;
}
void CPlayersVisited : : setPropertyDer ( ui8 what , ui32 val )
{
2016-01-20 09:44:13 +02:00
if ( what = = CPlayersVisited : : OBJPROP_VISITED )
2014-06-05 19:52:14 +03:00
players . insert ( PlayerColor ( val ) ) ;
}
bool CPlayersVisited : : wasVisited ( PlayerColor player ) const
{
return vstd : : contains ( players , player ) ;
}
bool CPlayersVisited : : wasVisited ( TeamID team ) const
{
for ( auto i : players )
{
if ( cb - > getPlayer ( i ) - > team = = team )
return true ;
}
return false ;
}
2014-06-24 20:39:36 +03:00
std : : string CGCreature : : getHoverText ( PlayerColor player ) const
2014-06-05 19:52:14 +03:00
{
if ( stacks . empty ( ) )
{
//should not happen...
logGlobal - > errorStream ( ) < < " Invalid stack at tile " < < pos < < " : subID= " < < subID < < " ; id= " < < id ;
2014-06-24 20:39:36 +03:00
return " !!!INVALID_STACK!!! " ;
2014-06-05 19:52:14 +03:00
}
2014-06-24 20:39:36 +03:00
std : : string hoverName ;
2014-06-05 19:52:14 +03:00
MetaString ms ;
int pom = stacks . begin ( ) - > second - > getQuantityID ( ) ;
pom = 172 + 3 * pom ;
ms . addTxt ( MetaString : : ARRAY_TXT , pom ) ;
ms < < " " ;
ms . addTxt ( MetaString : : CRE_PL_NAMES , subID ) ;
ms . toString ( hoverName ) ;
2014-06-24 20:39:36 +03:00
return hoverName ;
}
2014-06-05 19:52:14 +03:00
2014-06-24 20:39:36 +03:00
std : : string CGCreature : : getHoverText ( const CGHeroInstance * hero ) const
{
2015-02-06 14:42:09 +02:00
std : : string hoverName ;
2015-02-06 16:36:09 +02:00
if ( hero - > hasVisions ( this , 0 ) )
2015-11-16 15:30:40 +02:00
{
2015-02-06 14:42:09 +02:00
MetaString ms ;
ms < < stacks . begin ( ) - > second - > count ;
ms < < " " ;
ms . addTxt ( MetaString : : CRE_PL_NAMES , subID ) ;
2015-11-16 15:30:40 +02:00
2015-02-06 18:07:03 +02:00
ms < < " \n " ;
2015-11-16 15:30:40 +02:00
2015-02-06 18:07:03 +02:00
int decision = takenAction ( hero , true ) ;
2015-11-16 15:30:40 +02:00
2015-02-06 18:07:03 +02:00
switch ( decision )
{
case FIGHT :
ms . addTxt ( MetaString : : GENERAL_TXT , 246 ) ;
break ;
case FLEE :
ms . addTxt ( MetaString : : GENERAL_TXT , 245 ) ;
break ;
case JOIN_FOR_FREE :
ms . addTxt ( MetaString : : GENERAL_TXT , 243 ) ;
2015-11-16 15:30:40 +02:00
break ;
2015-02-06 18:07:03 +02:00
default : //decision = cost in gold
VLC - > generaltexth - > allTexts [ 244 ] ;
2015-11-16 15:30:40 +02:00
ms < < boost : : to_string ( boost : : format ( VLC - > generaltexth - > allTexts [ 244 ] ) % decision ) ;
2015-02-06 18:07:03 +02:00
break ;
2015-11-16 15:30:40 +02:00
}
2015-02-06 18:07:03 +02:00
2015-11-16 15:30:40 +02:00
ms . toString ( hoverName ) ;
2015-02-06 14:42:09 +02:00
}
else
{
2015-11-16 15:30:40 +02:00
hoverName = getHoverText ( hero - > tempOwner ) ;
}
2014-06-24 20:39:36 +03:00
const JsonNode & texts = VLC - > generaltexth - > localizedTexts [ " adventureMap " ] [ " monsterThreat " ] ;
hoverName + = texts [ " title " ] . String ( ) ;
int choice ;
double ratio = ( ( double ) getArmyStrength ( ) / hero - > getTotalStrength ( ) ) ;
if ( ratio < 0.1 ) choice = 0 ;
else if ( ratio < 0.25 ) choice = 1 ;
else if ( ratio < 0.6 ) choice = 2 ;
else if ( ratio < 0.9 ) choice = 3 ;
else if ( ratio < 1.1 ) choice = 4 ;
else if ( ratio < 1.3 ) choice = 5 ;
else if ( ratio < 1.8 ) choice = 6 ;
else if ( ratio < 2.5 ) choice = 7 ;
else if ( ratio < 4 ) choice = 8 ;
else if ( ratio < 8 ) choice = 9 ;
else if ( ratio < 20 ) choice = 10 ;
else choice = 11 ;
hoverName + = texts [ " levels " ] . Vector ( ) [ choice ] . String ( ) ;
2014-06-05 19:52:14 +03:00
return hoverName ;
}
2014-06-24 20:39:36 +03:00
2014-06-05 19:52:14 +03:00
void CGCreature : : onHeroVisit ( const CGHeroInstance * h ) const
{
int action = takenAction ( h ) ;
switch ( action ) //decide what we do...
{
case FIGHT :
fight ( h ) ;
break ;
2015-11-25 08:34:49 +02:00
case FLEE :
2014-06-05 19:52:14 +03:00
{
flee ( h ) ;
break ;
}
case JOIN_FOR_FREE : //join for free
{
BlockingDialog ynd ( true , false ) ;
ynd . player = h - > tempOwner ;
ynd . text . addTxt ( MetaString : : ADVOB_TXT , 86 ) ;
ynd . text . addReplacement ( MetaString : : CRE_PL_NAMES , subID ) ;
cb - > showBlockingDialog ( & ynd ) ;
break ;
}
default : //join for gold
{
assert ( action > 0 ) ;
//ask if player agrees to pay gold
BlockingDialog ynd ( true , false ) ;
ynd . player = h - > tempOwner ;
std : : string tmp = VLC - > generaltexth - > advobtxt [ 90 ] ;
boost : : algorithm : : replace_first ( tmp , " %d " , boost : : lexical_cast < std : : string > ( getStackCount ( SlotID ( 0 ) ) ) ) ;
boost : : algorithm : : replace_first ( tmp , " %d " , boost : : lexical_cast < std : : string > ( action ) ) ;
boost : : algorithm : : replace_first ( tmp , " %s " , VLC - > creh - > creatures [ subID ] - > namePl ) ;
ynd . text < < tmp ;
cb - > showBlockingDialog ( & ynd ) ;
break ;
}
}
}
void CGCreature : : initObj ( )
{
blockVisit = true ;
switch ( character )
{
case 0 :
character = - 4 ;
break ;
case 1 :
character = cb - > gameState ( ) - > getRandomGenerator ( ) . nextInt ( 1 , 7 ) ;
break ;
case 2 :
character = cb - > gameState ( ) - > getRandomGenerator ( ) . nextInt ( 1 , 10 ) ;
break ;
case 3 :
character = cb - > gameState ( ) - > getRandomGenerator ( ) . nextInt ( 4 , 10 ) ;
break ;
case 4 :
character = 10 ;
break ;
}
stacks [ SlotID ( 0 ) ] - > setType ( CreatureID ( subID ) ) ;
TQuantity & amount = stacks [ SlotID ( 0 ) ] - > count ;
CCreature & c = * VLC - > creh - > creatures [ subID ] ;
if ( amount = = 0 )
{
amount = cb - > gameState ( ) - > getRandomGenerator ( ) . nextInt ( c . ammMin , c . ammMax ) ;
if ( amount = = 0 ) //armies with 0 creatures are illegal
{
logGlobal - > warnStream ( ) < < " Problem: stack " < < nodeName ( ) < < " cannot have 0 creatures. Check properties of " < < c . nodeName ( ) ;
amount = 1 ;
}
}
temppower = stacks [ SlotID ( 0 ) ] - > count * 1000 ;
refusedJoining = false ;
}
void CGCreature : : newTurn ( ) const
{ //Works only for stacks of single type of size up to 2 millions
2015-02-22 21:54:09 +02:00
if ( ! notGrowingTeam )
2014-06-05 19:52:14 +03:00
{
2015-02-22 21:54:09 +02:00
if ( stacks . begin ( ) - > second - > count < VLC - > modh - > settings . CREEP_SIZE & & cb - > getDate ( Date : : DAY_OF_WEEK ) = = 1 & & cb - > getDate ( Date : : DAY ) > 1 )
{
ui32 power = temppower * ( 100 + VLC - > modh - > settings . WEEKLY_GROWTH ) / 100 ;
cb - > setObjProperty ( id , ObjProperty : : MONSTER_COUNT , std : : min ( power / 1000 , ( ui32 ) VLC - > modh - > settings . CREEP_SIZE ) ) ; //set new amount
cb - > setObjProperty ( id , ObjProperty : : MONSTER_POWER , power ) ; //increase temppower
}
2014-06-05 19:52:14 +03:00
}
if ( VLC - > modh - > modules . STACK_EXP )
cb - > setObjProperty ( id , ObjProperty : : MONSTER_EXP , VLC - > modh - > settings . NEUTRAL_STACK_EXP ) ; //for testing purpose
}
void CGCreature : : setPropertyDer ( ui8 what , ui32 val )
{
switch ( what )
{
case ObjProperty : : MONSTER_COUNT :
stacks [ SlotID ( 0 ) ] - > count = val ;
break ;
case ObjProperty : : MONSTER_POWER :
temppower = val ;
break ;
case ObjProperty : : MONSTER_EXP :
giveStackExp ( val ) ;
break ;
case ObjProperty : : MONSTER_RESTORE_TYPE :
formation . basicType = val ;
break ;
case ObjProperty : : MONSTER_REFUSED_JOIN :
refusedJoining = val ;
break ;
}
}
int CGCreature : : takenAction ( const CGHeroInstance * h , bool allowJoin ) const
{
//calculate relative strength of hero and creatures armies
double relStrength = double ( h - > getTotalStrength ( ) ) / getArmyStrength ( ) ;
int powerFactor ;
if ( relStrength > = 7 )
powerFactor = 11 ;
else if ( relStrength > = 1 )
powerFactor = ( int ) ( 2 * ( relStrength - 1 ) ) ;
else if ( relStrength > = 0.5 )
powerFactor = - 1 ;
else if ( relStrength > = 0.333 )
powerFactor = - 2 ;
else
powerFactor = - 3 ;
std : : set < CreatureID > myKindCres ; //what creatures are the same kind as we
const CCreature * myCreature = VLC - > creh - > creatures [ subID ] ;
myKindCres . insert ( myCreature - > idNumber ) ; //we
myKindCres . insert ( myCreature - > upgrades . begin ( ) , myCreature - > upgrades . end ( ) ) ; //our upgrades
for ( ConstTransitivePtr < CCreature > & crea : VLC - > creh - > creatures )
{
if ( vstd : : contains ( crea - > upgrades , myCreature - > idNumber ) ) //it's our base creatures
myKindCres . insert ( crea - > idNumber ) ;
}
int count = 0 , //how many creatures of similar kind has hero
totalCount = 0 ;
for ( auto & elem : h - > Slots ( ) )
{
if ( vstd : : contains ( myKindCres , elem . second - > type - > idNumber ) )
count + = elem . second - > count ;
totalCount + = elem . second - > count ;
}
int sympathy = 0 ; // 0 if hero have no similar creatures
if ( count )
sympathy + + ; // 1 - if hero have at least 1 similar creature
if ( count * 2 > totalCount )
sympathy + + ; // 2 - hero have similar creatures more that 50%
int charisma = powerFactor + h - > getSecSkillLevel ( SecondarySkill : : DIPLOMACY ) + sympathy ;
2015-11-25 08:34:49 +02:00
if ( charisma < character )
return FIGHT ;
2014-06-05 19:52:14 +03:00
if ( allowJoin )
{
if ( h - > getSecSkillLevel ( SecondarySkill : : DIPLOMACY ) + sympathy + 1 > = character )
2015-11-25 08:34:49 +02:00
return JOIN_FOR_FREE ;
2014-06-05 19:52:14 +03:00
else if ( h - > getSecSkillLevel ( SecondarySkill : : DIPLOMACY ) * 2 + sympathy + 1 > = character )
return VLC - > creh - > creatures [ subID ] - > cost [ 6 ] * getStackCount ( SlotID ( 0 ) ) ; //join for gold
}
//we are still here - creatures have not joined hero, flee or fight
2015-11-25 08:34:49 +02:00
if ( charisma > character & & ! neverFlees )
return FLEE ;
2014-06-05 19:52:14 +03:00
else
2015-11-25 08:34:49 +02:00
return FIGHT ;
2014-06-05 19:52:14 +03:00
}
void CGCreature : : fleeDecision ( const CGHeroInstance * h , ui32 pursue ) const
{
if ( refusedJoining )
cb - > setObjProperty ( id , ObjProperty : : MONSTER_REFUSED_JOIN , false ) ;
if ( pursue )
{
fight ( h ) ;
}
else
{
cb - > removeObject ( this ) ;
}
}
void CGCreature : : joinDecision ( const CGHeroInstance * h , int cost , ui32 accept ) const
{
if ( ! accept )
{
2015-11-25 08:34:49 +02:00
if ( takenAction ( h , false ) = = FLEE )
2014-06-05 19:52:14 +03:00
{
cb - > setObjProperty ( id , ObjProperty : : MONSTER_REFUSED_JOIN , true ) ;
flee ( h ) ;
}
else //they fight
{
showInfoDialog ( h , 87 , 0 ) ; //Insulted by your refusal of their offer, the monsters attack!
fight ( h ) ;
}
}
else //accepted
{
if ( cb - > getResource ( h - > tempOwner , Res : : GOLD ) < cost ) //player don't have enough gold!
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
iw . text < < std : : pair < ui8 , ui32 > ( 1 , 29 ) ; //You don't have enough gold
cb - > showInfoDialog ( & iw ) ;
//act as if player refused
joinDecision ( h , cost , false ) ;
return ;
}
//take gold
if ( cost )
cb - > giveResource ( h - > tempOwner , Res : : GOLD , - cost ) ;
2016-01-15 03:29:46 +02:00
giveReward ( h ) ;
2014-06-05 19:52:14 +03:00
cb - > tryJoiningArmy ( this , h , true , true ) ;
}
}
void CGCreature : : fight ( const CGHeroInstance * h ) const
{
//split stacks
//TODO: multiple creature types in a stack?
int basicType = stacks . begin ( ) - > second - > type - > idNumber ;
cb - > setObjProperty ( id , ObjProperty : : MONSTER_RESTORE_TYPE , basicType ) ; //store info about creature stack
2014-12-25 00:53:56 +02:00
int stacksCount = getNumberOfStacks ( h ) ;
2014-12-25 11:21:39 +02:00
//source: http://heroescommunity.com/viewthread.php3?TID=27539&PID=1266335#focus
int amount = getStackCount ( SlotID ( 0 ) ) ;
int m = amount / stacksCount ;
int b = stacksCount * ( m + 1 ) - amount ;
int a = stacksCount - b ;
2014-12-25 00:53:56 +02:00
2014-06-05 19:52:14 +03:00
SlotID sourceSlot = stacks . begin ( ) - > first ;
2014-12-25 11:21:39 +02:00
for ( int slotID = 1 ; slotID < a ; + + slotID )
2014-06-05 19:52:14 +03:00
{
2014-12-25 11:21:39 +02:00
int stackSize = m + 1 ;
cb - > moveStack ( StackLocation ( this , sourceSlot ) , StackLocation ( this , SlotID ( slotID ) ) , stackSize ) ;
}
for ( int slotID = a ; slotID < stacksCount ; + + slotID )
{
int stackSize = m ;
if ( slotID ) //don't do this when a = 0 -> stack is single
cb - > moveStack ( StackLocation ( this , sourceSlot ) , StackLocation ( this , SlotID ( slotID ) ) , stackSize ) ;
2014-06-05 19:52:14 +03:00
}
if ( stacksCount > 1 )
{
2014-12-25 00:53:56 +02:00
if ( containsUpgradedStack ( ) ) //upgrade
2014-06-05 19:52:14 +03:00
{
2014-12-25 11:21:39 +02:00
SlotID slotID = SlotID ( std : : floor ( ( float ) stacks . size ( ) / 2 ) ) ;
const auto & upgrades = getStack ( slotID ) . type - > upgrades ;
2014-06-05 19:52:14 +03:00
if ( ! upgrades . empty ( ) )
{
auto it = RandomGeneratorUtil : : nextItem ( upgrades , cb - > gameState ( ) - > getRandomGenerator ( ) ) ;
2014-12-25 11:21:39 +02:00
cb - > changeStackType ( StackLocation ( this , slotID ) , VLC - > creh - > creatures [ * it ] ) ;
2014-06-05 19:52:14 +03:00
}
}
}
cb - > startBattleI ( h , this ) ;
}
void CGCreature : : flee ( const CGHeroInstance * h ) const
{
BlockingDialog ynd ( true , false ) ;
ynd . player = h - > tempOwner ;
ynd . text . addTxt ( MetaString : : ADVOB_TXT , 91 ) ;
ynd . text . addReplacement ( MetaString : : CRE_PL_NAMES , subID ) ;
cb - > showBlockingDialog ( & ynd ) ;
}
void CGCreature : : battleFinished ( const CGHeroInstance * hero , const BattleResult & result ) const
{
2016-01-26 07:41:09 +02:00
if ( result . winner = = 0 )
2014-06-05 19:52:14 +03:00
{
2016-01-15 03:29:46 +02:00
giveReward ( hero ) ;
2014-06-05 19:52:14 +03:00
cb - > removeObject ( this ) ;
}
2016-01-26 07:41:09 +02:00
else if ( result . winner > 1 ) // draw
{
// guarded reward is lost forever on draw
cb - > removeObject ( this ) ;
}
2014-06-05 19:52:14 +03:00
else
{
//merge stacks into one
TSlots : : const_iterator i ;
CCreature * cre = VLC - > creh - > creatures [ formation . basicType ] ;
2016-01-26 07:41:09 +02:00
for ( i = stacks . begin ( ) ; i ! = stacks . end ( ) ; i + + )
2014-06-05 19:52:14 +03:00
{
2016-01-26 07:41:09 +02:00
if ( cre - > isMyUpgrade ( i - > second - > type ) )
2014-06-05 19:52:14 +03:00
{
2016-01-26 07:41:09 +02:00
cb - > changeStackType ( StackLocation ( this , i - > first ) , cre ) ; //un-upgrade creatures
2014-06-05 19:52:14 +03:00
}
}
//first stack has to be at slot 0 -> if original one got killed, move there first remaining stack
if ( ! hasStackAtSlot ( SlotID ( 0 ) ) )
cb - > moveStack ( StackLocation ( this , stacks . begin ( ) - > first ) , StackLocation ( this , SlotID ( 0 ) ) , stacks . begin ( ) - > second - > count ) ;
2016-01-26 07:41:09 +02:00
while ( stacks . size ( ) > 1 ) //hopefully that's enough
2014-06-05 19:52:14 +03:00
{
// TODO it's either overcomplicated (if we assume there'll be only one stack) or buggy (if we allow multiple stacks... but that'll also cause troubles elsewhere)
i = stacks . end ( ) ;
i - - ;
SlotID slot = getSlotFor ( i - > second - > type ) ;
2016-01-26 07:41:09 +02:00
if ( slot = = i - > first ) //no reason to move stack to its own slot
2014-06-05 19:52:14 +03:00
break ;
else
2016-01-26 07:41:09 +02:00
cb - > moveStack ( StackLocation ( this , i - > first ) , StackLocation ( this , slot ) , i - > second - > count ) ;
2014-06-05 19:52:14 +03:00
}
cb - > setObjProperty ( id , ObjProperty : : MONSTER_POWER , stacks . begin ( ) - > second - > count * 1000 ) ; //remember casualties
}
}
void CGCreature : : blockingDialogAnswered ( const CGHeroInstance * hero , ui32 answer ) const
{
auto action = takenAction ( hero ) ;
if ( ! refusedJoining & & action > = JOIN_FOR_FREE ) //higher means price
joinDecision ( hero , action , answer ) ;
else if ( action ! = FIGHT )
fleeDecision ( hero , answer ) ;
else
assert ( 0 ) ;
}
2014-12-25 00:53:56 +02:00
bool CGCreature : : containsUpgradedStack ( ) const
{
//source http://heroescommunity.com/viewthread.php3?TID=27539&PID=830557#focus
float a = 2992.911117 ;
float b = 14174.264968 ;
float c = 5325.181015 ;
float d = 32788.727920 ;
int val = std : : floor ( a * pos . x + b * pos . y + c * pos . z + d ) ;
return ( ( val % 32768 ) % 100 ) < 50 ;
}
int CGCreature : : getNumberOfStacks ( const CGHeroInstance * hero ) const
{
//source http://heroescommunity.com/viewthread.php3?TID=27539&PID=1266094#focus
2014-12-25 11:21:39 +02:00
double strengthRatio = ( double ) hero - > getArmyStrength ( ) / getArmyStrength ( ) ;
2014-12-25 00:53:56 +02:00
int split = 1 ;
if ( strengthRatio < 0.5f )
split = 7 ;
else if ( strengthRatio < 0.67f )
split = 6 ;
else if ( strengthRatio < 1 )
split = 5 ;
else if ( strengthRatio < 1.5f )
split = 4 ;
else if ( strengthRatio < 2 )
split = 3 ;
else
split = 2 ;
int a = 1550811371 ;
int b = - 935900487 ;
int c = 1943276003 ;
int d = - 1120346418 ;
int R1 = a * pos . x + b * pos . y + c * pos . z + d ;
int R2 = R1 / 65536 ;
int R3 = R2 % 32768 ;
if ( R3 < 0 )
R3 + = 32767 ; //is it ever needed if we do modulo calculus?
int R4 = R3 % 100 + 1 ;
if ( R4 < = 20 )
split - = 1 ;
else if ( R4 > = 80 )
split + = 1 ;
vstd : : amin ( split , getStack ( SlotID ( 0 ) ) . count ) ; //can't divide into more stacks than creatures total
vstd : : amin ( split , 7 ) ; //can't have more than 7 stacks
return split ;
}
2016-01-15 03:29:46 +02:00
void CGCreature : : giveReward ( const CGHeroInstance * h ) const
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
if ( resources . size ( ) )
{
cb - > giveResources ( h - > tempOwner , resources ) ;
for ( int i = 0 ; i < resources . size ( ) ; i + + )
{
if ( resources [ i ] > 0 )
iw . components . push_back ( Component ( Component : : RESOURCE , i , resources [ i ] , 0 ) ) ;
}
}
if ( gainedArtifact ! = ArtifactID : : NONE )
{
cb - > giveHeroNewArtifact ( h , VLC - > arth - > artifacts [ gainedArtifact ] , ArtifactPosition : : FIRST_AVAILABLE ) ;
iw . components . push_back ( Component ( Component : : ARTIFACT , gainedArtifact , 0 , 0 ) ) ;
}
if ( iw . components . size ( ) )
{
iw . text . addTxt ( MetaString : : ADVOB_TXT , 183 ) ; // % has found treasure
iw . text . addReplacement ( h - > name ) ;
cb - > showInfoDialog ( & iw ) ;
}
}
2016-02-03 03:18:03 +02:00
static const std : : string CHARACTER_JSON [ ] =
{
" compliant " , " friendly " , " aggressive " , " hostile " , " savage "
} ;
2015-11-16 15:30:40 +02:00
void CGCreature : : writeJsonOptions ( JsonNode & json ) const
{
2016-02-03 03:18:03 +02:00
assert ( vstd : : iswithin ( character , 0 , 4 ) ) ;
json [ " character " ] . String ( ) = CHARACTER_JSON [ character ] ;
2015-11-16 15:30:40 +02:00
2016-02-03 03:18:03 +02:00
if ( hasStackAtSlot ( SlotID ( 0 ) ) )
{
2016-02-05 08:40:20 +02:00
const auto & sta = getStack ( SlotID ( 0 ) ) ;
2016-02-03 03:18:03 +02:00
json [ " amount " ] . Float ( ) = sta . count ;
}
json [ " noGrowing " ] . Bool ( ) = notGrowingTeam ;
json [ " neverFlees " ] . Bool ( ) = neverFlees ;
json [ " rewardMessage " ] . String ( ) = message ;
json [ " rewardArtifact " ] . String ( ) = ( gainedArtifact = = ArtifactID ( ArtifactID : : NONE ) ? " " : gainedArtifact . toArtifact ( ) - > identifier ) ;
2016-02-04 07:44:46 +02:00
if ( resources . nonZero ( ) )
{
for ( size_t idx = 0 ; idx < resources . size ( ) ; idx + + )
json [ " rewardResources " ] [ GameConstants : : RESOURCE_NAMES [ idx ] ] . Float ( ) = resources [ idx ] ;
}
2015-11-16 15:30:40 +02:00
}
void CGCreature : : readJsonOptions ( const JsonNode & json )
{
2016-02-03 03:18:03 +02:00
character = vstd : : find_pos ( CHARACTER_JSON , json [ " character " ] . String ( ) ) ;
vstd : : amin ( character , 0 ) ;
auto hlp = new CStackInstance ( ) ;
hlp - > count = json [ " amount " ] . Float ( ) ;
//type will be set during initialization
putStack ( SlotID ( 0 ) , hlp ) ;
notGrowingTeam = json [ " noGrowing " ] . Bool ( ) ;
neverFlees = json [ " neverFlees " ] . Bool ( ) ;
message = json [ " rewardMessage " ] . String ( ) ;
2015-11-16 15:30:40 +02:00
2016-02-03 03:18:03 +02:00
gainedArtifact = ArtifactID ( ArtifactID : : NONE ) ;
if ( json [ " rewardArtifact " ] . String ( ) ! = " " )
{
auto artid = VLC - > modh - > identifiers . getIdentifier ( " core " , " artifact " , json [ " rewardArtifact " ] . String ( ) ) ;
if ( artid )
gainedArtifact = ArtifactID ( artid . get ( ) ) ;
}
2016-02-04 07:44:46 +02:00
TResources tmp ( json [ " rewardResources " ] ) ;
std : : swap ( tmp , resources ) ;
2015-11-16 15:30:40 +02:00
}
//CGMine
2014-06-05 19:52:14 +03:00
void CGMine : : onHeroVisit ( const CGHeroInstance * h ) const
{
int relations = cb - > gameState ( ) - > getPlayerRelations ( h - > tempOwner , tempOwner ) ;
if ( relations = = 2 ) //we're visiting our mine
{
cb - > showGarrisonDialog ( id , h - > id , true ) ;
return ;
}
else if ( relations = = 1 ) //ally
return ;
if ( stacksCount ( ) ) //Mine is guarded
{
BlockingDialog ynd ( true , false ) ;
ynd . player = h - > tempOwner ;
ynd . text . addTxt ( MetaString : : ADVOB_TXT , subID = = 7 ? 84 : 187 ) ;
cb - > showBlockingDialog ( & ynd ) ;
return ;
}
flagMine ( h - > tempOwner ) ;
}
void CGMine : : newTurn ( ) const
{
if ( cb - > getDate ( ) = = 1 )
return ;
if ( tempOwner = = PlayerColor : : NEUTRAL )
return ;
cb - > giveResource ( tempOwner , producedResource , producedQuantity ) ;
}
void CGMine : : initObj ( )
{
2016-01-23 18:53:02 +02:00
if ( isAbandoned ( ) )
2014-06-05 19:52:14 +03:00
{
//set guardians
int howManyTroglodytes = cb - > gameState ( ) - > getRandomGenerator ( ) . nextInt ( 100 , 199 ) ;
auto troglodytes = new CStackInstance ( CreatureID : : TROGLODYTES , howManyTroglodytes ) ;
putStack ( SlotID ( 0 ) , troglodytes ) ;
//after map reading tempOwner placeholds bitmask for allowed resources
std : : vector < Res : : ERes > possibleResources ;
for ( int i = 0 ; i < PlayerColor : : PLAYER_LIMIT_I ; i + + )
if ( tempOwner . getNum ( ) & 1 < < i ) //NOTE: reuse of tempOwner
possibleResources . push_back ( static_cast < Res : : ERes > ( i ) ) ;
assert ( ! possibleResources . empty ( ) ) ;
producedResource = * RandomGeneratorUtil : : nextItem ( possibleResources , cb - > gameState ( ) - > getRandomGenerator ( ) ) ;
tempOwner = PlayerColor : : NEUTRAL ;
}
else
{
producedResource = static_cast < Res : : ERes > ( subID ) ;
if ( tempOwner > = PlayerColor : : PLAYER_LIMIT )
tempOwner = PlayerColor : : NEUTRAL ;
}
producedQuantity = defaultResProduction ( ) ;
}
2016-01-23 18:53:02 +02:00
bool CGMine : : isAbandoned ( ) const
{
return ( subID > = 7 ) ;
}
2014-06-24 20:39:36 +03:00
std : : string CGMine : : getObjectName ( ) const
2014-06-05 19:52:14 +03:00
{
2014-06-24 20:39:36 +03:00
return VLC - > generaltexth - > mines . at ( subID ) . first ;
}
2014-06-05 19:52:14 +03:00
2014-06-24 20:39:36 +03:00
std : : string CGMine : : getHoverText ( PlayerColor player ) const
{
std : : string hoverName = getObjectName ( ) ; // Sawmill
if ( tempOwner ! = PlayerColor : : NEUTRAL )
2014-06-05 19:52:14 +03:00
{
2014-06-24 20:39:36 +03:00
hoverName + = " \n " ;
hoverName + = VLC - > generaltexth - > arraytxt [ 23 + tempOwner . getNum ( ) ] ; // owned by Red Player
hoverName + = " \n ( " + VLC - > generaltexth - > restypes [ producedResource ] + " ) " ;
2014-06-05 19:52:14 +03:00
}
2014-06-24 20:39:36 +03:00
2015-12-24 20:05:02 +02:00
if ( stacksCount ( ) )
2014-06-24 20:39:36 +03:00
{
hoverName + = " \n " ;
2015-12-24 20:05:02 +02:00
hoverName + = VLC - > generaltexth - > allTexts [ 202 ] ; //Guarded by
hoverName + = " " ;
hoverName + = getArmyDescription ( ) ;
2014-06-24 20:39:36 +03:00
}
return hoverName ;
}
void CGMine : : flagMine ( PlayerColor player ) const
{
assert ( tempOwner ! = player ) ;
cb - > setOwner ( this , player ) ; //not ours? flag it!
2014-06-05 19:52:14 +03:00
InfoWindow iw ;
iw . soundID = soundBase : : FLAGMINE ;
iw . text . addTxt ( MetaString : : MINE_EVNTS , producedResource ) ; //not use subID, abandoned mines uses default mine texts
iw . player = player ;
iw . components . push_back ( Component ( Component : : RESOURCE , producedResource , producedQuantity , - 1 ) ) ;
cb - > showInfoDialog ( & iw ) ;
}
ui32 CGMine : : defaultResProduction ( )
{
switch ( producedResource )
{
case Res : : WOOD :
case Res : : ORE :
return 2 ;
case Res : : GOLD :
return 1000 ;
default :
return 1 ;
}
}
void CGMine : : battleFinished ( const CGHeroInstance * hero , const BattleResult & result ) const
{
if ( result . winner = = 0 ) //attacker won
{
2016-01-23 18:53:02 +02:00
if ( isAbandoned ( ) )
2014-06-05 19:52:14 +03:00
{
showInfoDialog ( hero - > tempOwner , 85 , 0 ) ;
}
flagMine ( hero - > tempOwner ) ;
}
}
void CGMine : : blockingDialogAnswered ( const CGHeroInstance * hero , ui32 answer ) const
{
if ( answer )
cb - > startBattleI ( hero , this ) ;
}
2016-01-23 18:53:02 +02:00
void CGMine : : writeJsonOptions ( JsonNode & json ) const
2015-11-16 15:30:40 +02:00
{
2016-01-23 18:53:02 +02:00
CArmedInstance : : writeJsonOptions ( json ) ;
if ( isAbandoned ( ) )
{
JsonNode & node = json [ " possibleResources " ] ;
2015-11-16 15:30:40 +02:00
2016-01-23 18:53:02 +02:00
for ( int i = 0 ; i < PlayerColor : : PLAYER_LIMIT_I ; i + + )
if ( tempOwner . getNum ( ) & 1 < < i )
{
JsonNode one ( JsonNode : : DATA_STRING ) ;
one . String ( ) = GameConstants : : RESOURCE_NAMES [ i ] ;
node . Vector ( ) . push_back ( one ) ;
}
}
else
{
CGObjectInstance : : writeOwner ( json ) ;
}
2015-11-16 15:30:40 +02:00
}
2016-01-23 18:53:02 +02:00
void CGMine : : readJsonOptions ( const JsonNode & json )
2015-11-16 15:30:40 +02:00
{
2016-01-23 18:53:02 +02:00
CArmedInstance : : readJsonOptions ( json ) ;
if ( isAbandoned ( ) )
{
const JsonNode & node = json [ " possibleResources " ] ;
2015-11-16 15:30:40 +02:00
2016-01-23 18:53:02 +02:00
std : : set < int > possibleResources ;
if ( node . Vector ( ) . size ( ) = = 0 )
{
//assume all allowed
for ( int i = ( int ) Res : : WOOD ; i < ( int ) Res : : GOLD ; i + + )
possibleResources . insert ( i ) ;
}
else
{
auto names = node . convertTo < std : : vector < std : : string > > ( ) ;
for ( const std : : string & s : names )
{
int raw_res = vstd : : find_pos ( GameConstants : : RESOURCE_NAMES , s ) ;
if ( raw_res < 0 )
logGlobal - > errorStream ( ) < < " Invalid resource name: " + s ;
else
possibleResources . insert ( raw_res ) ;
}
int tmp = 0 ;
for ( int r : possibleResources )
tmp | = ( 1 < < r ) ;
tempOwner = PlayerColor ( tmp ) ;
}
}
else
{
CGObjectInstance : : readOwner ( json ) ;
}
2015-11-16 15:30:40 +02:00
}
2014-06-24 20:39:36 +03:00
std : : string CGResource : : getHoverText ( PlayerColor player ) const
{
return VLC - > generaltexth - > restypes [ subID ] ;
}
2014-07-27 20:30:17 +03:00
CGResource : : CGResource ( )
{
amount = 0 ;
}
2014-06-05 19:52:14 +03:00
void CGResource : : initObj ( )
{
blockVisit = true ;
if ( ! amount )
{
switch ( subID )
{
case 6 :
amount = cb - > gameState ( ) - > getRandomGenerator ( ) . nextInt ( 500 , 1000 ) ;
break ;
case 0 : case 2 :
amount = cb - > gameState ( ) - > getRandomGenerator ( ) . nextInt ( 6 , 10 ) ;
break ;
default :
amount = cb - > gameState ( ) - > getRandomGenerator ( ) . nextInt ( 3 , 5 ) ;
break ;
}
}
}
void CGResource : : onHeroVisit ( const CGHeroInstance * h ) const
{
if ( stacksCount ( ) )
{
if ( message . size ( ) )
{
BlockingDialog ynd ( true , false ) ;
ynd . player = h - > getOwner ( ) ;
ynd . text < < message ;
cb - > showBlockingDialog ( & ynd ) ;
}
else
{
blockingDialogAnswered ( h , true ) ; //behave as if player accepted battle
}
}
else
{
if ( message . length ( ) )
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
iw . text < < message ;
cb - > showInfoDialog ( & iw ) ;
}
collectRes ( h - > getOwner ( ) ) ;
}
}
void CGResource : : collectRes ( PlayerColor player ) const
{
cb - > giveResource ( player , static_cast < Res : : ERes > ( subID ) , amount ) ;
ShowInInfobox sii ;
sii . player = player ;
sii . c = Component ( Component : : RESOURCE , subID , amount , 0 ) ;
sii . text . addTxt ( MetaString : : ADVOB_TXT , 113 ) ;
sii . text . addReplacement ( MetaString : : RES_NAMES , subID ) ;
cb - > showCompInfo ( & sii ) ;
cb - > removeObject ( this ) ;
}
void CGResource : : battleFinished ( const CGHeroInstance * hero , const BattleResult & result ) const
{
if ( result . winner = = 0 ) //attacker won
collectRes ( hero - > getOwner ( ) ) ;
}
void CGResource : : blockingDialogAnswered ( const CGHeroInstance * hero , ui32 answer ) const
{
if ( answer )
cb - > startBattleI ( hero , this ) ;
}
2015-11-16 15:30:40 +02:00
void CGResource : : writeJsonOptions ( JsonNode & json ) const
{
2016-02-04 08:22:14 +02:00
CCreatureSet : : writeJson ( json [ " guards " ] ) ;
json [ " amount " ] . Float ( ) = amount ;
json [ " guardMessage " ] . String ( ) = message ;
2015-11-16 15:30:40 +02:00
}
void CGResource : : readJsonOptions ( const JsonNode & json )
{
2016-02-04 08:22:14 +02:00
CCreatureSet : : readJson ( json [ " guards " ] ) ;
amount = json [ " amount " ] . Float ( ) ;
message = json [ " guardMessage " ] . String ( ) ;
2015-11-16 15:30:40 +02:00
}
2015-03-08 15:11:23 +02:00
CGTeleport : : CGTeleport ( ) :
type ( UNKNOWN ) , channel ( TeleportChannelID ( ) )
2014-06-05 19:52:14 +03:00
{
2015-03-08 15:11:23 +02:00
}
bool CGTeleport : : isEntrance ( ) const
{
return type = = BOTH | | type = = ENTRANCE ;
}
bool CGTeleport : : isExit ( ) const
{
return type = = BOTH | | type = = EXIT ;
}
bool CGTeleport : : isChannelEntrance ( ObjectInstanceID id ) const
{
2015-03-08 19:19:00 +02:00
return vstd : : contains ( getAllEntrances ( ) , id ) ;
2015-03-08 15:11:23 +02:00
}
bool CGTeleport : : isChannelExit ( ObjectInstanceID id ) const
{
2015-03-08 19:19:00 +02:00
return vstd : : contains ( getAllExits ( ) , id ) ;
2015-03-08 15:11:23 +02:00
}
std : : vector < ObjectInstanceID > CGTeleport : : getAllEntrances ( bool excludeCurrent ) const
{
2015-03-09 14:44:48 +02:00
auto ret = cb - > getTeleportChannelEntraces ( channel ) ;
2015-03-08 15:11:23 +02:00
if ( excludeCurrent )
2015-03-09 14:44:48 +02:00
vstd : : erase_if_present ( ret , id ) ;
2015-03-08 15:11:23 +02:00
return ret ;
}
std : : vector < ObjectInstanceID > CGTeleport : : getAllExits ( bool excludeCurrent ) const
{
2015-03-09 14:44:48 +02:00
auto ret = cb - > getTeleportChannelExits ( channel ) ;
2015-03-08 15:11:23 +02:00
if ( excludeCurrent )
2015-03-09 14:44:48 +02:00
vstd : : erase_if_present ( ret , id ) ;
2015-03-08 15:11:23 +02:00
return ret ;
}
ObjectInstanceID CGTeleport : : getRandomExit ( const CGHeroInstance * h ) const
{
auto passableExits = getPassableExits ( cb - > gameState ( ) , h , getAllExits ( true ) ) ;
if ( passableExits . size ( ) )
return * RandomGeneratorUtil : : nextItem ( passableExits , cb - > gameState ( ) - > getRandomGenerator ( ) ) ;
return ObjectInstanceID ( ) ;
}
bool CGTeleport : : isTeleport ( const CGObjectInstance * obj )
{
2015-03-08 19:19:00 +02:00
return ( ( dynamic_cast < const CGTeleport * > ( obj ) ) ) ;
2015-03-08 15:11:23 +02:00
}
bool CGTeleport : : isConnected ( const CGTeleport * src , const CGTeleport * dst )
{
2015-12-02 16:56:26 +02:00
return src & & dst & & src - > isChannelExit ( dst - > id ) ;
2015-03-08 15:11:23 +02:00
}
bool CGTeleport : : isConnected ( const CGObjectInstance * src , const CGObjectInstance * dst )
{
auto srcObj = dynamic_cast < const CGTeleport * > ( src ) ;
auto dstObj = dynamic_cast < const CGTeleport * > ( dst ) ;
return isConnected ( srcObj , dstObj ) ;
}
bool CGTeleport : : isExitPassable ( CGameState * gs , const CGHeroInstance * h , const CGObjectInstance * obj )
{
auto objTopVisObj = gs - > map - > getTile ( obj - > visitablePos ( ) ) . topVisitableObj ( ) ;
if ( objTopVisObj - > ID = = Obj : : HERO )
2014-06-05 19:52:14 +03:00
{
2015-03-08 15:11:23 +02:00
if ( h - > id = = objTopVisObj - > id ) // Just to be sure it's won't happen.
return false ;
// Check if it's friendly hero or not
if ( gs - > getPlayerRelations ( h - > tempOwner , objTopVisObj - > tempOwner ) )
2014-06-05 19:52:14 +03:00
{
2015-03-08 15:11:23 +02:00
// Exchange between heroes only possible via subterranean gates
if ( ! dynamic_cast < const CGSubterraneanGate * > ( obj ) )
return false ;
2014-06-05 19:52:14 +03:00
}
}
2015-03-08 15:11:23 +02:00
return true ;
}
2014-06-05 19:52:14 +03:00
2015-03-08 15:11:23 +02:00
std : : vector < ObjectInstanceID > CGTeleport : : getPassableExits ( CGameState * gs , const CGHeroInstance * h , std : : vector < ObjectInstanceID > exits )
{
vstd : : erase_if ( exits , [ & ] ( ObjectInstanceID exit ) - > bool
{
return ! isExitPassable ( gs , h , gs - > getObj ( exit ) ) ;
} ) ;
return exits ;
}
2015-12-29 04:43:33 +02:00
void CGTeleport : : addToChannel ( std : : map < TeleportChannelID , std : : shared_ptr < TeleportChannel > > & channelsList , const CGTeleport * obj )
2015-03-08 15:11:23 +02:00
{
2015-12-29 04:43:33 +02:00
std : : shared_ptr < TeleportChannel > tc ;
2015-03-08 15:11:23 +02:00
if ( channelsList . find ( obj - > channel ) = = channelsList . end ( ) )
{
2015-12-29 04:43:33 +02:00
tc = std : : make_shared < TeleportChannel > ( ) ;
2015-03-08 15:11:23 +02:00
channelsList . insert ( std : : make_pair ( obj - > channel , tc ) ) ;
2014-06-05 19:52:14 +03:00
}
2015-03-08 15:11:23 +02:00
else
tc = channelsList [ obj - > channel ] ;
if ( obj - > isEntrance ( ) & & ! vstd : : contains ( tc - > entrances , obj - > id ) )
tc - > entrances . push_back ( obj - > id ) ;
if ( obj - > isExit ( ) & & ! vstd : : contains ( tc - > exits , obj - > id ) )
tc - > exits . push_back ( obj - > id ) ;
if ( tc - > entrances . size ( ) & & tc - > exits . size ( )
& & ( tc - > entrances . size ( ) ! = 1 | | tc - > entrances ! = tc - > exits ) )
2014-06-05 19:52:14 +03:00
{
2015-03-08 15:11:23 +02:00
tc - > passability = TeleportChannel : : PASSABLE ;
2014-06-05 19:52:14 +03:00
}
2015-03-08 15:11:23 +02:00
}
TeleportChannelID CGMonolith : : findMeChannel ( std : : vector < Obj > IDs , int SubID ) const
{
for ( auto obj : cb - > gameState ( ) - > map - > objects )
2014-06-05 19:52:14 +03:00
{
2015-03-08 15:11:23 +02:00
auto teleportObj = dynamic_cast < const CGTeleport * > ( cb - > getObj ( obj - > id ) ) ;
if ( teleportObj & & vstd : : contains ( IDs , teleportObj - > ID ) & & teleportObj - > subID = = SubID )
return teleportObj - > channel ;
2014-06-05 19:52:14 +03:00
}
2015-03-08 15:11:23 +02:00
return TeleportChannelID ( ) ;
2014-06-05 19:52:14 +03:00
}
2015-03-08 15:11:23 +02:00
void CGMonolith : : onHeroVisit ( const CGHeroInstance * h ) const
2014-06-05 19:52:14 +03:00
{
2015-03-08 15:11:23 +02:00
TeleportDialog td ( h , channel ) ;
if ( isEntrance ( ) )
2014-06-05 19:52:14 +03:00
{
2015-03-09 01:13:40 +02:00
if ( cb - > isTeleportChannelBidirectional ( channel ) & & 1 < cb - > getTeleportChannelExits ( channel ) . size ( ) )
2015-12-02 16:56:26 +02:00
{
auto exits = cb - > getTeleportChannelExits ( channel ) ;
for ( auto exit : exits )
{
td . exits . push_back ( std : : make_pair ( exit , CGHeroInstance : : convertPosition ( cb - > getObj ( exit ) - > visitablePos ( ) , true ) ) ) ;
}
}
2015-03-08 15:11:23 +02:00
2015-03-09 01:13:40 +02:00
if ( cb - > isTeleportChannelImpassable ( channel ) )
2014-06-05 19:52:14 +03:00
{
2015-03-08 19:19:00 +02:00
logGlobal - > debugStream ( ) < < " Cannot find corresponding exit monolith for " < < id < < " (obj at " < < pos < < " ) :( " ;
2015-03-08 15:11:23 +02:00
td . impassable = true ;
2014-06-05 19:52:14 +03:00
}
2015-03-08 15:11:23 +02:00
else if ( getRandomExit ( h ) = = ObjectInstanceID ( ) )
2015-03-08 19:19:00 +02:00
logGlobal - > debugStream ( ) < < " All exits blocked for monolith " < < id < < " (obj at " < < pos < < " ) :( " ;
2014-06-05 19:52:14 +03:00
}
2015-03-08 17:07:06 +02:00
else
showInfoDialog ( h , 70 , 0 ) ;
2015-03-08 15:11:23 +02:00
cb - > showTeleportDialog ( & td ) ;
}
2015-12-02 16:56:26 +02:00
void CGMonolith : : teleportDialogAnswered ( const CGHeroInstance * hero , ui32 answer , TTeleportExitsList exits ) const
2015-03-08 15:11:23 +02:00
{
2015-12-02 16:56:26 +02:00
int3 dPos ;
2015-12-18 00:08:19 +02:00
auto randomExit = getRandomExit ( hero ) ;
2015-03-08 15:11:23 +02:00
auto realExits = getAllExits ( true ) ;
if ( ! isEntrance ( ) // Do nothing if hero visited exit only object
| | ( ! exits . size ( ) & & ! realExits . size ( ) ) // Do nothing if there no exits on this channel
2015-12-18 00:08:19 +02:00
| | ObjectInstanceID ( ) = = randomExit ) // Do nothing if all exits are blocked by friendly hero and it's not subterranean gate
2014-06-05 19:52:14 +03:00
{
return ;
}
2015-12-02 16:56:26 +02:00
else if ( vstd : : isValidIndex ( exits , answer ) )
dPos = exits [ answer ] . second ;
2014-06-05 19:52:14 +03:00
else
2015-12-18 00:08:19 +02:00
dPos = CGHeroInstance : : convertPosition ( cb - > getObj ( randomExit ) - > visitablePos ( ) , true ) ;
2015-03-08 15:11:23 +02:00
2015-12-02 16:56:26 +02:00
cb - > moveHero ( hero - > id , dPos , true ) ;
2014-06-05 19:52:14 +03:00
}
2015-03-08 15:11:23 +02:00
void CGMonolith : : initObj ( )
2014-06-05 19:52:14 +03:00
{
2015-03-08 15:11:23 +02:00
std : : vector < Obj > IDs ;
IDs . push_back ( ID ) ;
switch ( ID )
2014-06-05 19:52:14 +03:00
{
2015-03-09 00:27:40 +02:00
case Obj : : MONOLITH_ONE_WAY_ENTRANCE :
type = ENTRANCE ;
IDs . push_back ( Obj : : MONOLITH_ONE_WAY_EXIT ) ;
break ;
case Obj : : MONOLITH_ONE_WAY_EXIT :
type = EXIT ;
IDs . push_back ( Obj : : MONOLITH_ONE_WAY_ENTRANCE ) ;
break ;
case Obj : : MONOLITH_TWO_WAY :
2014-06-05 19:52:14 +03:00
default :
2015-03-09 00:27:40 +02:00
type = BOTH ;
2014-06-05 19:52:14 +03:00
break ;
}
2015-03-08 15:11:23 +02:00
channel = findMeChannel ( IDs , subID ) ;
if ( channel = = TeleportChannelID ( ) )
channel = TeleportChannelID ( cb - > gameState ( ) - > map - > teleportChannels . size ( ) ) ;
addToChannel ( cb - > gameState ( ) - > map - > teleportChannels , this ) ;
}
void CGSubterraneanGate : : onHeroVisit ( const CGHeroInstance * h ) const
{
TeleportDialog td ( h , channel ) ;
2015-03-09 01:13:40 +02:00
if ( cb - > isTeleportChannelImpassable ( channel ) )
2015-03-08 15:11:23 +02:00
{
showInfoDialog ( h , 153 , 0 ) ; //Just inside the entrance you find a large pile of rubble blocking the tunnel. You leave discouraged.
logGlobal - > debugStream ( ) < < " Cannot find exit subterranean gate for " < < id < < " (obj at " < < pos < < " ) :( " ;
td . impassable = true ;
2014-06-05 19:52:14 +03:00
}
2015-03-08 15:11:23 +02:00
else
2015-12-02 16:56:26 +02:00
{
auto exit = getRandomExit ( h ) ;
td . exits . push_back ( std : : make_pair ( exit , CGHeroInstance : : convertPosition ( cb - > getObj ( exit ) - > visitablePos ( ) , true ) ) ) ;
}
2015-03-08 15:11:23 +02:00
cb - > showTeleportDialog ( & td ) ;
2014-06-05 19:52:14 +03:00
}
2015-03-08 15:11:23 +02:00
void CGSubterraneanGate : : initObj ( )
{
type = BOTH ;
2014-06-05 19:52:14 +03:00
}
2015-03-11 16:17:21 +02:00
void CGSubterraneanGate : : postInit ( ) //matches subterranean gates into pairs
2014-06-05 19:52:14 +03:00
{
//split on underground and surface gates
2015-03-08 15:11:23 +02:00
std : : vector < CGSubterraneanGate * > gatesSplit [ 2 ] ; //surface and underground gates
for ( auto & obj : cb - > gameState ( ) - > map - > objects )
2014-06-05 19:52:14 +03:00
{
2015-03-11 16:17:21 +02:00
auto hlp = dynamic_cast < CGSubterraneanGate * > ( cb - > gameState ( ) - > getObjInstance ( obj - > id ) ) ;
2015-03-08 15:11:23 +02:00
if ( hlp )
gatesSplit [ hlp - > pos . z ] . push_back ( hlp ) ;
2014-06-05 19:52:14 +03:00
}
//sort by position
std : : sort ( gatesSplit [ 0 ] . begin ( ) , gatesSplit [ 0 ] . end ( ) , [ ] ( const CGObjectInstance * a , const CGObjectInstance * b )
{
return a - > pos < b - > pos ;
} ) ;
2015-03-11 16:17:21 +02:00
auto assignToChannel = [ & ] ( CGSubterraneanGate * obj )
{
if ( obj - > channel = = TeleportChannelID ( ) )
{ // if object not linked to channel then create new channel
obj - > channel = TeleportChannelID ( cb - > gameState ( ) - > map - > teleportChannels . size ( ) ) ;
addToChannel ( cb - > gameState ( ) - > map - > teleportChannels , obj ) ;
}
} ;
2014-06-05 19:52:14 +03:00
for ( size_t i = 0 ; i < gatesSplit [ 0 ] . size ( ) ; i + + )
{
2015-03-08 15:11:23 +02:00
CGSubterraneanGate * objCurrent = gatesSplit [ 0 ] [ i ] ;
2014-06-05 19:52:14 +03:00
//find nearest underground exit
std : : pair < int , si32 > best ( - 1 , std : : numeric_limits < si32 > : : max ( ) ) ; //pair<pos_in_vector, distance^2>
for ( int j = 0 ; j < gatesSplit [ 1 ] . size ( ) ; j + + )
{
2015-03-08 15:11:23 +02:00
CGSubterraneanGate * checked = gatesSplit [ 1 ] [ j ] ;
2015-03-11 16:17:21 +02:00
if ( checked - > channel ! = TeleportChannelID ( ) )
2014-06-05 19:52:14 +03:00
continue ;
2015-03-08 15:11:23 +02:00
si32 hlp = checked - > pos . dist2dSQ ( objCurrent - > pos ) ;
2014-06-05 19:52:14 +03:00
if ( hlp < best . second )
{
best . first = j ;
best . second = hlp ;
}
}
2015-03-11 16:17:21 +02:00
assignToChannel ( objCurrent ) ;
2014-06-05 19:52:14 +03:00
if ( best . first > = 0 ) //found pair
{
2015-03-08 15:11:23 +02:00
gatesSplit [ 1 ] [ best . first ] - > channel = objCurrent - > channel ;
addToChannel ( cb - > gameState ( ) - > map - > teleportChannels , gatesSplit [ 1 ] [ best . first ] ) ;
2014-06-05 19:52:14 +03:00
}
}
2015-03-11 16:17:21 +02:00
// we should assign empty channels to underground gates if they don't have matching overground gates
for ( size_t i = 0 ; i < gatesSplit [ 1 ] . size ( ) ; i + + )
assignToChannel ( gatesSplit [ 1 ] [ i ] ) ;
2014-06-05 19:52:14 +03:00
}
2015-03-08 15:11:23 +02:00
void CGWhirlpool : : onHeroVisit ( const CGHeroInstance * h ) const
2014-06-05 19:52:14 +03:00
{
2015-03-08 15:11:23 +02:00
TeleportDialog td ( h , channel ) ;
2015-03-09 01:13:40 +02:00
if ( cb - > isTeleportChannelImpassable ( channel ) )
2014-06-05 19:52:14 +03:00
{
2015-03-08 19:19:00 +02:00
logGlobal - > debugStream ( ) < < " Cannot find exit whirlpool for " < < id < < " (obj at " < < pos < < " ) :( " ;
2015-03-08 15:11:23 +02:00
td . impassable = true ;
2014-06-05 19:52:14 +03:00
}
2015-03-08 15:11:23 +02:00
else if ( getRandomExit ( h ) = = ObjectInstanceID ( ) )
2015-03-08 19:19:00 +02:00
logGlobal - > debugStream ( ) < < " All exits are blocked for whirlpool " < < id < < " (obj at " < < pos < < " ) :( " ;
2014-06-05 19:52:14 +03:00
2015-03-08 15:11:23 +02:00
if ( ! isProtected ( h ) )
{
SlotID targetstack = h - > Slots ( ) . begin ( ) - > first ; //slot numbers may vary
for ( auto i = h - > Slots ( ) . rbegin ( ) ; i ! = h - > Slots ( ) . rend ( ) ; i + + )
{
if ( h - > getPower ( targetstack ) > h - > getPower ( i - > first ) )
targetstack = ( i - > first ) ;
}
TQuantity countToTake = h - > getStackCount ( targetstack ) * 0.5 ;
vstd : : amax ( countToTake , 1 ) ;
InfoWindow iw ;
iw . player = h - > tempOwner ;
iw . text . addTxt ( MetaString : : ADVOB_TXT , 168 ) ;
iw . components . push_back ( Component ( CStackBasicDescriptor ( h - > getCreature ( targetstack ) , countToTake ) ) ) ;
cb - > showInfoDialog ( & iw ) ;
cb - > changeStackCount ( StackLocation ( h , targetstack ) , - countToTake ) ;
}
else
2015-12-02 16:56:26 +02:00
{
auto exits = getAllExits ( ) ;
for ( auto exit : exits )
{
auto blockedPosList = cb - > getObj ( exit ) - > getBlockedPos ( ) ;
for ( auto bPos : blockedPosList )
td . exits . push_back ( std : : make_pair ( exit , CGHeroInstance : : convertPosition ( bPos , true ) ) ) ;
}
}
2015-03-08 15:11:23 +02:00
cb - > showTeleportDialog ( & td ) ;
}
2015-12-02 16:56:26 +02:00
void CGWhirlpool : : teleportDialogAnswered ( const CGHeroInstance * hero , ui32 answer , TTeleportExitsList exits ) const
2015-03-08 15:11:23 +02:00
{
2015-12-02 16:56:26 +02:00
int3 dPos ;
2015-03-08 15:11:23 +02:00
auto realExits = getAllExits ( ) ;
if ( ! exits . size ( ) & & ! realExits . size ( ) )
return ;
2015-12-02 16:56:26 +02:00
else if ( vstd : : isValidIndex ( exits , answer ) )
dPos = exits [ answer ] . second ;
2015-03-08 15:11:23 +02:00
else
{
2015-12-02 16:56:26 +02:00
auto obj = cb - > getObj ( getRandomExit ( hero ) ) ;
2015-03-08 15:11:23 +02:00
std : : set < int3 > tiles = obj - > getBlockedPos ( ) ;
2015-12-02 16:56:26 +02:00
dPos = CGHeroInstance : : convertPosition ( * RandomGeneratorUtil : : nextItem ( tiles , cb - > gameState ( ) - > getRandomGenerator ( ) ) , true ) ;
2015-03-08 15:11:23 +02:00
}
2015-12-02 16:56:26 +02:00
cb - > moveHero ( hero - > id , dPos , true ) ;
2015-03-08 15:11:23 +02:00
}
2015-12-24 20:30:57 +02:00
bool CGWhirlpool : : isProtected ( const CGHeroInstance * h )
2015-03-08 15:11:23 +02:00
{
2015-12-24 20:30:57 +02:00
if ( h - > hasBonusOfType ( Bonus : : WHIRLPOOL_PROTECTION ) | |
( h - > stacksCount ( ) = = 1 & & h - > Slots ( ) . begin ( ) - > second - > count = = 1 ) ) //we can't remove last unit
2015-03-08 15:11:23 +02:00
{
return true ;
}
return false ;
2014-06-05 19:52:14 +03:00
}
void CGArtifact : : initObj ( )
{
blockVisit = true ;
if ( ID = = Obj : : ARTIFACT )
{
2014-07-05 20:35:46 +03:00
if ( ! storedArtifact )
{
auto a = new CArtifactInstance ( ) ;
cb - > gameState ( ) - > map - > addNewArtifactInstance ( a ) ;
storedArtifact = a ;
}
2014-06-05 19:52:14 +03:00
if ( ! storedArtifact - > artType )
storedArtifact - > setType ( VLC - > arth - > artifacts [ subID ] ) ;
}
if ( ID = = Obj : : SPELL_SCROLL )
subID = 1 ;
assert ( storedArtifact - > artType ) ;
assert ( storedArtifact - > getParentNodes ( ) . size ( ) ) ;
//assert(storedArtifact->artType->id == subID); //this does not stop desync
}
2014-06-24 20:39:36 +03:00
std : : string CGArtifact : : getObjectName ( ) const
{
return VLC - > arth - > artifacts [ subID ] - > Name ( ) ;
}
2015-12-24 20:05:02 +02:00
void CGArtifact : : onHeroVisit ( const CGHeroInstance * h ) const
2014-06-05 19:52:14 +03:00
{
if ( ! stacksCount ( ) )
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
switch ( ID )
{
case Obj : : ARTIFACT :
{
iw . soundID = soundBase : : treasure ; //play sound only for non-scroll arts
2015-12-24 20:05:02 +02:00
iw . components . push_back ( Component ( Component : : ARTIFACT , subID , 0 , 0 ) ) ;
2014-06-05 19:52:14 +03:00
if ( message . length ( ) )
2015-12-24 20:05:02 +02:00
iw . text < < message ;
2014-06-05 19:52:14 +03:00
else
{
2015-12-24 20:05:02 +02:00
if ( VLC - > arth - > artifacts [ subID ] - > EventText ( ) . size ( ) )
iw . text < < std : : pair < ui8 , ui32 > ( MetaString : : ART_EVNTS , subID ) ;
2014-06-05 19:52:14 +03:00
else //fix for mod artifacts with no event text
{
2015-12-24 20:05:02 +02:00
iw . text . addTxt ( MetaString : : ADVOB_TXT , 183 ) ; //% has found treasure
iw . text . addReplacement ( h - > name ) ;
2014-06-05 19:52:14 +03:00
}
}
}
break ;
case Obj : : SPELL_SCROLL :
{
int spellID = storedArtifact - > getGivenSpellID ( ) ;
2015-12-24 20:05:02 +02:00
iw . components . push_back ( Component ( Component : : SPELL , spellID , 0 , 0 ) ) ;
if ( message . length ( ) )
iw . text < < message ;
else
{
iw . text . addTxt ( MetaString : : ADVOB_TXT , 135 ) ;
iw . text . addReplacement ( MetaString : : SPELL_NAME , spellID ) ;
}
2014-06-05 19:52:14 +03:00
}
break ;
}
cb - > showInfoDialog ( & iw ) ;
pick ( h ) ;
}
else
{
2015-12-24 20:05:02 +02:00
switch ( ID )
2014-06-05 19:52:14 +03:00
{
2015-12-24 20:05:02 +02:00
case Obj : : ARTIFACT :
{
BlockingDialog ynd ( true , false ) ;
ynd . player = h - > getOwner ( ) ;
if ( message . length ( ) )
ynd . text < < message ;
else
{
// TODO: Guard text is more complex in H3, see mantis issue 2325 for details
ynd . text . addTxt ( MetaString : : GENERAL_TXT , 420 ) ;
ynd . text . addReplacement ( " " ) ;
ynd . text . addReplacement ( getArmyDescription ( ) ) ;
ynd . text . addReplacement ( MetaString : : GENERAL_TXT , 43 ) ; // creatures
}
cb - > showBlockingDialog ( & ynd ) ;
}
break ;
case Obj : : SPELL_SCROLL :
{
if ( message . length ( ) )
{
BlockingDialog ynd ( true , false ) ;
ynd . player = h - > getOwner ( ) ;
ynd . text < < message ;
cb - > showBlockingDialog ( & ynd ) ;
}
else
blockingDialogAnswered ( h , true ) ;
}
break ;
2014-06-05 19:52:14 +03:00
}
}
}
void CGArtifact : : pick ( const CGHeroInstance * h ) const
{
cb - > giveHeroArtifact ( h , storedArtifact , ArtifactPosition : : FIRST_AVAILABLE ) ;
cb - > removeObject ( this ) ;
}
void CGArtifact : : battleFinished ( const CGHeroInstance * hero , const BattleResult & result ) const
{
if ( result . winner = = 0 ) //attacker won
pick ( hero ) ;
}
void CGArtifact : : blockingDialogAnswered ( const CGHeroInstance * hero , ui32 answer ) const
{
if ( answer )
cb - > startBattleI ( hero , this ) ;
}
2015-11-16 15:30:40 +02:00
void CGArtifact : : writeJsonOptions ( JsonNode & json ) const
{
2016-01-23 18:53:02 +02:00
CCreatureSet : : writeJson ( json [ " guards " ] ) ;
json [ " guardMessage " ] . String ( ) = message ;
2015-11-16 15:30:40 +02:00
}
void CGArtifact : : readJsonOptions ( const JsonNode & json )
{
2016-01-23 18:53:02 +02:00
CCreatureSet : : readJson ( json [ " guards " ] ) ;
message = json [ " guardMessage " ] . String ( ) ;
2015-11-16 15:30:40 +02:00
}
2014-06-05 19:52:14 +03:00
void CGWitchHut : : initObj ( )
{
2014-07-05 20:35:46 +03:00
if ( allowedAbilities . empty ( ) ) //this can happen for RMG. regular maps load abilities from map file
{
for ( int i = 0 ; i < GameConstants : : SKILL_QUANTITY ; i + + )
allowedAbilities . push_back ( i ) ;
}
2014-06-05 19:52:14 +03:00
ability = * RandomGeneratorUtil : : nextItem ( allowedAbilities , cb - > gameState ( ) - > getRandomGenerator ( ) ) ;
}
void CGWitchHut : : onHeroVisit ( const CGHeroInstance * h ) const
{
InfoWindow iw ;
iw . soundID = soundBase : : gazebo ;
iw . player = h - > getOwner ( ) ;
if ( ! wasVisited ( h - > tempOwner ) )
2016-01-20 09:44:13 +02:00
cb - > setObjProperty ( id , CGWitchHut : : OBJPROP_VISITED , h - > tempOwner . getNum ( ) ) ;
2014-06-05 19:52:14 +03:00
ui32 txt_id ;
if ( h - > getSecSkillLevel ( SecondarySkill ( ability ) ) ) //you already know this skill
{
txt_id = 172 ;
}
else if ( ! h - > canLearnSkill ( ) ) //already all skills slots used
{
txt_id = 173 ;
}
else //give sec skill
{
iw . components . push_back ( Component ( Component : : SEC_SKILL , ability , 1 , 0 ) ) ;
txt_id = 171 ;
cb - > changeSecSkill ( h , SecondarySkill ( ability ) , 1 , true ) ;
}
iw . text . addTxt ( MetaString : : ADVOB_TXT , txt_id ) ;
iw . text . addReplacement ( MetaString : : SEC_SKILL_NAME , ability ) ;
cb - > showInfoDialog ( & iw ) ;
}
2014-06-24 20:39:36 +03:00
std : : string CGWitchHut : : getHoverText ( PlayerColor player ) const
2014-06-05 19:52:14 +03:00
{
2014-06-24 20:39:36 +03:00
std : : string hoverName = getObjectName ( ) ;
if ( wasVisited ( player ) )
2014-06-05 19:52:14 +03:00
{
hoverName + = " \n " + VLC - > generaltexth - > allTexts [ 356 ] ; // + (learn %s)
boost : : algorithm : : replace_first ( hoverName , " %s " , VLC - > generaltexth - > skillName [ ability ] ) ;
}
return hoverName ;
}
2014-06-24 20:39:36 +03:00
std : : string CGWitchHut : : getHoverText ( const CGHeroInstance * hero ) const
{
std : : string hoverName = getHoverText ( hero - > tempOwner ) ;
if ( hero - > getSecSkillLevel ( SecondarySkill ( ability ) ) ) //hero knows that ability
hoverName + = " \n \n " + VLC - > generaltexth - > allTexts [ 357 ] ; // (Already learned)
return hoverName ;
}
2015-11-16 15:30:40 +02:00
void CGWitchHut : : writeJsonOptions ( JsonNode & json ) const
{
}
void CGWitchHut : : readJsonOptions ( const JsonNode & json )
{
}
2014-06-05 19:52:14 +03:00
void CGMagicWell : : onHeroVisit ( const CGHeroInstance * h ) const
{
int message ;
if ( h - > hasBonusFrom ( Bonus : : OBJECT , ID ) ) //has already visited Well today
{
message = 78 ; //"A second drink at the well in one day will not help you."
}
else if ( h - > mana < h - > manaLimit ( ) )
{
giveDummyBonus ( h - > id ) ;
cb - > setManaPoints ( h - > id , h - > manaLimit ( ) ) ;
message = 77 ;
}
else
{
message = 79 ;
}
showInfoDialog ( h , message , soundBase : : faerie ) ;
}
2014-06-24 20:39:36 +03:00
std : : string CGMagicWell : : getHoverText ( const CGHeroInstance * hero ) const
2014-06-05 19:52:14 +03:00
{
2014-06-24 20:39:36 +03:00
return getObjectName ( ) + " " + visitedTxt ( hero - > hasBonusFrom ( Bonus : : OBJECT , ID ) ) ;
2014-06-05 19:52:14 +03:00
}
void CGObservatory : : onHeroVisit ( const CGHeroInstance * h ) const
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
switch ( ID )
{
case Obj : : REDWOOD_OBSERVATORY :
case Obj : : PILLAR_OF_FIRE :
{
iw . soundID = soundBase : : LIGHTHOUSE ;
iw . text . addTxt ( MetaString : : ADVOB_TXT , 98 + ( ID = = Obj : : PILLAR_OF_FIRE ) ) ;
FoWChange fw ;
fw . player = h - > tempOwner ;
fw . mode = 1 ;
cb - > getTilesInRange ( fw . tiles , pos , 20 , h - > tempOwner , 1 ) ;
cb - > sendAndApply ( & fw ) ;
break ;
}
case Obj : : COVER_OF_DARKNESS :
{
iw . text . addTxt ( MetaString : : ADVOB_TXT , 31 ) ;
2014-06-29 17:23:06 +03:00
for ( auto & player : cb - > gameState ( ) - > players )
2014-06-24 14:50:27 +03:00
{
if ( cb - > getPlayerStatus ( player . first ) = = EPlayerStatus : : INGAME & &
cb - > getPlayerRelations ( player . first , h - > tempOwner ) = = PlayerRelations : : ENEMIES )
cb - > changeFogOfWar ( visitablePos ( ) , 20 , player . first , true ) ;
}
2014-06-05 19:52:14 +03:00
break ;
}
}
cb - > showInfoDialog ( & iw ) ;
}
void CGShrine : : onHeroVisit ( const CGHeroInstance * h ) const
{
if ( spell = = SpellID : : NONE )
{
logGlobal - > errorStream ( ) < < " Not initialized shrine visited! " ;
return ;
}
if ( ! wasVisited ( h - > tempOwner ) )
2016-01-20 09:44:13 +02:00
cb - > setObjProperty ( id , CGShrine : : OBJPROP_VISITED , h - > tempOwner . getNum ( ) ) ;
2014-06-05 19:52:14 +03:00
InfoWindow iw ;
iw . soundID = soundBase : : temple ;
iw . player = h - > getOwner ( ) ;
iw . text . addTxt ( MetaString : : ADVOB_TXT , 127 + ID - 88 ) ;
iw . text . addTxt ( MetaString : : SPELL_NAME , spell ) ;
iw . text < < " . " ;
if ( ! h - > getArt ( ArtifactPosition : : SPELLBOOK ) )
{
iw . text . addTxt ( MetaString : : ADVOB_TXT , 131 ) ;
}
else if ( ID = = Obj : : SHRINE_OF_MAGIC_THOUGHT & & ! h - > getSecSkillLevel ( SecondarySkill : : WISDOM ) ) //it's third level spell and hero doesn't have wisdom
{
iw . text . addTxt ( MetaString : : ADVOB_TXT , 130 ) ;
}
else if ( vstd : : contains ( h - > spells , spell ) ) //hero already knows the spell
{
iw . text . addTxt ( MetaString : : ADVOB_TXT , 174 ) ;
}
else //give spell
{
std : : set < SpellID > spells ;
spells . insert ( spell ) ;
cb - > changeSpells ( h , true , spells ) ;
iw . components . push_back ( Component ( Component : : SPELL , spell , 0 , 0 ) ) ;
}
cb - > showInfoDialog ( & iw ) ;
}
void CGShrine : : initObj ( )
{
if ( spell = = SpellID : : NONE ) //spell not set
{
int level = ID - 87 ;
std : : vector < SpellID > possibilities ;
cb - > getAllowedSpells ( possibilities , level ) ;
if ( possibilities . empty ( ) )
{
logGlobal - > errorStream ( ) < < " Error: cannot init shrine, no allowed spells! " ;
return ;
}
spell = * RandomGeneratorUtil : : nextItem ( possibilities , cb - > gameState ( ) - > getRandomGenerator ( ) ) ;
}
}
2014-06-24 20:39:36 +03:00
std : : string CGShrine : : getHoverText ( PlayerColor player ) const
2014-06-05 19:52:14 +03:00
{
2014-06-24 20:39:36 +03:00
std : : string hoverName = getObjectName ( ) ;
if ( wasVisited ( player ) )
2014-06-05 19:52:14 +03:00
{
hoverName + = " \n " + VLC - > generaltexth - > allTexts [ 355 ] ; // + (learn %s)
boost : : algorithm : : replace_first ( hoverName , " %s " , spell . toSpell ( ) - > name ) ;
}
return hoverName ;
}
2014-06-24 20:39:36 +03:00
std : : string CGShrine : : getHoverText ( const CGHeroInstance * hero ) const
{
std : : string hoverName = getHoverText ( hero - > tempOwner ) ;
if ( vstd : : contains ( hero - > spells , spell ) ) //hero knows that spell
hoverName + = " \n \n " + VLC - > generaltexth - > allTexts [ 354 ] ; // (Already learned)
return hoverName ;
}
2015-11-16 15:30:40 +02:00
void CGShrine : : writeJsonOptions ( JsonNode & json ) const
{
}
void CGShrine : : readJsonOptions ( const JsonNode & json )
{
}
2014-06-05 19:52:14 +03:00
void CGSignBottle : : initObj ( )
{
//if no text is set than we pick random from the predefined ones
if ( message . empty ( ) )
{
message = * RandomGeneratorUtil : : nextItem ( VLC - > generaltexth - > randsign , cb - > gameState ( ) - > getRandomGenerator ( ) ) ;
}
if ( ID = = Obj : : OCEAN_BOTTLE )
{
blockVisit = true ;
}
}
void CGSignBottle : : onHeroVisit ( const CGHeroInstance * h ) const
{
InfoWindow iw ;
iw . soundID = soundBase : : STORE ;
iw . player = h - > getOwner ( ) ;
iw . text < < message ;
cb - > showInfoDialog ( & iw ) ;
if ( ID = = Obj : : OCEAN_BOTTLE )
cb - > removeObject ( this ) ;
}
2015-11-16 15:30:40 +02:00
void CGSignBottle : : writeJsonOptions ( JsonNode & json ) const
{
2016-02-04 07:50:46 +02:00
json [ " text " ] . String ( ) = message ;
2015-11-16 15:30:40 +02:00
}
void CGSignBottle : : readJsonOptions ( const JsonNode & json )
{
2016-02-04 07:50:46 +02:00
message = json [ " text " ] . String ( ) ;
2015-11-16 15:30:40 +02:00
}
2014-06-05 19:52:14 +03:00
void CGScholar : : onHeroVisit ( const CGHeroInstance * h ) const
{
EBonusType type = bonusType ;
int bid = bonusID ;
//check if the bonus if applicable, if not - give primary skill (always possible)
int ssl = h - > getSecSkillLevel ( SecondarySkill ( bid ) ) ; //current sec skill level, used if bonusType == 1
if ( ( type = = SECONDARY_SKILL
& & ( ( ssl = = 3 ) | | ( ! ssl & & ! h - > canLearnSkill ( ) ) ) ) ////hero already has expert level in the skill or (don't know skill and doesn't have free slot)
| | ( type = = SPELL & & ( ! h - > getArt ( ArtifactPosition : : SPELLBOOK ) | | vstd : : contains ( h - > spells , ( ui32 ) bid )
| | ( SpellID ( bid ) . toSpell ( ) - > level > h - > getSecSkillLevel ( SecondarySkill : : WISDOM ) + 2 )
) ) ) //hero doesn't have a spellbook or already knows the spell or doesn't have Wisdom
{
type = PRIM_SKILL ;
bid = cb - > gameState ( ) - > getRandomGenerator ( ) . nextInt ( GameConstants : : PRIMARY_SKILLS - 1 ) ;
}
InfoWindow iw ;
iw . soundID = soundBase : : gazebo ;
iw . player = h - > getOwner ( ) ;
iw . text . addTxt ( MetaString : : ADVOB_TXT , 115 ) ;
switch ( type )
{
case PRIM_SKILL :
cb - > changePrimSkill ( h , static_cast < PrimarySkill : : PrimarySkill > ( bid ) , + 1 ) ;
iw . components . push_back ( Component ( Component : : PRIM_SKILL , bid , + 1 , 0 ) ) ;
break ;
case SECONDARY_SKILL :
cb - > changeSecSkill ( h , SecondarySkill ( bid ) , + 1 ) ;
iw . components . push_back ( Component ( Component : : SEC_SKILL , bid , ssl + 1 , 0 ) ) ;
break ;
case SPELL :
{
std : : set < SpellID > hlp ;
hlp . insert ( SpellID ( bid ) ) ;
cb - > changeSpells ( h , true , hlp ) ;
iw . components . push_back ( Component ( Component : : SPELL , bid , 0 , 0 ) ) ;
}
break ;
default :
logGlobal - > errorStream ( ) < < " Error: wrong bonus type ( " < < ( int ) type < < " ) for Scholar! \n " ;
return ;
}
cb - > showInfoDialog ( & iw ) ;
cb - > removeObject ( this ) ;
}
void CGScholar : : initObj ( )
{
blockVisit = true ;
if ( bonusType = = RANDOM )
{
bonusType = static_cast < EBonusType > ( cb - > gameState ( ) - > getRandomGenerator ( ) . nextInt ( 2 ) ) ;
switch ( bonusType )
{
case PRIM_SKILL :
bonusID = cb - > gameState ( ) - > getRandomGenerator ( ) . nextInt ( GameConstants : : PRIMARY_SKILLS - 1 ) ;
break ;
case SECONDARY_SKILL :
bonusID = cb - > gameState ( ) - > getRandomGenerator ( ) . nextInt ( GameConstants : : SKILL_QUANTITY - 1 ) ;
break ;
case SPELL :
std : : vector < SpellID > possibilities ;
for ( int i = 1 ; i < 6 ; + + i )
cb - > getAllowedSpells ( possibilities , i ) ;
bonusID = * RandomGeneratorUtil : : nextItem ( possibilities , cb - > gameState ( ) - > getRandomGenerator ( ) ) ;
break ;
}
}
}
2015-11-16 15:30:40 +02:00
void CGScholar : : writeJsonOptions ( JsonNode & json ) const
{
2016-02-09 08:02:34 +02:00
switch ( bonusType )
{
case PRIM_SKILL :
json [ " rewardPrimSkill " ] . String ( ) = PrimarySkill : : names [ bonusID ] ;
break ;
case SECONDARY_SKILL :
json [ " rewardSkill " ] . String ( ) = NSecondarySkill : : names [ bonusID ] ;
break ;
case SPELL :
json [ " rewardSpell " ] . String ( ) = VLC - > spellh - > objects . at ( bonusID ) - > identifier ;
break ;
case RANDOM :
break ;
}
2015-11-16 15:30:40 +02:00
}
void CGScholar : : readJsonOptions ( const JsonNode & json )
{
2016-02-09 08:02:34 +02:00
bonusType = RANDOM ;
if ( json [ " rewardPrimSkill " ] . String ( ) ! = " " )
{
auto raw = VLC - > modh - > identifiers . getIdentifier ( " core " , " primSkill " , json [ " rewardPrimSkill " ] . String ( ) ) ;
if ( raw )
{
bonusType = PRIM_SKILL ;
bonusID = raw . get ( ) ;
}
}
else if ( json [ " rewardSkill " ] . String ( ) ! = " " )
{
auto raw = VLC - > modh - > identifiers . getIdentifier ( " core " , " skill " , json [ " rewardSkill " ] . String ( ) ) ;
if ( raw )
{
bonusType = SECONDARY_SKILL ;
bonusID = raw . get ( ) ;
}
}
else if ( json [ " rewardSpell " ] . String ( ) ! = " " )
{
auto raw = VLC - > modh - > identifiers . getIdentifier ( " core " , " spell " , json [ " rewardSpell " ] . String ( ) ) ;
if ( raw )
{
bonusType = SPELL ;
bonusID = raw . get ( ) ;
}
}
2015-11-16 15:30:40 +02:00
}
2014-06-05 19:52:14 +03:00
void CGGarrison : : onHeroVisit ( const CGHeroInstance * h ) const
{
int ally = cb - > gameState ( ) - > getPlayerRelations ( h - > tempOwner , tempOwner ) ;
if ( ! ally & & stacksCount ( ) > 0 ) {
//TODO: Find a way to apply magic garrison effects in battle.
cb - > startBattleI ( h , this ) ;
return ;
}
//New owner.
if ( ! ally )
cb - > setOwner ( this , h - > tempOwner ) ;
cb - > showGarrisonDialog ( id , h - > id , removableUnits ) ;
}
2014-06-24 02:26:36 +03:00
bool CGGarrison : : passableFor ( PlayerColor player ) const
2014-06-05 19:52:14 +03:00
{
2014-06-24 02:26:36 +03:00
//FIXME: identical to same method in CGTownInstance
2014-06-05 19:52:14 +03:00
if ( ! stacksCount ( ) ) //empty - anyone can visit
2014-06-24 02:26:36 +03:00
return true ;
2014-06-05 19:52:14 +03:00
if ( tempOwner = = PlayerColor : : NEUTRAL ) //neutral guarded - no one can visit
2014-06-24 02:26:36 +03:00
return false ;
2014-06-05 19:52:14 +03:00
2014-06-24 02:26:36 +03:00
if ( cb - > getPlayerRelations ( tempOwner , player ) ! = PlayerRelations : : ENEMIES )
return true ;
return false ;
2014-06-05 19:52:14 +03:00
}
void CGGarrison : : battleFinished ( const CGHeroInstance * hero , const BattleResult & result ) const
{
if ( result . winner = = 0 )
onHeroVisit ( hero ) ;
}
2015-11-16 15:30:40 +02:00
void CGGarrison : : writeJsonOptions ( JsonNode & json ) const
{
2016-01-23 18:53:02 +02:00
CArmedInstance : : writeJsonOptions ( json ) ;
CGObjectInstance : : writeOwner ( json ) ;
json [ " removableUnits " ] . Bool ( ) = removableUnits ;
2015-11-16 15:30:40 +02:00
}
void CGGarrison : : readJsonOptions ( const JsonNode & json )
{
2016-01-23 18:53:02 +02:00
CArmedInstance : : readJsonOptions ( json ) ;
CGObjectInstance : : readOwner ( json ) ;
removableUnits = json [ " removableUnits " ] . Bool ( ) ;
2015-11-16 15:30:40 +02:00
}
2014-06-05 19:52:14 +03:00
void CGMagi : : initObj ( )
{
if ( ID = = Obj : : EYE_OF_MAGI )
{
blockVisit = true ;
eyelist [ subID ] . push_back ( id ) ;
}
}
void CGMagi : : onHeroVisit ( const CGHeroInstance * h ) const
{
if ( ID = = Obj : : HUT_OF_MAGI )
{
showInfoDialog ( h , 61 , soundBase : : LIGHTHOUSE ) ;
if ( ! eyelist [ subID ] . empty ( ) )
{
CenterView cv ;
cv . player = h - > tempOwner ;
cv . focusTime = 2000 ;
FoWChange fw ;
fw . player = h - > tempOwner ;
fw . mode = 1 ;
2014-06-21 16:41:05 +03:00
fw . waitForDialogs = true ;
2014-06-05 19:52:14 +03:00
for ( auto it : eyelist [ subID ] )
{
const CGObjectInstance * eye = cb - > getObj ( it ) ;
cb - > getTilesInRange ( fw . tiles , eye - > pos , 10 , h - > tempOwner , 1 ) ;
cb - > sendAndApply ( & fw ) ;
cv . pos = eye - > pos ;
cb - > sendAndApply ( & cv ) ;
}
cv . pos = h - > getPosition ( false ) ;
2016-01-22 23:35:41 +02:00
cv . focusTime = 0 ;
2014-06-05 19:52:14 +03:00
cb - > sendAndApply ( & cv ) ;
}
}
else if ( ID = = Obj : : EYE_OF_MAGI )
{
showInfoDialog ( h , 48 , soundBase : : invalid ) ;
}
}
void CGBoat : : initObj ( )
{
hero = nullptr ;
}
void CGSirens : : initObj ( )
{
blockVisit = true ;
}
2014-06-24 20:39:36 +03:00
std : : string CGSirens : : getHoverText ( const CGHeroInstance * hero ) const
2014-06-05 19:52:14 +03:00
{
2014-06-24 20:39:36 +03:00
return getObjectName ( ) + " " + visitedTxt ( hero - > hasBonusFrom ( Bonus : : OBJECT , ID ) ) ;
2014-06-05 19:52:14 +03:00
}
void CGSirens : : onHeroVisit ( const CGHeroInstance * h ) const
{
InfoWindow iw ;
iw . soundID = soundBase : : DANGER ;
iw . player = h - > tempOwner ;
if ( h - > hasBonusFrom ( Bonus : : OBJECT , ID ) ) //has already visited Sirens
{
iw . text . addTxt ( MetaString : : ADVOB_TXT , 133 ) ;
}
else
{
giveDummyBonus ( h - > id , Bonus : : ONE_BATTLE ) ;
TExpType xp = 0 ;
for ( auto i = h - > Slots ( ) . begin ( ) ; i ! = h - > Slots ( ) . end ( ) ; i + + )
{
TQuantity drown = i - > second - > count * 0.3 ;
if ( drown )
{
cb - > changeStackCount ( StackLocation ( h , i - > first ) , - drown ) ;
xp + = drown * i - > second - > type - > valOfBonuses ( Bonus : : STACK_HEALTH ) ;
}
}
if ( xp )
{
xp = h - > calculateXp ( xp ) ;
iw . text . addTxt ( MetaString : : ADVOB_TXT , 132 ) ;
iw . text . addReplacement ( xp ) ;
cb - > changePrimSkill ( h , PrimarySkill : : EXPERIENCE , xp , false ) ;
}
else
{
iw . text . addTxt ( MetaString : : ADVOB_TXT , 134 ) ;
}
}
cb - > showInfoDialog ( & iw ) ;
}
CGShipyard : : CGShipyard ( )
: IShipyard ( this )
{
}
void CGShipyard : : getOutOffsets ( std : : vector < int3 > & offsets ) const
{
// H J L K I
// A x S x B
// C E G F D
2014-10-03 23:34:13 +03:00
offsets = {
int3 ( - 3 , 0 , 0 ) , int3 ( 1 , 0 , 0 ) , //AB
2014-06-05 19:52:14 +03:00
int3 ( - 3 , 1 , 0 ) , int3 ( 1 , 1 , 0 ) , int3 ( - 2 , 1 , 0 ) , int3 ( 0 , 1 , 0 ) , int3 ( - 1 , 1 , 0 ) , //CDEFG
2014-10-03 23:34:13 +03:00
int3 ( - 3 , - 1 , 0 ) , int3 ( 1 , - 1 , 0 ) , int3 ( - 2 , - 1 , 0 ) , int3 ( 0 , - 1 , 0 ) , int3 ( - 1 , - 1 , 0 ) //HIJKL
2015-11-16 15:30:40 +02:00
} ;
2014-06-05 19:52:14 +03:00
}
void CGShipyard : : onHeroVisit ( const CGHeroInstance * h ) const
{
if ( ! cb - > gameState ( ) - > getPlayerRelations ( tempOwner , h - > tempOwner ) )
cb - > setOwner ( this , h - > tempOwner ) ;
auto s = shipyardStatus ( ) ;
if ( s ! = IBoatGenerator : : GOOD )
{
InfoWindow iw ;
iw . player = tempOwner ;
getProblemText ( iw . text , h ) ;
cb - > showInfoDialog ( & iw ) ;
}
else
{
openWindow ( OpenWindow : : SHIPYARD_WINDOW , id . getNum ( ) , h - > id . getNum ( ) ) ;
}
}
2015-11-16 15:30:40 +02:00
void CGShipyard : : writeJsonOptions ( JsonNode & json ) const
{
}
void CGShipyard : : readJsonOptions ( const JsonNode & json )
{
}
2014-06-05 19:52:14 +03:00
void CCartographer : : onHeroVisit ( const CGHeroInstance * h ) const
{
2014-12-09 13:01:32 +02:00
//if player has not bought map of this subtype yet and underground exist for stalagmite cartographer
if ( ! wasVisited ( h - > getOwner ( ) ) & & ( subID ! = 2 | | cb - > gameState ( ) - > map - > twoLevel ) )
2014-06-05 19:52:14 +03:00
{
if ( cb - > getResource ( h - > tempOwner , Res : : GOLD ) > = 1000 ) //if he can afford a map
{
//ask if he wants to buy one
int text = 0 ;
switch ( subID )
{
case 0 :
text = 25 ;
break ;
case 1 :
text = 26 ;
break ;
case 2 :
text = 27 ;
break ;
default :
logGlobal - > warnStream ( ) < < " Unrecognized subtype of cartographer " ;
}
assert ( text ) ;
BlockingDialog bd ( true , false ) ;
bd . player = h - > getOwner ( ) ;
bd . soundID = soundBase : : LIGHTHOUSE ;
bd . text . addTxt ( MetaString : : ADVOB_TXT , text ) ;
cb - > showBlockingDialog ( & bd ) ;
}
else //if he cannot afford
{
showInfoDialog ( h , 28 , soundBase : : CAVEHEAD ) ;
}
}
else //if he already visited carographer
{
showInfoDialog ( h , 24 , soundBase : : CAVEHEAD ) ;
}
}
void CCartographer : : blockingDialogAnswered ( const CGHeroInstance * hero , ui32 answer ) const
{
if ( answer ) //if hero wants to buy map
{
cb - > giveResource ( hero - > tempOwner , Res : : GOLD , - 1000 ) ;
FoWChange fw ;
fw . mode = 1 ;
fw . player = hero - > tempOwner ;
//subIDs of different types of cartographers:
//water = 0; land = 1; underground = 2;
cb - > getAllTiles ( fw . tiles , hero - > tempOwner , subID - 1 , ! subID + 1 ) ; //reveal appropriate tiles
cb - > sendAndApply ( & fw ) ;
2016-01-20 09:44:13 +02:00
cb - > setObjProperty ( id , CCartographer : : OBJPROP_VISITED , hero - > tempOwner . getNum ( ) ) ;
2014-06-05 19:52:14 +03:00
}
}
void CGDenOfthieves : : onHeroVisit ( const CGHeroInstance * h ) const
{
cb - > showThievesGuildWindow ( h - > tempOwner , id ) ;
}
void CGObelisk : : onHeroVisit ( const CGHeroInstance * h ) const
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
TeamState * ts = cb - > gameState ( ) - > getPlayerTeam ( h - > tempOwner ) ;
assert ( ts ) ;
TeamID team = ts - > id ;
if ( ! wasVisited ( team ) )
{
iw . text . addTxt ( MetaString : : ADVOB_TXT , 96 ) ;
cb - > sendAndApply ( & iw ) ;
2016-01-20 09:44:13 +02:00
// increment general visited obelisks counter
cb - > setObjProperty ( id , CGObelisk : : OBJPROP_INC , team . getNum ( ) ) ;
2014-06-05 19:52:14 +03:00
openWindow ( OpenWindow : : PUZZLE_MAP , h - > tempOwner . getNum ( ) ) ;
2016-01-20 10:14:03 +02:00
// mark that particular obelisk as visited for all players in the team
for ( auto & color : ts - > players )
{
cb - > setObjProperty ( id , CGObelisk : : OBJPROP_VISITED , color . getNum ( ) ) ;
}
2014-06-05 19:52:14 +03:00
}
else
{
iw . text . addTxt ( MetaString : : ADVOB_TXT , 97 ) ;
cb - > sendAndApply ( & iw ) ;
}
}
void CGObelisk : : initObj ( )
{
obeliskCount + + ;
}
2016-01-20 12:02:52 +02:00
void CGObelisk : : reset ( )
{
obeliskCount = 0 ;
visited . clear ( ) ;
}
2014-06-24 20:39:36 +03:00
std : : string CGObelisk : : getHoverText ( PlayerColor player ) const
2014-06-05 19:52:14 +03:00
{
2014-06-24 20:39:36 +03:00
return getObjectName ( ) + " " + visitedTxt ( wasVisited ( player ) ) ;
2014-06-05 19:52:14 +03:00
}
void CGObelisk : : setPropertyDer ( ui8 what , ui32 val )
{
switch ( what )
{
2016-01-20 09:44:13 +02:00
case CGObelisk : : OBJPROP_INC :
{
assert ( val < PlayerColor : : PLAYER_LIMIT_I ) ;
auto progress = + + visited [ TeamID ( val ) ] ;
logGlobal - > debugStream ( ) < < boost : : format ( " Player %d: obelisk progress %d / %d " )
% val % static_cast < int > ( progress ) % static_cast < int > ( obeliskCount ) ;
2014-06-05 19:52:14 +03:00
2016-01-20 09:44:13 +02:00
if ( progress > obeliskCount )
{
logGlobal - > errorStream ( ) < < " Error: Visited " < < progress < < " \t \t " < < obeliskCount ;
assert ( 0 ) ;
}
2014-06-05 19:52:14 +03:00
2016-01-20 09:44:13 +02:00
break ;
}
default :
CPlayersVisited : : setPropertyDer ( what , val ) ;
break ;
2014-06-05 19:52:14 +03:00
}
}
void CGLighthouse : : onHeroVisit ( const CGHeroInstance * h ) const
{
if ( h - > tempOwner ! = tempOwner )
{
PlayerColor oldOwner = tempOwner ;
cb - > setOwner ( this , h - > tempOwner ) ; //not ours? flag it!
showInfoDialog ( h , 69 , soundBase : : LIGHTHOUSE ) ;
giveBonusTo ( h - > tempOwner ) ;
if ( oldOwner < PlayerColor : : PLAYER_LIMIT ) //remove bonus from old owner
{
RemoveBonus rb ( RemoveBonus : : PLAYER ) ;
rb . whoID = oldOwner . getNum ( ) ;
rb . source = Bonus : : OBJECT ;
rb . id = id . getNum ( ) ;
cb - > sendAndApply ( & rb ) ;
}
}
}
void CGLighthouse : : initObj ( )
{
if ( tempOwner < PlayerColor : : PLAYER_LIMIT )
{
giveBonusTo ( tempOwner ) ;
}
}
2014-06-24 20:39:36 +03:00
std : : string CGLighthouse : : getHoverText ( PlayerColor player ) const
2014-06-05 19:52:14 +03:00
{
//TODO: owned by %s player
2014-06-24 20:39:36 +03:00
return getObjectName ( ) ;
2014-06-05 19:52:14 +03:00
}
void CGLighthouse : : giveBonusTo ( PlayerColor player ) const
{
GiveBonus gb ( GiveBonus : : PLAYER ) ;
gb . bonus . type = Bonus : : SEA_MOVEMENT ;
gb . bonus . val = 500 ;
gb . id = player . getNum ( ) ;
gb . bonus . duration = Bonus : : PERMANENT ;
gb . bonus . source = Bonus : : OBJECT ;
gb . bonus . sid = id . getNum ( ) ;
cb - > sendAndApply ( & gb ) ;
}
2015-11-16 15:30:40 +02:00
void CGLighthouse : : writeJsonOptions ( JsonNode & json ) const
{
2016-02-03 03:18:03 +02:00
CGObjectInstance : : writeOwner ( json ) ;
2015-11-16 15:30:40 +02:00
}
void CGLighthouse : : readJsonOptions ( const JsonNode & json )
{
2016-02-03 03:18:03 +02:00
CGObjectInstance : : readOwner ( json ) ;
2015-11-16 15:30:40 +02:00
}