2011-12-14 00:23:17 +03:00
# include "StdInc.h"
# include "../lib/int3.h"
2010-12-20 23:22:53 +02:00
# include "../lib/CCampaignHandler.h"
2011-12-14 00:23:17 +03:00
# include "../lib/StartInfo.h"
2010-12-20 23:22:53 +02:00
# include "../lib/CArtHandler.h"
# include "../lib/CBuildingHandler.h"
# include "../lib/CDefObjInfoHandler.h"
# include "../lib/CHeroHandler.h"
# include "../lib/CObjectHandler.h"
# include "../lib/CSpellHandler.h"
# include "../lib/CGeneralTextHandler.h"
# include "../lib/CTownHandler.h"
# include "../lib/CCreatureHandler.h"
2009-05-20 13:08:56 +03:00
# include "../lib/CGameState.h"
2010-12-25 21:23:30 +02:00
# include "../lib/BattleState.h"
2008-08-04 18:56:36 +03:00
# include "../lib/CondSh.h"
2008-11-28 03:36:34 +02:00
# include "../lib/NetPacks.h"
# include "../lib/VCMI_Lib.h"
2009-05-20 13:08:56 +03:00
# include "../lib/map.h"
2009-10-10 08:47:59 +03:00
# include "../lib/VCMIDirs.h"
2010-12-20 23:22:53 +02:00
# include "../client/CSoundBase.h"
2008-11-28 03:36:34 +02:00
# include "CGameHandler.h"
2011-04-23 20:10:54 +03:00
# include <boost/random/mersenne_twister.hpp>
# include <boost/random/variate_generator.hpp>
# include <boost/random/poisson_distribution.hpp>
2011-05-10 01:20:47 +03:00
# include "../lib/CCreatureSet.h"
2011-12-14 00:23:17 +03:00
# include "../lib/CThreadHelper.h"
# include "../lib/GameConstants.h"
# include "../lib/RegisterTypes.h"
2009-03-09 12:37:49 +02:00
2009-04-15 17:03:31 +03:00
/*
* CGameHandler . 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
*
*/
2011-12-14 00:23:17 +03:00
2008-08-09 02:02:32 +03:00
# ifndef _MSC_VER
2008-08-04 12:05:52 +03:00
# include <boost/thread/xtime.hpp>
2008-08-09 02:02:32 +03:00
# endif
2008-08-04 12:05:52 +03:00
extern bool end2 ;
2008-08-04 18:56:36 +03:00
# ifdef min
# undef min
# endif
# ifdef max
# undef max
# endif
2010-03-11 01:16:30 +02:00
2010-12-13 01:44:16 +02:00
# define COMPLAIN_RET_IF(cond, txt) do {if(cond){complain(txt); return;}} while(0)
2010-03-11 01:16:30 +02:00
# define COMPLAIN_RET(txt) {complain(txt); return false;}
2012-01-03 04:55:26 +03:00
# define COMPLAIN_RETF(txt, FORMAT) {complain(boost::str(boost::format(txt) % FORMAT)); return false;}
2008-08-04 18:56:36 +03:00
# define NEW_ROUND BattleNextRound bnr;\
bnr . round = gs - > curB - > round + 1 ; \
sendAndApply ( & bnr ) ;
2008-07-25 20:28:28 +03:00
2008-08-04 18:56:36 +03:00
CondSh < bool > battleMadeAction ;
CondSh < BattleResult * > battleResult ( NULL ) ;
2010-08-22 21:21:45 +03:00
std : : ptrdiff_t randomizer ( ptrdiff_t i ) { return rand ( ) ; }
2010-08-22 10:54:24 +03:00
std : : ptrdiff_t ( * p_myrandom ) ( std : : ptrdiff_t ) = randomizer ;
2008-08-13 03:44:31 +03:00
2010-09-04 17:47:39 +03:00
template < typename T > class CApplyOnGH ;
2010-09-03 21:42:54 +03:00
2009-03-09 12:37:49 +02:00
class CBaseForGHApply
{
public :
2012-01-03 04:55:26 +03:00
virtual bool applyOnGH ( CGameHandler * gh , CConnection * c , void * pack , ui8 player ) const = 0 ;
2010-09-03 21:42:54 +03:00
virtual ~ CBaseForGHApply ( ) { }
template < typename U > static CBaseForGHApply * getApplier ( const U * t = NULL )
{
return new CApplyOnGH < U > ;
}
2009-03-09 12:37:49 +02:00
} ;
2010-09-04 17:47:39 +03:00
2009-03-09 12:37:49 +02:00
template < typename T > class CApplyOnGH : public CBaseForGHApply
{
public :
2012-01-03 04:55:26 +03:00
bool applyOnGH ( CGameHandler * gh , CConnection * c , void * pack , ui8 player ) const
2009-03-09 12:37:49 +02:00
{
T * ptr = static_cast < T * > ( pack ) ;
2009-03-09 21:40:43 +02:00
ptr - > c = c ;
2012-01-03 04:55:26 +03:00
ptr - > player = player ;
2009-04-16 03:28:54 +03:00
return ptr - > applyGh ( gh ) ;
2009-03-09 12:37:49 +02:00
}
} ;
2010-09-04 17:47:39 +03:00
static CApplier < CBaseForGHApply > * applier = NULL ;
2009-03-09 12:37:49 +02:00
2009-09-07 05:29:44 +03:00
CMP_stack cmpst ;
2008-08-04 18:56:36 +03:00
2009-05-24 01:57:39 +03:00
static inline double distance ( int3 a , int3 b )
2008-07-28 15:44:08 +03:00
{
return std : : sqrt ( ( double ) ( a . x - b . x ) * ( a . x - b . x ) + ( a . y - b . y ) * ( a . y - b . y ) ) ;
}
2009-05-24 01:57:39 +03:00
static void giveExp ( BattleResult & r )
2008-09-29 13:16:02 +03:00
{
r . exp [ 0 ] = 0 ;
r . exp [ 1 ] = 0 ;
2009-09-24 16:23:52 +03:00
for ( std : : map < ui32 , si32 > : : iterator i = r . casualties [ ! r . winner ] . begin ( ) ; i ! = r . casualties [ ! r . winner ] . end ( ) ; i + + )
2008-09-29 13:16:02 +03:00
{
2010-05-02 21:20:26 +03:00
r . exp [ r . winner ] + = VLC - > creh - > creatures [ i - > first ] - > valOfBonuses ( Bonus : : STACK_HEALTH ) * i - > second ;
2008-09-29 13:16:02 +03:00
}
}
2009-03-31 23:47:53 +03:00
2008-08-13 03:44:31 +03:00
PlayerStatus PlayerStatuses : : operator [ ] ( ui8 player )
{
boost : : unique_lock < boost : : mutex > l ( mx ) ;
if ( players . find ( player ) ! = players . end ( ) )
{
return players [ player ] ;
}
else
{
throw std : : string ( " No such player! " ) ;
}
}
void PlayerStatuses : : addPlayer ( ui8 player )
{
boost : : unique_lock < boost : : mutex > l ( mx ) ;
players [ player ] ;
}
2012-01-03 04:55:26 +03:00
int PlayerStatuses : : getQueriesCount ( ui8 player )
2008-08-13 03:44:31 +03:00
{
boost : : unique_lock < boost : : mutex > l ( mx ) ;
if ( players . find ( player ) ! = players . end ( ) )
{
return players [ player ] . queries . size ( ) ;
}
else
{
2012-01-03 04:55:26 +03:00
return 0 ;
2008-08-13 03:44:31 +03:00
}
}
2012-01-03 04:55:26 +03:00
2008-08-13 03:44:31 +03:00
bool PlayerStatuses : : checkFlag ( ui8 player , bool PlayerStatus : : * flag )
{
boost : : unique_lock < boost : : mutex > l ( mx ) ;
if ( players . find ( player ) ! = players . end ( ) )
{
return players [ player ] . * flag ;
}
else
{
throw std : : string ( " No such player! " ) ;
}
}
void PlayerStatuses : : setFlag ( ui8 player , bool PlayerStatus : : * flag , bool val )
{
boost : : unique_lock < boost : : mutex > l ( mx ) ;
if ( players . find ( player ) ! = players . end ( ) )
{
players [ player ] . * flag = val ;
}
else
{
throw std : : string ( " No such player! " ) ;
}
cv . notify_all ( ) ;
}
void PlayerStatuses : : addQuery ( ui8 player , ui32 id )
{
boost : : unique_lock < boost : : mutex > l ( mx ) ;
if ( players . find ( player ) ! = players . end ( ) )
{
players [ player ] . queries . insert ( id ) ;
}
else
{
throw std : : string ( " No such player! " ) ;
}
cv . notify_all ( ) ;
}
void PlayerStatuses : : removeQuery ( ui8 player , ui32 id )
{
boost : : unique_lock < boost : : mutex > l ( mx ) ;
if ( players . find ( player ) ! = players . end ( ) )
{
players [ player ] . queries . erase ( id ) ;
}
else
{
throw std : : string ( " No such player! " ) ;
}
cv . notify_all ( ) ;
}
2009-03-31 23:47:53 +03:00
2008-08-13 03:44:31 +03:00
template < typename T >
void callWith ( std : : vector < T > args , boost : : function < void ( T ) > fun , ui32 which )
{
fun ( args [ which ] ) ;
}
2010-07-29 01:39:56 +03:00
void CGameHandler : : levelUpHero ( int ID , int skill )
2008-08-13 03:44:31 +03:00
{
2010-07-29 01:39:56 +03:00
changeSecSkill ( ID , skill , 1 , 0 ) ;
levelUpHero ( ID ) ;
}
2009-07-31 23:10:22 +03:00
2010-07-29 01:39:56 +03:00
void CGameHandler : : levelUpHero ( int ID )
{
2010-12-19 00:11:28 +02:00
CGHeroInstance * hero = static_cast < CGHeroInstance * > ( gs - > map - > objects [ ID ] . get ( ) ) ;
2011-07-30 18:04:34 +03:00
// required exp for at least 1 lvl-up hasn't been reached
2011-07-17 16:56:55 +03:00
if ( hero - > exp < VLC - > heroh - > reqExp ( hero - > level + 1 ) )
2011-07-30 21:02:31 +03:00
{
afterBattleCallback ( ) ;
2010-07-29 01:39:56 +03:00
return ;
2011-07-30 21:02:31 +03:00
}
2010-07-29 01:39:56 +03:00
//give prim skill
tlog5 < < hero - > name < < " got level " < < hero - > level < < std : : endl ;
int r = rand ( ) % 100 , pom = 0 , x = 0 ;
int std : : pair < int , int > : : * g = ( hero - > level > 9 ) ? ( & std : : pair < int , int > : : second ) : ( & std : : pair < int , int > : : first ) ;
2011-12-14 00:23:17 +03:00
for ( ; x < GameConstants : : PRIMARY_SKILLS ; x + + )
2009-07-31 23:10:22 +03:00
{
2010-07-29 01:39:56 +03:00
pom + = hero - > type - > heroClass - > primChance [ x ] . * g ;
if ( r < pom )
break ;
}
2011-07-30 18:04:34 +03:00
tlog5 < < " The hero gets the primary skill with the no. " < < x < < " with a probability of " < < r < < " %. " < < std : : endl ;
2010-07-29 01:39:56 +03:00
SetPrimSkill sps ;
sps . id = ID ;
sps . which = x ;
sps . abs = false ;
sps . val = 1 ;
sendAndApply ( & sps ) ;
HeroLevelUp hlu ;
hlu . heroid = ID ;
hlu . primskill = x ;
hlu . level = hero - > level + 1 ;
//picking sec. skills for choice
std : : set < int > basicAndAdv , expert , none ;
2011-12-14 00:23:17 +03:00
for ( int i = 0 ; i < GameConstants : : SKILL_QUANTITY ; i + + )
2010-07-29 01:39:56 +03:00
if ( isAllowed ( 2 , i ) )
none . insert ( i ) ;
for ( unsigned i = 0 ; i < hero - > secSkills . size ( ) ; i + + )
{
if ( hero - > secSkills [ i ] . second < 3 )
basicAndAdv . insert ( hero - > secSkills [ i ] . first ) ;
else
expert . insert ( hero - > secSkills [ i ] . first ) ;
none . erase ( hero - > secSkills [ i ] . first ) ;
}
//first offered skill
if ( basicAndAdv . size ( ) )
{
int s = hero - > type - > heroClass - > chooseSecSkill ( basicAndAdv ) ; //upgrade existing
hlu . skills . push_back ( s ) ;
basicAndAdv . erase ( s ) ;
}
2010-08-19 14:03:33 +03:00
else if ( none . size ( ) & & hero - > secSkills . size ( ) < hero - > type - > heroClass - > skillLimit )
2010-07-29 01:39:56 +03:00
{
hlu . skills . push_back ( hero - > type - > heroClass - > chooseSecSkill ( none ) ) ; //give new skill
none . erase ( hlu . skills . back ( ) ) ;
}
//second offered skill
2010-08-19 14:03:33 +03:00
if ( none . size ( ) & & hero - > secSkills . size ( ) < hero - > type - > heroClass - > skillLimit ) //hero have free skill slot
2010-07-29 01:39:56 +03:00
{
hlu . skills . push_back ( hero - > type - > heroClass - > chooseSecSkill ( none ) ) ; //new skill
}
else if ( basicAndAdv . size ( ) )
{
hlu . skills . push_back ( hero - > type - > heroClass - > chooseSecSkill ( basicAndAdv ) ) ; //upgrade existing
}
2011-07-17 21:11:03 +03:00
if ( hlu . skills . size ( ) > 1 ) //apply and ask for secondary skill
2010-07-29 01:39:56 +03:00
{
2011-07-17 21:11:03 +03:00
boost : : function < void ( ui32 ) > callback = boost : : function < void ( ui32 ) > ( boost : : bind ( callWith < ui16 > , hlu . skills , boost : : function < void ( ui16 ) > ( boost : : bind ( & CGameHandler : : levelUpHero , this , ID , _1 ) ) , _1 ) ) ;
applyAndAsk ( & hlu , hero - > tempOwner , callback ) ; //call levelUpHero when client responds
2010-07-29 01:39:56 +03:00
}
2011-07-17 21:11:03 +03:00
else if ( hlu . skills . size ( ) = = 1 ) //apply, give only possible skill and send info
2010-07-29 01:39:56 +03:00
{
2011-07-17 21:11:03 +03:00
sendAndApply ( & hlu ) ;
levelUpHero ( ID , hlu . skills . back ( ) ) ;
}
else //apply and send info
{
sendAndApply ( & hlu ) ;
levelUpHero ( ID ) ;
2009-07-31 23:10:22 +03:00
}
2008-08-13 03:44:31 +03:00
}
2009-01-11 00:08:18 +02:00
2009-08-16 18:39:18 +03:00
void CGameHandler : : changePrimSkill ( int ID , int which , si64 val , bool abs )
2008-08-04 18:56:36 +03:00
{
SetPrimSkill sps ;
sps . id = ID ;
sps . which = which ;
sps . abs = abs ;
sps . val = val ;
sendAndApply ( & sps ) ;
2011-07-30 18:04:34 +03:00
//only for exp - hero may level up
if ( which = = 4 )
2008-08-04 18:56:36 +03:00
{
2010-07-29 01:39:56 +03:00
levelUpHero ( ID ) ;
2011-07-30 18:04:34 +03:00
2011-02-12 20:48:11 +02:00
//TODO: Commander
2011-07-30 18:04:34 +03:00
//TODO: Stack Experience only after battle
2010-07-29 01:39:56 +03:00
}
}
2009-02-14 17:00:29 +02:00
2010-07-29 01:39:56 +03:00
void CGameHandler : : changeSecSkill ( int ID , int which , int val , bool abs /*=false*/ )
{
SetSecSkill sss ;
sss . id = ID ;
sss . which = which ;
sss . val = val ;
sss . abs = abs ;
sendAndApply ( & sss ) ;
2009-02-14 17:00:29 +02:00
2010-07-29 01:39:56 +03:00
if ( which = = 7 ) //Wisdom
{
const CGHeroInstance * h = getHero ( ID ) ;
if ( h & & h - > visitedTown )
giveSpells ( h - > visitedTown , h ) ;
2008-08-04 18:56:36 +03:00
}
}
2010-12-22 22:14:40 +02:00
void CGameHandler : : startBattle ( const CArmedInstance * armies [ 2 ] , int3 tile , const CGHeroInstance * heroes [ 2 ] , bool creatureBank , boost : : function < void ( BattleResult * ) > cb , const CGTownInstance * town /*= NULL*/ )
2008-08-04 18:56:36 +03:00
{
2009-09-28 17:21:48 +03:00
battleEndCallback = new boost : : function < void ( BattleResult * ) > ( cb ) ;
2010-05-19 21:06:16 +03:00
{
2010-12-22 22:14:40 +02:00
setupBattle ( tile , armies , heroes , creatureBank , town ) ; //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
2010-05-19 21:06:16 +03:00
}
2010-12-22 22:14:40 +02:00
runBattle ( ) ;
2009-09-28 17:21:48 +03:00
}
void CGameHandler : : endBattle ( int3 tile , const CGHeroInstance * hero1 , const CGHeroInstance * hero2 )
{
2011-01-08 21:38:42 +02:00
bool duel = gs - > initialOpts - > mode = = StartInfo : : DUEL ;
2009-08-04 02:53:18 +03:00
BattleResultsApplied resultsApplied ;
2011-01-08 20:33:40 +02:00
const CArmedInstance * bEndArmy1 = gs - > curB - > belligerents [ 0 ] ;
2011-01-09 19:41:46 +02:00
const CArmedInstance * bEndArmy2 = gs - > curB - > belligerents [ 1 ] ;
2009-09-28 17:21:48 +03:00
resultsApplied . player1 = bEndArmy1 - > tempOwner ;
resultsApplied . player2 = bEndArmy2 - > tempOwner ;
2011-02-14 00:46:04 +02:00
const CGHeroInstance * victoriousHero = gs - > curB - > heroes [ battleResult . data - > winner ] ;
2009-08-04 02:53:18 +03:00
2011-01-08 21:38:42 +02:00
if ( ! duel )
{
//unblock engaged players
2011-12-14 00:23:17 +03:00
if ( bEndArmy1 - > tempOwner < GameConstants : : PLAYER_LIMIT )
2011-01-08 21:38:42 +02:00
states . setFlag ( bEndArmy1 - > tempOwner , & PlayerStatus : : engagedIntoBattle , false ) ;
2011-12-14 00:23:17 +03:00
if ( bEndArmy2 & & bEndArmy2 - > tempOwner < GameConstants : : PLAYER_LIMIT )
2011-01-08 21:38:42 +02:00
states . setFlag ( bEndArmy2 - > tempOwner , & PlayerStatus : : engagedIntoBattle , false ) ;
}
2008-09-20 21:30:37 +03:00
2008-08-04 18:56:36 +03:00
//end battle, remove all info, free memory
2008-09-29 13:16:02 +03:00
giveExp ( * battleResult . data ) ;
2010-07-26 01:47:59 +03:00
if ( hero1 )
2011-02-12 20:48:11 +02:00
battleResult . data - > exp [ 0 ] = hero1 - > calculateXp ( battleResult . data - > exp [ 0 ] ) ; //scholar skill
2010-07-26 01:47:59 +03:00
if ( hero2 )
2011-02-12 20:48:11 +02:00
battleResult . data - > exp [ 1 ] = hero2 - > calculateXp ( battleResult . data - > exp [ 1 ] ) ;
2010-12-06 01:10:02 +02:00
ui8 sides [ 2 ] ;
2011-01-09 19:41:46 +02:00
for ( int i = 0 ; i < 2 ; + + i )
{
sides [ i ] = gs - > curB - > sides [ i ] ;
}
2010-12-06 01:10:02 +02:00
ui8 loser = sides [ ! battleResult . data - > winner ] ;
2010-12-12 01:11:26 +02:00
CasualtiesAfterBattle cab1 ( bEndArmy1 , gs - > curB ) , cab2 ( bEndArmy2 , gs - > curB ) ; //calculate casualties before deleting battle
2011-02-14 00:46:04 +02:00
ChangeSpells cs ; //for Eagle Eye
2011-02-12 18:12:48 +02:00
2011-02-14 00:46:04 +02:00
if ( victoriousHero )
{
if ( int eagleEyeLevel = victoriousHero - > getSecSkillLevel ( CGHeroInstance : : EAGLE_EYE ) )
{
int maxLevel = eagleEyeLevel + 1 ;
double eagleEyeChance = victoriousHero - > valOfBonuses ( Bonus : : SECONDARY_SKILL_PREMY , CGHeroInstance : : EAGLE_EYE ) ;
BOOST_FOREACH ( const CSpell * sp , gs - > curB - > usedSpellsHistory [ ! battleResult . data - > winner ] )
if ( sp - > level < = maxLevel & & ! vstd : : contains ( victoriousHero - > spells , sp - > id ) & & rand ( ) % 100 < eagleEyeChance )
cs . spells . insert ( sp - > id ) ;
}
}
2011-02-12 18:12:48 +02:00
2010-12-12 01:11:26 +02:00
sendAndApply ( battleResult . data ) ;
2011-02-11 21:12:08 +02:00
//Eagle Eye secondary skill handling
2011-02-14 11:29:35 +02:00
if ( cs . spells . size ( ) )
2011-02-11 21:12:08 +02:00
{
2011-02-14 00:46:04 +02:00
cs . learn = 1 ;
cs . hid = victoriousHero - > id ;
InfoWindow iw ;
2011-02-14 11:29:35 +02:00
iw . player = victoriousHero - > tempOwner ;
2011-02-14 00:46:04 +02:00
iw . text . addTxt ( MetaString : : GENERAL_TXT , 221 ) ; //Through eagle-eyed observation, %s is able to learn %s
iw . text . addReplacement ( victoriousHero - > name ) ;
std : : ostringstream names ;
for ( int i = 0 ; i < cs . spells . size ( ) ; i + + )
2011-02-11 21:12:08 +02:00
{
2011-02-14 00:46:04 +02:00
names < < " %s " ;
if ( i < cs . spells . size ( ) - 2 )
names < < " , " ;
else if ( i < cs . spells . size ( ) - 1 )
names < < " %s " ;
}
2011-02-14 11:29:35 +02:00
names < < " . " ;
2011-02-11 21:12:08 +02:00
2011-02-14 00:46:04 +02:00
iw . text . addReplacement ( names . str ( ) ) ;
2011-02-11 21:12:08 +02:00
2011-02-14 00:46:04 +02:00
std : : set < ui32 > : : iterator it = cs . spells . begin ( ) ;
for ( int i = 0 ; i < cs . spells . size ( ) ; i + + , it + + )
{
iw . text . addReplacement ( MetaString : : SPELL_NAME , * it ) ;
if ( i = = cs . spells . size ( ) - 2 ) //we just added pre-last name
iw . text . addReplacement ( MetaString : : GENERAL_TXT , 141 ) ; // " and "
iw . components . push_back ( Component ( Component : : SPELL , * it , 0 , 0 ) ) ;
2011-02-11 21:12:08 +02:00
}
2011-02-14 00:46:04 +02:00
sendAndApply ( & iw ) ;
sendAndApply ( & cs ) ;
2011-02-14 11:29:35 +02:00
}
2011-07-23 09:14:05 +03:00
// Necromancy if applicable.
const CGHeroInstance * winnerHero = battleResult . data - > winner ! = 0 ? hero2 : hero1 ;
const CGHeroInstance * loserHero = battleResult . data - > winner ! = 0 ? hero1 : hero2 ;
2011-09-04 04:44:50 +03:00
const CStackBasicDescriptor raisedStack = winnerHero ? winnerHero - > calculateNecromancy ( * battleResult . data ) : CStackBasicDescriptor ( ) ;
// Give raised units to winner and show dialog, if any were raised,
// units will be given after casualities are taken
const TSlot necroSlot = raisedStack . type ? winnerHero - > getSlotFor ( raisedStack . type ) : - 1 ;
2011-02-11 21:12:08 +02:00
2011-01-08 21:38:42 +02:00
if ( ! duel )
2008-09-20 21:30:37 +03:00
{
2011-01-08 21:38:42 +02:00
cab1 . takeFromArmy ( this ) ; cab2 . takeFromArmy ( this ) ; //take casualties after battle is deleted
//if one hero has lost we will erase him
if ( battleResult . data - > winner ! = 0 & & hero1 )
{
RemoveObject ro ( hero1 - > id ) ;
sendAndApply ( & ro ) ;
}
if ( battleResult . data - > winner ! = 1 & & hero2 )
{
RemoveObject ro ( hero2 - > id ) ;
sendAndApply ( & ro ) ;
}
2008-09-20 21:30:37 +03:00
2011-01-08 21:38:42 +02:00
//give exp
2011-07-30 18:04:34 +03:00
if ( battleResult . data - > exp [ 0 ] & & hero1 )
2011-01-08 21:38:42 +02:00
changePrimSkill ( hero1 - > id , 4 , battleResult . data - > exp [ 0 ] ) ;
2011-07-30 18:04:34 +03:00
else if ( battleResult . data - > exp [ 1 ] & & hero2 )
2011-01-08 21:38:42 +02:00
changePrimSkill ( hero2 - > id , 4 , battleResult . data - > exp [ 1 ] ) ;
2011-07-30 21:02:31 +03:00
else
afterBattleCallback ( ) ;
2011-01-08 21:38:42 +02:00
}
2008-09-29 13:16:02 +03:00
2011-09-04 04:44:50 +03:00
if ( necroSlot ! = - 1 )
{
winnerHero - > showNecromancyDialog ( raisedStack ) ;
addToSlot ( StackLocation ( winnerHero , necroSlot ) , raisedStack . type , raisedStack . count ) ;
}
2010-08-18 16:42:46 +03:00
sendAndApply ( & resultsApplied ) ;
2011-01-08 21:38:42 +02:00
if ( duel )
{
CSaveFile resultFile ( " result.vdrst " ) ;
2011-01-21 04:36:30 +02:00
resultFile < < * battleResult . data ;
2011-01-08 21:38:42 +02:00
return ;
}
2010-05-27 00:59:58 +03:00
if ( visitObjectAfterVictory & & winnerHero = = hero1 )
{
visitObjectOnTile ( * getTile ( winnerHero - > getPosition ( ) ) , winnerHero ) ;
}
visitObjectAfterVictory = false ;
2010-01-29 22:52:45 +02:00
winLoseHandle ( 1 < < sides [ 0 ] | 1 < < sides [ 1 ] ) ; //handle victory/loss of engaged players
2010-07-08 08:52:11 +03:00
int result = battleResult . get ( ) - > result ;
if ( result = = 1 | | result = = 2 ) //loser has escaped or surrendered
{
SetAvailableHeroes sah ;
sah . player = loser ;
sah . hid [ 0 ] = loserHero - > subID ;
if ( result = = 1 ) //retreat
{
2011-01-28 04:11:58 +02:00
sah . army [ 0 ] . clear ( ) ;
sah . army [ 0 ] . setCreature ( 0 , VLC - > creh - > nameToID [ loserHero - > type - > refTypeStack [ 0 ] ] , 1 ) ;
2010-07-08 08:52:11 +03:00
}
2011-05-10 01:20:47 +03:00
if ( const CGHeroInstance * another = getPlayer ( loser ) - > availableHeroes [ 1 ] )
2010-07-08 08:52:11 +03:00
sah . hid [ 1 ] = another - > subID ;
else
sah . hid [ 1 ] = - 1 ;
sendAndApply ( & sah ) ;
}
2011-07-17 15:53:06 +03:00
}
2010-07-08 08:52:11 +03:00
2011-07-17 21:11:03 +03:00
void CGameHandler : : afterBattleCallback ( ) //object interaction after leveling up is done
2011-07-17 15:53:06 +03:00
{
if ( battleEndCallback & & * battleEndCallback )
{
2011-09-01 05:48:29 +03:00
boost : : function < void ( BattleResult * ) > * backup = battleEndCallback ;
battleEndCallback = NULL ; //we need to set it to NULL or else we have infinite recursion when after battle callback gives exp (eg Pandora Box with exp)
( * backup ) ( battleResult . data ) ;
delete backup ;
2011-07-17 15:53:06 +03:00
}
2008-08-04 18:56:36 +03:00
}
2011-07-05 09:07:36 +03:00
void CGameHandler : : prepareAttack ( BattleAttack & bat , const CStack * att , const CStack * def , int distance , int targetHex )
2008-10-18 14:41:24 +03:00
{
2009-03-28 02:38:48 +02:00
bat . bsa . clear ( ) ;
2008-10-18 14:41:24 +03:00
bat . stackAttacking = att - > ID ;
2010-05-02 21:20:26 +03:00
int attackerLuck = att - > LuckVal ( ) ;
2010-05-20 14:06:40 +03:00
const CGHeroInstance * h0 = gs - > curB - > heroes [ 0 ] ,
* h1 = gs - > curB - > heroes [ 1 ] ;
bool noLuck = false ;
2011-07-05 14:31:26 +03:00
if ( ( h0 & & NBonus : : hasOfType ( h0 , Bonus : : BLOCK_LUCK ) ) | |
( h1 & & NBonus : : hasOfType ( h1 , Bonus : : BLOCK_LUCK ) ) )
2010-05-20 14:06:40 +03:00
{
noLuck = true ;
}
if ( ! noLuck & & attackerLuck > 0 & & rand ( ) % 24 < attackerLuck ) //TODO?: negative luck option?
2009-02-05 11:49:45 +02:00
{
2011-02-24 17:33:03 +02:00
bat . flags | = BattleAttack : : LUCKY ;
2009-02-05 11:49:45 +02:00
}
2011-07-06 20:00:45 +03:00
if ( rand ( ) % 100 < att - > valOfBonuses ( Bonus : : DOUBLE_DAMAGE_CHANCE ) )
{
bat . flags | = BattleAttack : : DEATH_BLOW ;
}
2010-04-06 16:19:54 +03:00
2011-02-24 17:33:03 +02:00
if ( att - > getCreature ( ) - > idNumber = = 146 )
{
static const int artilleryLvlToChance [ ] = { 0 , 50 , 75 , 100 } ;
const CGHeroInstance * owner = gs - > curB - > getHero ( att - > owner ) ;
int chance = artilleryLvlToChance [ owner - > getSecSkillLevel ( CGHeroInstance : : ARTILLERY ) ] ;
if ( chance > rand ( ) % 100 )
{
bat . flags | = BattleAttack : : BALLISTA_DOUBLE_DMG ;
}
}
2011-07-04 22:34:49 +03:00
// only primary target
applyBattleEffects ( bat , att , def , distance , false ) ;
2011-02-24 17:33:03 +02:00
2011-07-04 22:34:49 +03:00
if ( ! bat . shot ( ) ) //multiple-hex attack - only in meele
{
2011-07-05 09:07:36 +03:00
std : : set < CStack * > attackedCreatures = gs - > curB - > getAttackedCreatures ( att , targetHex ) ;
2011-07-04 22:34:49 +03:00
//TODO: get exact attacked hex for defender
BOOST_FOREACH ( CStack * stack , attackedCreatures )
{
if ( stack ! = def ) //do not hit same stack twice
{
applyBattleEffects ( bat , att , stack , distance , true ) ;
}
}
}
2010-02-26 17:52:48 +02:00
2011-07-02 19:49:22 +03:00
const Bonus * bonus = att - > getBonus ( Selector : : type ( Bonus : : SPELL_LIKE_ATTACK ) ) ;
2011-07-04 22:34:49 +03:00
if ( bonus & & ( bat . shot ( ) ) ) //TODO: make it work in meele?
2010-05-07 15:29:41 +03:00
{
2011-07-04 22:34:49 +03:00
bat . bsa . front ( ) . flags | = BattleStackAttacked : : EFFECT ;
bat . bsa . front ( ) . effect = VLC - > spellh - > spells [ bonus - > subtype ] - > mainEffectAnim ; //hopefully it does not interfere with any other effect?
2011-07-02 20:40:33 +03:00
2011-07-05 09:07:36 +03:00
std : : set < CStack * > attackedCreatures = gs - > curB - > getAttackedCreatures ( VLC - > spellh - > spells [ bonus - > subtype ] , bonus - > val , att - > owner , targetHex ) ;
2011-07-03 08:55:57 +03:00
//TODO: get exact attacked hex for defender
2010-05-07 15:29:41 +03:00
2011-07-02 19:49:22 +03:00
BOOST_FOREACH ( CStack * stack , attackedCreatures )
2010-05-07 15:29:41 +03:00
{
2011-07-02 19:49:22 +03:00
if ( stack ! = def ) //do not hit same stack twice
{
2011-07-04 22:34:49 +03:00
applyBattleEffects ( bat , att , stack , distance , true ) ;
2011-07-02 19:49:22 +03:00
}
}
2010-02-26 17:52:48 +02:00
}
2011-07-04 22:34:49 +03:00
}
void CGameHandler : : applyBattleEffects ( BattleAttack & bat , const CStack * att , const CStack * def , int distance , bool secondary ) //helper function for prepareAttack
{
BattleStackAttacked bsa ;
if ( secondary )
bsa . flags | = BattleStackAttacked : : SECONDARY ; //all other targets do not suffer from spells & spell-like abilities
bsa . attackerID = att - > ID ;
bsa . stackAttacked = def - > ID ;
2011-07-06 20:00:45 +03:00
bsa . damageAmount = gs - > curB - > calculateDmg ( att , def , gs - > curB - > battleGetOwner ( att ) , gs - > curB - > battleGetOwner ( def ) , bat . shot ( ) , distance , bat . lucky ( ) , bat . deathBlow ( ) , bat . ballistaDoubleDmg ( ) ) ;
2011-07-04 22:34:49 +03:00
def - > prepareAttacked ( bsa ) ; //calculate casualties
//life drain handling
if ( att - > hasBonusOfType ( Bonus : : LIFE_DRAIN ) & & def - > isLiving ( ) )
{
StacksHealedOrResurrected shi ;
shi . lifeDrain = ( ui8 ) true ;
shi . tentHealing = ( ui8 ) false ;
shi . drainedFrom = def - > ID ;
StacksHealedOrResurrected : : HealInfo hi ;
hi . stackID = att - > ID ;
hi . healedHP = std : : min < int > ( bsa . damageAmount , att - > MaxHealth ( ) - att - > firstHPleft + att - > MaxHealth ( ) * ( att - > baseAmount - att - > count ) ) ;
hi . lowLevelResurrection = false ;
shi . healedStacks . push_back ( hi ) ;
if ( hi . healedHP > 0 )
2011-07-02 19:49:22 +03:00
{
2011-07-04 22:34:49 +03:00
bsa . healedStacks . push_back ( shi ) ;
2011-07-02 19:49:22 +03:00
}
2011-09-05 09:37:24 +03:00
}
bat . bsa . push_back ( bsa ) ; //add this stack to the list of victims after drain life has been calculated
2011-07-04 22:34:49 +03:00
//fire shield handling
if ( ! bat . shot ( ) & & def - > hasBonusOfType ( Bonus : : FIRE_SHIELD ) & & ! att - > hasBonusOfType ( Bonus : : FIRE_IMMUNITY ) & & ! bsa . killed ( ) )
{
BattleStackAttacked bsa ;
bsa . stackAttacked = att - > ID ; //invert
bsa . attackerID = def - > ID ;
bsa . flags | = BattleStackAttacked : : EFFECT ;
bsa . effect = 11 ;
bsa . damageAmount = ( bsa . damageAmount * def - > valOfBonuses ( Bonus : : FIRE_SHIELD ) ) / 100 ;
att - > prepareAttacked ( bsa ) ;
bat . bsa . push_back ( bsa ) ;
2011-07-02 19:49:22 +03:00
}
2008-10-18 14:41:24 +03:00
}
2008-07-25 20:28:28 +03:00
void CGameHandler : : handleConnection ( std : : set < int > players , CConnection & c )
{
2011-02-24 15:57:47 +02:00
setThreadName ( - 1 , " CGameHandler::handleConnection " ) ;
2009-04-09 18:05:20 +03:00
srand ( time ( NULL ) ) ;
2009-03-09 21:40:43 +02:00
CPack * pack = NULL ;
2012-01-03 04:55:26 +03:00
ui8 player = 255 ;
2008-07-27 20:07:37 +03:00
try
2008-07-25 20:28:28 +03:00
{
2010-01-29 22:52:45 +02:00
while ( 1 ) //server should never shut connection first //was: while(!end2)
2008-07-25 20:28:28 +03:00
{
2009-04-16 03:28:54 +03:00
{
boost : : unique_lock < boost : : mutex > lock ( * c . rmx ) ;
2012-01-03 04:55:26 +03:00
c > > player > > pack ; //get the package
2009-04-16 03:28:54 +03:00
tlog5 < < " Received client message of type " < < typeid ( * pack ) . name ( ) < < std : : endl ;
}
int packType = typeList . getTypeID ( pack ) ; //get the id of type
CBaseForGHApply * apply = applier - > apps [ packType ] ; //and appropriae applier object
2012-01-03 04:55:26 +03:00
if ( packType ! = typeList . getTypeID < QueryReply > ( )
& & ( packType ! = typeList . getTypeID < ArrangeStacks > ( ) | | ! isAllowedArrangePack ( ( ArrangeStacks * ) pack ) ) // for dialogs like garrison
& & states . getQueriesCount ( player ) )
2011-09-06 09:00:32 +03:00
{
2012-01-03 04:55:26 +03:00
complain ( boost : : str ( boost : : format ( " Player %d has to answer queries before attempting any further actions (count=%d)! " ) % ( int ) player % states . getQueriesCount ( player ) ) ) ;
2011-09-06 09:00:32 +03:00
PackageApplied applied ;
2012-01-03 04:55:26 +03:00
applied . player = player ;
2011-09-06 09:00:32 +03:00
applied . result = false ;
applied . packType = packType ;
{
boost : : unique_lock < boost : : mutex > lock ( * c . wmx ) ;
c < < & applied ;
}
}
else if ( apply )
2008-07-27 20:07:37 +03:00
{
2012-01-03 04:55:26 +03:00
bool result = apply - > applyOnGH ( this , & c , pack , player ) ;
2009-04-16 03:28:54 +03:00
tlog5 < < " Message successfully applied (result= " < < result < < " )! \n " ;
//send confirmation that we've applied the package
2011-07-17 21:49:05 +03:00
PackageApplied applied ;
2012-01-03 04:55:26 +03:00
applied . player = player ;
2011-07-17 21:49:05 +03:00
applied . result = result ;
applied . packType = packType ;
2009-04-16 03:28:54 +03:00
{
2011-07-17 21:49:05 +03:00
boost : : unique_lock < boost : : mutex > lock ( * c . wmx ) ;
c < < & applied ;
2009-04-16 03:28:54 +03:00
}
2008-07-27 20:07:37 +03:00
}
2009-03-09 12:37:49 +02:00
else
{
2009-04-16 03:28:54 +03:00
tlog1 < < " Message cannot be applied, cannot find applier (unregistered type)! \n " ;
2009-03-09 12:37:49 +02:00
}
delete pack ;
pack = NULL ;
2012-01-03 04:55:26 +03:00
player = 255 ;
2008-07-25 20:28:28 +03:00
}
}
2009-09-20 15:47:40 +03:00
catch ( boost : : system : : system_error & e ) //for boost errors just log, not crash - probably client shut down connection
{
2010-01-29 22:52:45 +02:00
assert ( ! c . connected ) ; //make sure that connection has been marked as broken
2009-09-20 15:47:40 +03:00
tlog1 < < e . what ( ) < < std : : endl ;
end2 = true ;
}
2009-01-25 18:19:34 +02:00
HANDLE_EXCEPTION ( end2 = true ) ;
2010-01-29 22:52:45 +02:00
2008-09-23 13:58:54 +03:00
tlog1 < < " Ended handling connection \n " ;
2008-07-25 20:28:28 +03:00
}
2009-08-22 18:29:30 +03:00
2011-12-22 16:05:19 +03:00
int CGameHandler : : moveStack ( int stack , BattleHex dest )
2009-08-22 18:29:30 +03:00
{
int ret = 0 ;
2008-08-09 02:02:32 +03:00
CStack * curStack = gs - > curB - > getStack ( stack ) ,
* stackAtEnd = gs - > curB - > getStackT ( dest ) ;
2009-09-20 15:47:40 +03:00
assert ( curStack ) ;
2011-12-14 00:23:17 +03:00
assert ( dest < GameConstants : : BFIELD_SIZE ) ;
2009-09-20 15:47:40 +03:00
2011-02-12 18:12:48 +02:00
if ( gs - > curB - > tacticDistance )
{
assert ( gs - > curB - > isInTacticRange ( dest ) ) ;
}
2008-08-09 02:02:32 +03:00
//initing necessary tables
2011-12-14 00:23:17 +03:00
bool accessibility [ GameConstants : : BFIELD_SIZE ] ;
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > accessible = gs - > curB - > getAccessibility ( curStack , false ) ;
2011-12-14 00:23:17 +03:00
for ( int b = 0 ; b < GameConstants : : BFIELD_SIZE ; + + b )
2009-02-14 15:49:30 +02:00
{
accessibility [ b ] = false ;
}
for ( int g = 0 ; g < accessible . size ( ) ; + + g )
{
accessibility [ accessible [ g ] ] = true ;
}
2008-08-09 02:02:32 +03:00
2009-02-05 16:44:27 +02:00
//shifting destination (if we have double wide stack and we can occupy dest but not be exactly there)
2010-05-02 21:20:26 +03:00
if ( ! stackAtEnd & & curStack - > doubleWide ( ) & & ! accessibility [ dest ] )
2009-02-05 16:44:27 +02:00
{
if ( curStack - > attackerOwned )
{
if ( accessibility [ dest + 1 ] )
2011-12-22 16:05:19 +03:00
dest + = BattleHex : : RIGHT ;
2009-02-05 16:44:27 +02:00
}
else
{
if ( accessibility [ dest - 1 ] )
2011-12-22 16:05:19 +03:00
dest + = BattleHex : : LEFT ;
2009-02-05 16:44:27 +02:00
}
}
2008-09-12 11:51:46 +03:00
if ( ( stackAtEnd & & stackAtEnd ! = curStack & & stackAtEnd - > alive ( ) ) | | ! accessibility [ dest ] )
2009-08-22 18:29:30 +03:00
return 0 ;
2008-08-09 02:02:32 +03:00
2011-12-14 00:23:17 +03:00
bool accessibilityWithOccupyable [ GameConstants : : BFIELD_SIZE ] ;
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > accOc = gs - > curB - > getAccessibility ( curStack , true ) ;
2011-12-14 00:23:17 +03:00
for ( int b = 0 ; b < GameConstants : : BFIELD_SIZE ; + + b )
2009-08-03 17:29:29 +03:00
{
accessibilityWithOccupyable [ b ] = false ;
}
for ( int g = 0 ; g < accOc . size ( ) ; + + g )
{
accessibilityWithOccupyable [ accOc [ g ] ] = true ;
}
2008-08-09 02:02:32 +03:00
//if(dists[dest] > curStack->creature->speed && !(stackAtEnd && dists[dest] == curStack->creature->speed+1)) //we can attack a stack if we can go to adjacent hex
// return false;
2011-12-22 16:05:19 +03:00
std : : pair < std : : vector < BattleHex > , int > path = gs - > curB - > getPath ( curStack - > position , dest , accessibilityWithOccupyable , curStack - > hasBonusOfType ( Bonus : : FLYING ) , curStack - > doubleWide ( ) , curStack - > attackerOwned ) ;
2009-08-22 18:29:30 +03:00
ret = path . second ;
2011-12-14 00:23:17 +03:00
int creSpeed = gs - > curB - > tacticDistance ? GameConstants : : BFIELD_SIZE : curStack - > Speed ( ) ;
2011-02-12 18:12:48 +02:00
2010-05-02 21:20:26 +03:00
if ( curStack - > hasBonusOfType ( Bonus : : FLYING ) )
2008-08-09 02:02:32 +03:00
{
2011-02-12 18:12:48 +02:00
if ( path . second < = creSpeed & & path . first . size ( ) > 0 )
2009-03-07 18:05:53 +02:00
{
//inform clients about move
BattleStackMoved sm ;
sm . stack = curStack - > ID ;
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > tiles ;
2011-08-01 20:36:18 +03:00
tiles . push_back ( path . first [ 0 ] ) ;
sm . tilesToMove = tiles ;
2009-03-07 18:05:53 +02:00
sm . distance = path . second ;
2010-05-07 17:05:48 +03:00
sm . teleporting = false ;
2009-03-07 18:05:53 +02:00
sendAndApply ( & sm ) ;
}
}
else //for non-flying creatures
{
2011-08-01 20:36:18 +03:00
// send one package with the creature path information
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > tiles ;
2011-02-12 18:12:48 +02:00
int tilesToMove = std : : max ( ( int ) ( path . first . size ( ) - creSpeed ) , 0 ) ;
2009-03-07 18:05:53 +02:00
for ( int v = path . first . size ( ) - 1 ; v > = tilesToMove ; - - v )
{
2011-08-01 20:36:18 +03:00
tiles . push_back ( path . first [ v ] ) ;
}
if ( tiles . size ( ) > 0 )
{
2009-03-07 18:05:53 +02:00
BattleStackMoved sm ;
sm . stack = curStack - > ID ;
sm . distance = path . second ;
2010-05-07 17:05:48 +03:00
sm . teleporting = false ;
2011-08-01 20:36:18 +03:00
sm . tilesToMove = tiles ;
2009-03-07 18:05:53 +02:00
sendAndApply ( & sm ) ;
}
2008-08-09 02:02:32 +03:00
}
2009-08-22 18:29:30 +03:00
return ret ;
2008-08-09 02:02:32 +03:00
}
2010-11-27 22:17:28 +02:00
2008-07-01 11:01:02 +03:00
CGameHandler : : CGameHandler ( void )
{
2009-04-12 03:58:41 +03:00
QID = 1 ;
2011-05-03 06:14:18 +03:00
//gs = NULL;
2009-01-12 22:05:56 +02:00
IObjectInterface : : cb = this ;
2010-09-03 21:42:54 +03:00
applier = new CApplier < CBaseForGHApply > ;
registerTypes3 ( * applier ) ;
2010-05-27 00:59:58 +03:00
visitObjectAfterVictory = false ;
2011-01-08 21:38:42 +02:00
battleEndCallback = NULL ;
2008-07-01 11:01:02 +03:00
}
CGameHandler : : ~ CGameHandler ( void )
{
2009-03-09 12:37:49 +02:00
delete applier ;
applier = NULL ;
2008-07-25 20:28:28 +03:00
delete gs ;
}
2010-01-29 18:19:12 +02:00
2008-07-25 20:28:28 +03:00
void CGameHandler : : init ( StartInfo * si , int Seed )
{
gs = new CGameState ( ) ;
2008-09-19 15:09:15 +03:00
tlog0 < < " Gamestate created! " < < std : : endl ;
2010-05-08 21:56:38 +03:00
gs - > init ( si , 0 , Seed ) ;
2008-09-19 15:09:15 +03:00
tlog0 < < " Gamestate initialized! " < < std : : endl ;
2008-12-22 19:48:41 +02:00
2009-01-11 00:08:18 +02:00
for ( std : : map < ui8 , PlayerState > : : iterator i = gs - > players . begin ( ) ; i ! = gs - > players . end ( ) ; i + + )
states . addPlayer ( i - > first ) ;
2008-07-01 11:01:02 +03:00
}
2008-09-28 16:29:37 +03:00
2009-05-24 01:57:39 +03:00
static bool evntCmp ( const CMapEvent * a , const CMapEvent * b )
2009-03-09 21:40:43 +02:00
{
return * a < * b ;
}
2010-08-24 17:26:57 +03:00
void CGameHandler : : setPortalDwelling ( const CGTownInstance * town , bool forced = false , bool clear = false )
2010-07-10 19:50:23 +03:00
{ // bool forced = true - if creature should be replaced, if false - only if no creature was set
2011-05-30 02:49:25 +03:00
const PlayerState * p = gs - > getPlayer ( town - > tempOwner ) ;
if ( ! p )
{
tlog3 < < " There is no player owner of town " < < town - > name < < " at " < < town - > pos < < std : : endl ;
return ;
}
2010-07-10 19:50:23 +03:00
2011-12-14 00:23:17 +03:00
if ( forced | | town - > creatures [ GameConstants : : CREATURES_PER_TOWN ] . second . empty ( ) ) //we need to change creature
2010-07-10 19:50:23 +03:00
{
SetAvailableCreatures ssi ;
ssi . tid = town - > id ;
ssi . creatures = town - > creatures ;
2011-12-14 00:23:17 +03:00
ssi . creatures [ GameConstants : : CREATURES_PER_TOWN ] . second . clear ( ) ; //remove old one
2010-07-10 19:50:23 +03:00
2011-05-30 02:49:25 +03:00
const std : : vector < ConstTransitivePtr < CGDwelling > > & dwellings = p - > dwellings ;
2010-07-10 19:50:23 +03:00
if ( dwellings . empty ( ) ) //no dwellings - just remove
{
sendAndApply ( & ssi ) ;
return ;
}
ui32 dwellpos = rand ( ) % dwellings . size ( ) ; //take random dwelling
ui32 creapos = rand ( ) % dwellings [ dwellpos ] - > creatures . size ( ) ; //for multi-creature dwellings like Golem Factory
ui32 creature = dwellings [ dwellpos ] - > creatures [ creapos ] . second [ 0 ] ;
2010-08-24 17:26:57 +03:00
if ( clear )
2011-12-14 00:23:17 +03:00
ssi . creatures [ GameConstants : : CREATURES_PER_TOWN ] . first = std : : max ( ( ui32 ) 1 , ( VLC - > creh - > creatures [ creature ] - > growth ) / 2 ) ;
2010-08-24 17:26:57 +03:00
else
2011-12-14 00:23:17 +03:00
ssi . creatures [ GameConstants : : CREATURES_PER_TOWN ] . first = VLC - > creh - > creatures [ creature ] - > growth ;
ssi . creatures [ GameConstants : : CREATURES_PER_TOWN ] . second . push_back ( creature ) ;
2010-07-10 19:50:23 +03:00
sendAndApply ( & ssi ) ;
}
}
2008-07-25 20:28:28 +03:00
void CGameHandler : : newTurn ( )
{
2008-10-26 22:58:34 +02:00
tlog5 < < " Turn " < < gs - > day + 1 < < std : : endl ;
2008-07-26 16:57:32 +03:00
NewTurn n ;
2011-08-26 23:32:05 +03:00
n . specialWeek = NewTurn : : NO_ACTION ;
2010-08-26 10:23:08 +03:00
n . creatureid = - 1 ;
2008-07-26 16:57:32 +03:00
n . day = gs - > day + 1 ;
2011-08-26 23:32:05 +03:00
bool firstTurn = ! getDate ( 0 ) ;
bool newWeek = getDate ( 1 ) = = 7 ; //day numbers are confusing, as day was not yet switched
bool newMonth = getDate ( 4 ) = = 28 ;
2010-01-25 23:25:14 +02:00
std : : map < ui8 , si32 > hadGold ; //starting gold - for buildings like dwarven treasury
srand ( time ( NULL ) ) ;
2008-07-26 16:57:32 +03:00
2011-08-26 23:32:05 +03:00
if ( newWeek & & ! firstTurn )
2010-08-24 17:26:57 +03:00
{
2011-08-26 23:32:05 +03:00
n . specialWeek = NewTurn : : NORMAL ;
bool deityOfFireBuilt = false ;
BOOST_FOREACH ( const CGTownInstance * t , gs - > map - > towns )
2010-08-24 17:26:57 +03:00
{
2011-12-14 00:23:17 +03:00
if ( t - > subID = = 3 & & vstd : : contains ( t - > builtBuildings , EBuilding : : GRAIL ) )
2010-08-24 17:26:57 +03:00
{
2011-08-26 23:32:05 +03:00
deityOfFireBuilt = true ;
break ;
2010-08-24 17:26:57 +03:00
}
}
2011-08-26 23:32:05 +03:00
if ( deityOfFireBuilt )
2010-08-24 17:26:57 +03:00
{
2011-08-26 23:32:05 +03:00
n . specialWeek = NewTurn : : DEITYOFFIRE ;
n . creatureid = 42 ;
}
else
{
int monthType = rand ( ) % 100 ;
if ( newMonth ) //new month
2010-08-24 17:26:57 +03:00
{
2011-08-26 23:32:05 +03:00
if ( monthType < 40 ) //double growth
{
n . specialWeek = NewTurn : : DOUBLE_GROWTH ;
if ( ALLCREATURESGETDOUBLEMONTHS )
{
std : : pair < int , int > newMonster ( 54 , VLC - > creh - > pickRandomMonster ( boost : : ref ( rand ) ) ) ;
n . creatureid = newMonster . second ;
}
else
{
std : : set < TCreature > : : const_iterator it = VLC - > creh - > doubledCreatures . begin ( ) ;
std : : advance ( it , rand ( ) % VLC - > creh - > doubledCreatures . size ( ) ) ; //picking random element of set is tiring
n . creatureid = * it ;
}
}
else if ( monthType < 50 )
n . specialWeek = NewTurn : : PLAGUE ;
}
else //it's a week, but not full month
{
if ( monthType < 25 )
{
n . specialWeek = NewTurn : : BONUS_GROWTH ; //+5
std : : pair < int , int > newMonster ( 54 , VLC - > creh - > pickRandomMonster ( boost : : ref ( rand ) ) ) ;
//TODO do not pick neutrals
n . creatureid = newMonster . second ;
}
2010-08-24 17:26:57 +03:00
}
}
}
2010-12-20 15:04:24 +02:00
bmap < ui32 , ConstTransitivePtr < CGHeroInstance > > pool = gs - > hpool . heroesPool ;
2009-08-05 03:05:37 +03:00
2008-07-25 20:28:28 +03:00
for ( std : : map < ui8 , PlayerState > : : iterator i = gs - > players . begin ( ) ; i ! = gs - > players . end ( ) ; i + + )
{
2010-07-08 08:52:11 +03:00
if ( i - > first = = 255 )
continue ;
2011-12-14 00:23:17 +03:00
else if ( i - > first > = GameConstants : : PLAYER_LIMIT )
2010-07-08 08:52:11 +03:00
assert ( 0 ) ; //illegal player number!
2009-11-13 21:04:36 +02:00
2011-06-24 20:43:02 +03:00
std : : pair < ui8 , si32 > playerGold ( i - > first , i - > second . resources [ Res : : GOLD ] ) ;
2010-01-25 23:25:14 +02:00
hadGold . insert ( playerGold ) ;
2011-08-26 23:32:05 +03:00
if ( newWeek ) //new heroes in tavern
2008-10-26 22:58:34 +02:00
{
SetAvailableHeroes sah ;
sah . player = i - > first ;
2010-07-08 08:52:11 +03:00
//pick heroes and their armies
CHeroClass * banned = NULL ;
2011-12-14 00:23:17 +03:00
for ( int j = 0 ; j < GameConstants : : AVAILABLE_HEROES_PER_PLAYER ; j + + )
2010-07-08 08:52:11 +03:00
{
2010-07-09 02:03:27 +03:00
if ( CGHeroInstance * h = gs - > hpool . pickHeroFor ( j = = 0 , i - > first , getNativeTown ( i - > first ) , pool , banned ) ) //first hero - native if possible, second hero -> any other class
2010-07-08 08:52:11 +03:00
{
sah . hid [ j ] = h - > subID ;
2011-01-28 04:11:58 +02:00
h - > initArmy ( & sah . army [ j ] ) ;
2010-07-08 08:52:11 +03:00
banned = h - > type - > heroClass ;
}
else
sah . hid [ j ] = - 1 ;
}
2008-10-26 22:58:34 +02:00
sendAndApply ( & sah ) ;
}
2009-11-28 03:42:08 +02:00
n . res [ i - > first ] = i - > second . resources ;
2011-07-05 09:14:07 +03:00
2008-10-18 14:41:24 +03:00
BOOST_FOREACH ( CGHeroInstance * h , ( * i ) . second . heroes )
2008-07-25 20:28:28 +03:00
{
2009-07-31 23:10:22 +03:00
if ( h - > visitedTown )
giveSpells ( h - > visitedTown , h ) ;
2008-10-18 14:41:24 +03:00
NewTurn : : Hero hth ;
hth . id = h - > id ;
2009-07-26 06:33:13 +03:00
hth . move = h - > maxMovePoints ( gs - > map - > getTile ( h - > getPosition ( false ) ) . tertype ! = TerrainTile : : water ) ;
2009-04-04 01:34:31 +03:00
if ( h - > visitedTown & & vstd : : contains ( h - > visitedTown - > builtBuildings , 0 ) ) //if hero starts turn in town with mage guild
2009-12-24 08:48:45 +02:00
hth . mana = std : : max ( h - > mana , h - > manaLimit ( ) ) ; //restore all mana
2009-04-04 01:34:31 +03:00
else
2012-02-20 19:26:14 +03:00
hth . mana = std : : max ( ( si32 ) ( 0 ) , std : : max ( h - > mana , std : : min ( ( si32 ) ( h - > mana + h - > manaRegain ( ) ) , h - > manaLimit ( ) ) ) ) ;
2009-04-04 01:34:31 +03:00
2008-10-18 14:41:24 +03:00
n . heroes . insert ( hth ) ;
2011-08-26 23:32:05 +03:00
if ( ! firstTurn ) //not first day
2008-09-28 16:29:37 +03:00
{
2011-06-24 21:16:28 +03:00
n . res [ i - > first ] [ Res : : GOLD ] + = h - > valOfBonuses ( Selector : : typeSubtype ( Bonus : : SECONDARY_SKILL_PREMY , CGHeroInstance : : ESTATES ) ) ; //estates
2010-08-24 17:26:57 +03:00
2011-12-14 00:23:17 +03:00
for ( int k = 0 ; k < GameConstants : : RESOURCE_QUANTITY ; k + + )
2010-07-06 13:02:38 +03:00
{
n . res [ i - > first ] [ k ] + = h - > valOfBonuses ( Bonus : : GENERATE_RESOURCE , k ) ;
}
2009-06-19 04:01:43 +03:00
}
2008-07-25 20:28:28 +03:00
}
2009-11-07 22:23:39 +02:00
}
2010-08-25 17:57:58 +03:00
// townID, creatureID, amount
std : : map < si32 , std : : map < si32 , si32 > > newCreas ; //creatures that needs to be added by town events
2011-08-26 23:32:05 +03:00
BOOST_FOREACH ( CGTownInstance * t , gs - > map - > towns )
2009-11-13 21:04:36 +02:00
{
2011-08-26 23:32:05 +03:00
ui8 player = t - > tempOwner ;
handleTownEvents ( t , n , newCreas ) ;
if ( newWeek ) //first day of week
2008-07-25 20:28:28 +03:00
{
2011-12-14 00:23:17 +03:00
if ( t - > subID = = 5 & & vstd : : contains ( t - > builtBuildings , EBuilding : : SPECIAL_3 ) )
2011-08-26 23:32:05 +03:00
setPortalDwelling ( t , true , ( n . specialWeek = = NewTurn : : PLAGUE ? true : false ) ) ; //set creatures for Portal of Summoning
if ( ! firstTurn )
2011-12-14 00:23:17 +03:00
if ( t - > subID = = 1 & & player < GameConstants : : PLAYER_LIMIT & & vstd : : contains ( t - > builtBuildings , EBuilding : : SPECIAL_3 ) ) //dwarven treasury
2011-08-26 23:32:05 +03:00
n . res [ player ] [ Res : : GOLD ] + = hadGold [ player ] / 10 ; //give 10% of starting gold
SetAvailableCreatures sac ;
sac . tid = t - > id ;
sac . creatures = t - > creatures ;
2011-12-14 00:23:17 +03:00
for ( int k = 0 ; k < GameConstants : : CREATURES_PER_TOWN ; k + + ) //creature growths
2011-08-26 23:32:05 +03:00
{
if ( t - > creatureDwelling ( k ) ) //there is dwelling (k-level)
{
ui32 & availableCount = sac . creatures [ k ] . first ;
2012-01-13 17:18:32 +03:00
const CCreature * cre = VLC - > creh - > creatures [ t - > creatures [ k ] . second . back ( ) ] ;
2011-08-26 23:32:05 +03:00
if ( n . specialWeek = = NewTurn : : PLAGUE )
availableCount = t - > creatures [ k ] . first / 2 ; //halve their number, no growth
else
{
if ( firstTurn ) //first day of game: use only basic growths
availableCount = cre - > growth ;
else
availableCount + = t - > creatureGrowth ( k ) ;
2012-01-19 17:33:22 +03:00
//Deity of fire week - upgrade both imps and upgrades
if ( n . specialWeek = = NewTurn : : DEITYOFFIRE & & vstd : : contains ( t - > creatures [ k ] . second , n . creatureid ) )
availableCount + = 15 ;
if ( cre - > idNumber = = n . creatureid ) //bonus week, effect applies only to identical creatures
2011-08-26 23:32:05 +03:00
{
if ( n . specialWeek = = NewTurn : : DOUBLE_GROWTH )
availableCount * = 2 ;
else if ( n . specialWeek = = NewTurn : : BONUS_GROWTH )
availableCount + = 5 ;
}
}
}
}
//add creatures from town events
if ( vstd : : contains ( newCreas , t - > id ) )
for ( std : : map < si32 , si32 > : : iterator i = newCreas [ t - > id ] . begin ( ) ; i ! = newCreas [ t - > id ] . end ( ) ; i + + )
sac . creatures [ i - > first ] . first + = i - > second ;
2010-08-24 17:26:57 +03:00
2011-08-26 23:32:05 +03:00
n . cres . push_back ( sac ) ;
2009-11-13 21:04:36 +02:00
}
2011-12-14 00:23:17 +03:00
if ( ! firstTurn & & player < GameConstants : : PLAYER_LIMIT ) //not the first day and town not neutral
2009-11-13 21:04:36 +02:00
{
2011-12-14 00:23:17 +03:00
if ( vstd : : contains ( t - > builtBuildings , EBuilding : : RESOURCE_SILO ) ) //there is resource silo
2009-11-07 22:23:39 +02:00
{
2011-08-26 23:32:05 +03:00
if ( t - > town - > primaryRes = = 127 ) //we'll give wood and ore
2009-11-07 22:23:39 +02:00
{
2011-06-24 21:16:28 +03:00
n . res [ player ] [ Res : : WOOD ] + + ;
n . res [ player ] [ Res : : ORE ] + + ;
2009-11-07 22:23:39 +02:00
}
2009-11-13 21:04:36 +02:00
else
{
2011-08-26 23:32:05 +03:00
n . res [ player ] [ t - > town - > primaryRes ] + + ;
2009-11-13 21:04:36 +02:00
}
}
2011-08-26 23:32:05 +03:00
n . res [ player ] [ Res : : GOLD ] + = t - > dailyIncome ( ) ;
2010-06-07 08:28:12 +03:00
}
2011-12-14 00:23:17 +03:00
if ( vstd : : contains ( t - > builtBuildings , EBuilding : : GRAIL ) & & t - > subID = = 2 )
2010-08-24 17:26:57 +03:00
{
2011-08-26 23:32:05 +03:00
// Skyship, probably easier to handle same as Veil of darkness
//do it every new day after veils apply
FoWChange fw ;
fw . mode = 1 ;
fw . player = player ;
getAllTiles ( fw . tiles , player , - 1 , 0 ) ;
sendAndApply ( & fw ) ;
2010-06-07 08:28:12 +03:00
}
2011-08-26 23:32:05 +03:00
if ( t - > hasBonusOfType ( Bonus : : DARKNESS ) )
2010-06-07 08:28:12 +03:00
{
2011-08-26 23:32:05 +03:00
t - > hideTiles ( t - > getOwner ( ) , t - > getBonus ( Selector : : type ( Bonus : : DARKNESS ) ) - > val ) ;
2010-06-07 08:28:12 +03:00
}
2010-06-19 12:13:10 +03:00
//unhiding what shouldn't be hidden? //that's handled in netpacks client
2009-11-13 21:04:36 +02:00
}
2009-11-07 22:23:39 +02:00
2011-08-26 23:32:05 +03:00
if ( newMonth )
2010-06-27 19:03:01 +03:00
{
2010-08-22 10:54:24 +03:00
SetAvailableArtifacts saa ;
2010-06-27 19:03:01 +03:00
saa . id = - 1 ;
pickAllowedArtsSet ( saa . arts ) ;
sendAndApply ( & saa ) ;
}
2010-08-24 17:26:57 +03:00
sendAndApply ( & n ) ;
2011-08-26 23:32:05 +03:00
if ( newWeek )
{
//spawn wandering monsters
if ( newMonth & & ( n . specialWeek = = NewTurn : : DOUBLE_GROWTH | | n . specialWeek = = NewTurn : : DEITYOFFIRE ) )
2010-08-22 21:21:45 +03:00
{
2011-08-26 23:32:05 +03:00
spawnWanderingMonsters ( n . creatureid ) ;
2010-08-22 21:21:45 +03:00
}
2011-08-26 23:32:05 +03:00
//new week info popup
if ( ! firstTurn )
2010-08-22 21:21:45 +03:00
{
2011-08-26 23:32:05 +03:00
InfoWindow iw ;
2010-08-24 17:26:57 +03:00
switch ( n . specialWeek )
2010-08-22 21:21:45 +03:00
{
2010-08-24 17:26:57 +03:00
case NewTurn : : DOUBLE_GROWTH :
iw . text . addTxt ( MetaString : : ARRAY_TXT , 131 ) ;
iw . text . addReplacement ( MetaString : : CRE_SING_NAMES , n . creatureid ) ;
iw . text . addReplacement ( MetaString : : CRE_SING_NAMES , n . creatureid ) ;
break ;
case NewTurn : : PLAGUE :
iw . text . addTxt ( MetaString : : ARRAY_TXT , 132 ) ;
break ;
case NewTurn : : BONUS_GROWTH :
iw . text . addTxt ( MetaString : : ARRAY_TXT , 134 ) ;
iw . text . addReplacement ( MetaString : : CRE_SING_NAMES , n . creatureid ) ;
iw . text . addReplacement ( MetaString : : CRE_SING_NAMES , n . creatureid ) ;
break ;
2010-08-26 10:23:08 +03:00
case NewTurn : : DEITYOFFIRE :
iw . text . addTxt ( MetaString : : ARRAY_TXT , 135 ) ;
iw . text . addReplacement ( MetaString : : CRE_SING_NAMES , 42 ) ; //%s imp
iw . text . addReplacement ( MetaString : : CRE_SING_NAMES , 42 ) ; //%s imp
iw . text . addReplacement2 ( 15 ) ; //%+d 15
iw . text . addReplacement ( MetaString : : CRE_SING_NAMES , 43 ) ; //%s familiar
iw . text . addReplacement2 ( 15 ) ; //%+d 15
break ;
2010-08-24 17:26:57 +03:00
default :
2012-01-19 17:33:22 +03:00
if ( newMonth )
{
iw . text . addTxt ( MetaString : : ARRAY_TXT , ( 130 ) ) ;
iw . text . addReplacement ( MetaString : : ARRAY_TXT , 32 + rand ( ) % 10 ) ;
}
else
{
iw . text . addTxt ( MetaString : : ARRAY_TXT , ( 133 ) ) ;
iw . text . addReplacement ( MetaString : : ARRAY_TXT , 43 + rand ( ) % 15 ) ;
}
2010-08-22 21:21:45 +03:00
}
2010-08-24 17:26:57 +03:00
for ( std : : map < ui8 , PlayerState > : : iterator i = gs - > players . begin ( ) ; i ! = gs - > players . end ( ) ; i + + )
{
iw . player = i - > first ;
sendAndApply ( & iw ) ;
}
2010-08-22 21:21:45 +03:00
}
}
2011-08-26 23:32:05 +03:00
2008-10-26 22:58:34 +02:00
tlog5 < < " Info about turn " < < n . day < < " has been sent! " < < std : : endl ;
2009-03-09 21:40:43 +02:00
handleTimeEvents ( ) ;
//call objects
2008-12-22 19:48:41 +02:00
for ( size_t i = 0 ; i < gs - > map - > objects . size ( ) ; i + + )
2010-08-22 10:54:24 +03:00
{
2008-12-27 03:01:59 +02:00
if ( gs - > map - > objects [ i ] )
gs - > map - > objects [ i ] - > newTurn ( ) ;
2010-08-22 10:54:24 +03:00
}
2010-01-29 22:52:45 +02:00
winLoseHandle ( 0xff ) ;
2010-02-02 01:30:03 +02:00
//warn players without town
if ( gs - > day )
{
for ( std : : map < ui8 , PlayerState > : : iterator i = gs - > players . begin ( ) ; i ! = gs - > players . end ( ) ; i + + )
{
2011-12-14 00:23:17 +03:00
if ( i - > second . status | | i - > second . towns . size ( ) | | i - > second . color > = GameConstants : : PLAYER_LIMIT )
2010-02-02 01:30:03 +02:00
continue ;
InfoWindow iw ;
iw . player = i - > first ;
iw . components . push_back ( Component ( Component : : FLAG , i - > first , 0 , 0 ) ) ;
if ( ! i - > second . daysWithoutCastle )
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 6 ) ; //%s, you have lost your last town. If you do not conquer another town in the next week, you will be eliminated.
iw . text . addReplacement ( MetaString : : COLOR , i - > first ) ;
}
else if ( i - > second . daysWithoutCastle = = 6 )
{
iw . text . addTxt ( MetaString : : ARRAY_TXT , 129 ) ; //%s, this is your last day to capture a town or you will be banished from this land.
iw . text . addReplacement ( MetaString : : COLOR , i - > first ) ;
}
else
{
iw . text . addTxt ( MetaString : : ARRAY_TXT , 128 ) ; //%s, you only have %d days left to capture a town or you will be banished from this land.
iw . text . addReplacement ( MetaString : : COLOR , i - > first ) ;
iw . text . addReplacement ( 7 - i - > second . daysWithoutCastle ) ;
}
sendAndApply ( & iw ) ;
}
}
2008-07-25 20:28:28 +03:00
}
2010-10-24 14:35:14 +03:00
void CGameHandler : : run ( bool resume )
2010-05-02 21:20:26 +03:00
{
using namespace boost : : posix_time ;
2008-07-25 20:28:28 +03:00
BOOST_FOREACH ( CConnection * cc , conns )
{ //init conn.
2010-10-24 14:35:14 +03:00
ui32 quantity ;
ui8 pom ;
2008-07-25 20:28:28 +03:00
//ui32 seed;
2009-01-11 00:08:18 +02:00
if ( ! resume )
2010-12-22 22:14:40 +02:00
{
ui32 sum = gs - > map ? gs - > map - > checksum : 612 ;
( * cc ) < < gs - > initialOpts < < sum < < gs - > seed ; // gs->scenarioOps
}
2009-01-11 00:08:18 +02:00
2008-10-18 14:41:24 +03:00
( * cc ) > > quantity ; //how many players will be handled at that client
2010-10-24 14:35:14 +03:00
tlog0 < < " Connection " < < cc - > connectionID < < " will handle " < < quantity < < " player: " ;
2008-07-25 20:28:28 +03:00
for ( int i = 0 ; i < quantity ; i + + )
{
2008-10-18 14:41:24 +03:00
( * cc ) > > pom ; //read player color
2010-10-24 14:35:14 +03:00
tlog0 < < ( int ) pom < < " " ;
2009-04-12 04:48:50 +03:00
{
boost : : unique_lock < boost : : recursive_mutex > lock ( gsm ) ;
connections [ pom ] = cc ;
}
2008-07-25 20:28:28 +03:00
}
2010-10-24 14:35:14 +03:00
tlog0 < < std : : endl ;
2008-07-25 20:28:28 +03:00
}
2008-08-15 15:11:42 +03:00
2008-07-25 20:28:28 +03:00
for ( std : : set < CConnection * > : : iterator i = conns . begin ( ) ; i ! = conns . end ( ) ; i + + )
{
std : : set < int > pom ;
for ( std : : map < int , CConnection * > : : iterator j = connections . begin ( ) ; j ! = connections . end ( ) ; j + + )
if ( j - > second = = * i )
pom . insert ( j - > first ) ;
boost : : thread ( boost : : bind ( & CGameHandler : : handleConnection , this , pom , boost : : ref ( * * i ) ) ) ;
}
2008-07-31 00:27:15 +03:00
2011-09-28 01:03:43 +03:00
if ( gs - > scenarioOps - > mode = = StartInfo : : DUEL )
{
runBattle ( ) ;
return ;
}
2008-08-04 12:05:52 +03:00
while ( ! end2 )
2008-07-25 20:28:28 +03:00
{
2009-01-25 18:19:34 +02:00
if ( ! resume )
newTurn ( ) ;
2009-01-11 00:08:18 +02:00
std : : map < ui8 , PlayerState > : : iterator i ;
if ( ! resume )
i = gs - > players . begin ( ) ;
else
i = gs - > players . find ( gs - > currentPlayer ) ;
2010-04-02 05:07:40 +03:00
resume = false ;
2009-01-11 00:08:18 +02:00
for ( ; i ! = gs - > players . end ( ) ; i + + )
2008-07-25 20:28:28 +03:00
{
2011-05-30 22:20:14 +03:00
if ( ( i - > second . towns . size ( ) = = 0 & & i - > second . heroes . size ( ) = = 0 )
2010-01-29 22:52:45 +02:00
| | i - > second . color < 0
2011-12-14 00:23:17 +03:00
| | i - > first > = GameConstants : : PLAYER_LIMIT
2010-01-29 22:52:45 +02:00
| | i - > second . status )
{
continue ;
}
2008-08-13 03:44:31 +03:00
states . setFlag ( i - > first , & PlayerStatus : : makingTurn , true ) ;
2010-04-02 05:07:40 +03:00
2009-03-07 17:54:12 +02:00
{
YourTurn yt ;
yt . player = i - > first ;
2011-09-06 12:59:06 +03:00
applyAndSend ( & yt ) ;
2009-03-07 17:54:12 +02:00
}
2008-08-13 03:44:31 +03:00
2008-07-25 20:28:28 +03:00
//wait till turn is done
2008-08-13 03:44:31 +03:00
boost : : unique_lock < boost : : mutex > lock ( states . mx ) ;
while ( states . players [ i - > first ] . makingTurn & & ! end2 )
2008-07-25 20:28:28 +03:00
{
2010-12-22 22:14:40 +02:00
static time_duration p = milliseconds ( 200 ) ;
states . cv . timed_wait ( lock , p ) ;
2012-02-16 20:10:58 +03:00
2010-02-04 17:50:59 +02:00
}
2010-02-06 15:27:58 +02:00
}
2010-02-04 17:50:59 +02:00
}
2010-12-22 22:14:40 +02:00
while ( conns . size ( ) & & ( * conns . begin ( ) ) - > isOpen ( ) )
boost : : this_thread : : sleep ( boost : : posix_time : : milliseconds ( 5 ) ) ; //give time client to close socket
}
2009-07-21 16:53:26 +03:00
2010-12-22 22:14:40 +02:00
void CGameHandler : : setupBattle ( int3 tile , const CArmedInstance * armies [ 2 ] , const CGHeroInstance * heroes [ 2 ] , bool creatureBank , const CGTownInstance * town )
{
battleResult . set ( NULL ) ;
2008-08-15 15:11:42 +03:00
//send info about battles
BattleStart bs ;
2010-12-22 22:14:40 +02:00
bs . info = gs - > setupBattle ( tile , armies , heroes , creatureBank , town ) ;
2008-08-15 15:11:42 +03:00
sendAndApply ( & bs ) ;
2008-08-17 17:09:30 +03:00
}
2008-09-09 10:05:02 +03:00
void CGameHandler : : checkForBattleEnd ( std : : vector < CStack * > & stacks )
{
//checking winning condition
2011-09-24 04:15:36 +03:00
bool hasStack [ 2 ] ; //hasStack[0] - true if attacker has a living stack; defender similarly
2008-09-09 10:05:02 +03:00
hasStack [ 0 ] = hasStack [ 1 ] = false ;
for ( int b = 0 ; b < stacks . size ( ) ; + + b )
{
2010-05-02 21:20:26 +03:00
if ( stacks [ b ] - > alive ( ) & & ! stacks [ b ] - > hasBonusOfType ( Bonus : : SIEGE_WEAPON ) )
2008-09-09 10:05:02 +03:00
{
hasStack [ 1 - stacks [ b ] - > attackerOwned ] = true ;
}
}
if ( ! hasStack [ 0 ] | | ! hasStack [ 1 ] ) //somebody has won
{
2011-03-05 18:38:22 +02:00
setBattleResult ( 0 , hasStack [ 1 ] ) ;
2008-09-09 10:05:02 +03:00
}
2008-09-12 11:51:46 +03:00
}
void CGameHandler : : giveSpells ( const CGTownInstance * t , const CGHeroInstance * h )
{
2011-01-18 20:56:14 +02:00
if ( ! h - > hasSpellbook ( ) )
2008-09-12 11:51:46 +03:00
return ; //hero hasn't spellbok
ChangeSpells cs ;
cs . hid = h - > id ;
cs . learn = true ;
2010-12-23 22:18:10 +02:00
for ( int i = 0 ; i < std : : min ( t - > mageGuildLevel ( ) , h - > getSecSkillLevel ( CGHeroInstance : : WISDOM ) + 2 ) ; i + + )
2008-09-12 11:51:46 +03:00
{
2011-12-14 00:23:17 +03:00
if ( t - > subID = = 8 & & vstd : : contains ( t - > builtBuildings , EBuilding : : GRAIL ) ) //Aurora Borealis
2008-09-12 11:51:46 +03:00
{
2010-06-19 13:42:30 +03:00
std : : vector < ui16 > spells ;
getAllowedSpells ( spells , i ) ;
for ( int j = 0 ; j < spells . size ( ) ; + + j )
cs . spells . insert ( spells [ j ] ) ;
}
else
{
for ( int j = 0 ; j < t - > spellsAtLevel ( i + 1 , true ) & & j < t - > spells [ i ] . size ( ) ; j + + )
{
if ( ! vstd : : contains ( h - > spells , t - > spells [ i ] [ j ] ) )
cs . spells . insert ( t - > spells [ i ] [ j ] ) ;
}
2008-09-12 11:51:46 +03:00
}
}
if ( cs . spells . size ( ) )
sendAndApply ( & cs ) ;
2008-09-18 23:24:53 +03:00
}
2008-12-22 19:48:41 +02:00
void CGameHandler : : setBlockVis ( int objid , bool bv )
{
SetObjectProperty sop ( objid , 2 , bv ) ;
sendAndApply ( & sop ) ;
}
2009-04-16 03:28:54 +03:00
bool CGameHandler : : removeObject ( int objid )
2008-12-22 19:48:41 +02:00
{
2009-04-16 03:28:54 +03:00
if ( ! getObj ( objid ) )
{
tlog1 < < " Something wrong, that object already has been removed or hasn't existed! \n " ;
return false ;
}
2008-12-22 19:48:41 +02:00
RemoveObject ro ;
ro . id = objid ;
sendAndApply ( & ro ) ;
2010-01-29 22:52:45 +02:00
winLoseHandle ( 255 ) ; //eg if monster escaped (removing objs after battle is done dircetly by endBattle, not this function)
2009-04-16 03:28:54 +03:00
return true ;
2008-12-22 19:48:41 +02:00
}
void CGameHandler : : setAmount ( int objid , ui32 val )
{
SetObjectProperty sop ( objid , 3 , val ) ;
sendAndApply ( & sop ) ;
}
2009-04-16 03:28:54 +03:00
bool CGameHandler : : moveHero ( si32 hid , int3 dst , ui8 instant , ui8 asker /*= 255*/ )
2008-12-22 19:48:41 +02:00
{
2009-03-09 12:37:49 +02:00
bool blockvis = false ;
const CGHeroInstance * h = getHero ( hid ) ;
2011-05-30 22:20:14 +03:00
if ( ! h | | ( asker ! = 255 & & ( instant | | h - > getOwner ( ) ! = gs - > currentPlayer ) ) //not turn of that hero or player can't simply teleport hero (at least not with this function)
2009-03-09 12:37:49 +02:00
)
2008-12-22 19:48:41 +02:00
{
2009-03-09 12:37:49 +02:00
tlog1 < < " Illegal call to move hero! \n " ;
2009-04-16 03:28:54 +03:00
return false ;
2008-12-22 19:48:41 +02:00
}
2009-03-09 12:37:49 +02:00
2009-08-04 02:53:18 +03:00
2009-03-09 12:37:49 +02:00
tlog5 < < " Player " < < int ( asker ) < < " wants to move hero " < < hid < < " from " < < h - > pos < < " to " < < dst < < std : : endl ;
int3 hmpos = dst + int3 ( - 1 , 0 , 0 ) ;
2009-07-31 23:10:22 +03:00
if ( ! gs - > map - > isInTheMap ( hmpos ) )
{
2010-05-06 15:13:31 +03:00
tlog1 < < " Destination tile is outside the map! \n " ;
2009-07-31 23:10:22 +03:00
return false ;
}
2009-03-09 12:37:49 +02:00
TerrainTile t = gs - > map - > terrain [ hmpos . x ] [ hmpos . y ] [ hmpos . z ] ;
2010-05-15 18:00:19 +03:00
int cost = gs - > getMovementCost ( h , h - > getPosition ( false ) , CGHeroInstance : : convertPosition ( dst , false ) , h - > movement ) ;
2010-06-01 00:14:15 +03:00
int3 guardPos = gs - > guardingCreaturePosition ( hmpos ) ;
2009-03-09 12:37:49 +02:00
//result structure for start - movement failed, no move points used
TryMoveHero tmh ;
tmh . id = hid ;
tmh . start = h - > pos ;
tmh . end = dst ;
2009-07-03 22:57:14 +03:00
tmh . result = TryMoveHero : : FAILED ;
2009-03-09 12:37:49 +02:00
tmh . movePoints = h - > movement ;
//check if destination tile is available
2009-07-19 04:00:19 +03:00
//it's a rock or blocked and not visitable tile
//OR hero is on land and dest is water and (there is not present only one object - boat)
2011-07-05 14:31:26 +03:00
if ( ( ( t . tertype = = TerrainTile : : rock | | ( t . blocked & & ! t . visitable & & ! h - > hasBonusOfType ( Bonus : : FLYING_MOVEMENT ) ) )
& & complain ( " Cannot move hero, destination tile is blocked! " ) )
2011-12-14 00:23:17 +03:00
| | ( ( ! h - > boat & & ! h - > canWalkOnSea ( ) & & t . tertype = = TerrainTile : : water & & ( t . visitableObjects . size ( ) < 1 | | ( t . visitableObjects . back ( ) - > ID ! = 8 & & t . visitableObjects . back ( ) - > ID ! = GameConstants : : HEROI_TYPE ) ) ) //hero is not on boat/water walking and dst water tile doesn't contain boat/hero (objs visitable from land) -> we test back cause boat may be on top of another object (#276)
2011-07-05 14:31:26 +03:00
& & complain ( " Cannot move hero, destination tile is on water! " ) )
| | ( ( h - > boat & & t . tertype ! = TerrainTile : : water & & t . blocked )
& & complain ( " Cannot disembark hero, tile is blocked! " ) )
| | ( ( h - > movement < cost & & dst ! = h - > pos & & ! instant )
& & complain ( " Hero doesn't have any movement points left! " ) )
| | ( states . checkFlag ( h - > tempOwner , & PlayerStatus : : engagedIntoBattle )
& & complain ( " Cannot move hero during the battle " ) ) )
2009-03-09 12:37:49 +02:00
{
2009-07-19 04:00:19 +03:00
//send info about movement failure
2009-03-09 12:37:49 +02:00
sendAndApply ( & tmh ) ;
2009-04-16 03:28:54 +03:00
return false ;
2009-03-09 12:37:49 +02:00
}
2009-07-19 04:00:19 +03:00
//hero enters the boat
2011-04-23 01:28:13 +03:00
if ( ! h - > boat & & t . visitableObjects . size ( ) & & t . visitableObjects . back ( ) - > ID = = 8 )
2009-07-19 04:00:19 +03:00
{
tmh . result = TryMoveHero : : EMBARK ;
2011-09-19 23:50:25 +03:00
tmh . movePoints = h - > movementPointsAfterEmbark ( h - > movement , cost , false ) ;
2009-07-19 04:00:19 +03:00
getTilesInRange ( tmh . fowRevealed , h - > getSightCenter ( ) + ( tmh . end - tmh . start ) , h - > getSightRadious ( ) , h - > tempOwner , 1 ) ;
sendAndApply ( & tmh ) ;
return true ;
}
2009-07-19 06:10:24 +03:00
//hero leaves the boat
else if ( h - > boat & & t . tertype ! = TerrainTile : : water & & ! t . blocked )
{
2011-09-19 23:50:25 +03:00
//TODO? code similarity with the block above
2009-07-19 06:10:24 +03:00
tmh . result = TryMoveHero : : DISEMBARK ;
2011-09-19 23:50:25 +03:00
tmh . movePoints = h - > movementPointsAfterEmbark ( h - > movement , cost , true ) ;
2009-07-19 06:10:24 +03:00
getTilesInRange ( tmh . fowRevealed , h - > getSightCenter ( ) + ( tmh . end - tmh . start ) , h - > getSightRadious ( ) , h - > tempOwner , 1 ) ;
sendAndApply ( & tmh ) ;
2010-06-01 00:14:15 +03:00
tryAttackingGuard ( guardPos , h ) ;
2009-07-19 06:10:24 +03:00
return true ;
}
2009-07-19 04:00:19 +03:00
2009-03-09 12:37:49 +02:00
//checks for standard movement
if ( ! instant )
2008-12-22 19:48:41 +02:00
{
2011-07-05 14:31:26 +03:00
if ( ( distance ( h - > pos , dst ) > = 1.5 & & complain ( " Tiles are not neighboring! " ) )
| | ( h - > movement < cost & & h - > movement < 100 & & complain ( " Not enough move points! " ) ) )
2009-03-09 12:37:49 +02:00
{
sendAndApply ( & tmh ) ;
2009-04-16 03:28:54 +03:00
return false ;
2009-03-09 12:37:49 +02:00
}
//check if there is blocking visitable object
blockvis = false ;
2012-01-26 19:48:53 +03:00
tmh . movePoints = std : : max ( ( ui32 ) ( 0 ) , h - > movement - cost ) ; //take move points
2009-03-09 12:37:49 +02:00
BOOST_FOREACH ( CGObjectInstance * obj , t . visitableObjects )
{
2009-12-20 19:14:14 +02:00
if ( obj ! = h & & obj - > blockVisit & & ! ( obj - > getPassableness ( ) & 1 < < h - > tempOwner ) )
2009-03-09 12:37:49 +02:00
{
blockvis = true ;
break ;
}
}
//we start moving
if ( blockvis ) //interaction with blocking object (like resources)
2008-12-22 19:48:41 +02:00
{
2009-07-03 22:57:14 +03:00
tmh . result = TryMoveHero : : BLOCKING_VISIT ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & tmh ) ;
//failed to move to that tile but we visit object
2010-02-01 19:07:46 +02:00
if ( t . visitableObjects . size ( ) )
objectVisited ( t . visitableObjects . back ( ) , h ) ;
2009-03-09 12:37:49 +02:00
tlog5 < < " Blocking visit at " < < hmpos < < std : : endl ;
2009-04-16 03:28:54 +03:00
return true ;
2008-12-22 19:48:41 +02:00
}
2009-03-09 12:37:49 +02:00
else //normal move
{
BOOST_FOREACH ( CGObjectInstance * obj , gs - > map - > terrain [ h - > pos . x - 1 ] [ h - > pos . y ] [ h - > pos . z ] . visitableObjects )
{
obj - > onHeroLeave ( h ) ;
}
2009-03-14 13:25:25 +02:00
getTilesInRange ( tmh . fowRevealed , h - > getSightCenter ( ) + ( tmh . end - tmh . start ) , h - > getSightRadious ( ) , h - > tempOwner , 1 ) ;
2010-05-06 15:13:31 +03:00
tmh . result = TryMoveHero : : SUCCESS ;
tmh . attackedFrom = guardPos ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & tmh ) ;
tlog5 < < " Moved to " < < tmh . end < < std : : endl ;
2010-05-09 22:10:59 +03:00
// If a creature guards the tile, block visit.
2010-06-01 00:14:15 +03:00
const bool fightingGuard = tryAttackingGuard ( guardPos , h ) ;
if ( ! fightingGuard & & t . visitableObjects . size ( ) ) //call objects if they are visited
2010-02-10 04:56:00 +02:00
{
2010-05-27 00:59:58 +03:00
visitObjectOnTile ( t , h ) ;
2010-02-10 04:56:00 +02:00
}
2010-05-06 15:13:31 +03:00
tlog5 < < " Movement end! \n " ;
return true ;
2009-03-09 12:37:49 +02:00
}
}
else //instant move - teleportation
{
BOOST_FOREACH ( CGObjectInstance * obj , t . blockingObjects )
{
2011-12-14 00:23:17 +03:00
if ( obj - > ID = = GameConstants : : HEROI_TYPE )
2009-03-09 12:37:49 +02:00
{
2009-07-21 02:34:06 +03:00
CGHeroInstance * dh = static_cast < CGHeroInstance * > ( obj ) ;
2010-08-13 13:46:08 +03:00
if ( gameState ( ) - > getPlayerRelations ( dh - > tempOwner , h - > tempOwner ) )
2009-07-21 02:34:06 +03:00
{
2010-08-12 18:54:25 +03:00
heroExchange ( h - > id , dh - > id ) ;
2009-07-21 02:34:06 +03:00
return true ;
}
2009-09-13 01:17:23 +03:00
startBattleI ( h , dh ) ;
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
}
2009-07-03 22:57:14 +03:00
tmh . result = TryMoveHero : : TELEPORTATION ;
2009-03-14 13:25:25 +02:00
getTilesInRange ( tmh . fowRevealed , h - > getSightCenter ( ) + ( tmh . end - tmh . start ) , h - > getSightRadious ( ) , h - > tempOwner , 1 ) ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & tmh ) ;
2009-04-16 03:28:54 +03:00
return true ;
2008-12-22 19:48:41 +02:00
}
}
2010-06-30 22:27:35 +03:00
bool CGameHandler : : teleportHero ( si32 hid , si32 dstid , ui8 source , ui8 asker /* = 255*/ )
{
const CGHeroInstance * h = getHero ( hid ) ;
const CGTownInstance * t = getTown ( dstid ) ;
if ( ! h | | ! t | | h - > getOwner ( ) ! = gs - > currentPlayer )
tlog1 < < " Invalid call to teleportHero! " ;
const CGTownInstance * from = h - > visitedTown ;
2011-07-05 14:31:26 +03:00
if ( ( ( h - > getOwner ( ) ! = t - > getOwner ( ) )
& & complain ( " Cannot teleport hero to another player " ) )
2011-12-14 00:23:17 +03:00
| | ( ( ! from | | from - > subID ! = 3 | | ! vstd : : contains ( from - > builtBuildings , EBuilding : : SPECIAL_3 ) )
2011-07-05 14:31:26 +03:00
& & complain ( " Hero must be in town with Castle gate for teleporting " ) )
2011-12-14 00:23:17 +03:00
| | ( ( t - > subID ! = 3 | | ! vstd : : contains ( t - > builtBuildings , EBuilding : : SPECIAL_3 ) )
2011-07-05 14:31:26 +03:00
& & complain ( " Cannot teleport hero to town without Castle gate in it " ) ) )
2010-06-30 22:27:35 +03:00
return false ;
int3 pos = t - > visitablePos ( ) ;
2010-07-12 13:42:51 +03:00
pos + = h - > getVisitableOffset ( ) ;
2010-06-30 22:27:35 +03:00
stopHeroVisitCastle ( from - > id , hid ) ;
moveHero ( hid , pos , 1 ) ;
heroVisitCastle ( dstid , hid ) ;
2010-07-10 19:50:23 +03:00
return true ;
2010-06-30 22:27:35 +03:00
}
2008-12-22 19:48:41 +02:00
void CGameHandler : : setOwner ( int objid , ui8 owner )
{
2010-02-02 01:30:03 +02:00
ui8 oldOwner = getOwner ( objid ) ;
2008-12-22 19:48:41 +02:00
SetObjectProperty sop ( objid , 1 , owner ) ;
sendAndApply ( & sop ) ;
2010-01-29 22:52:45 +02:00
2010-02-02 01:30:03 +02:00
winLoseHandle ( 1 < < owner | 1 < < oldOwner ) ;
2011-12-14 00:23:17 +03:00
if ( owner < GameConstants : : PLAYER_LIMIT & & getTown ( objid ) ) //town captured
2010-02-02 01:30:03 +02:00
{
2010-07-10 19:50:23 +03:00
const CGTownInstance * town = getTown ( objid ) ;
if ( town - > subID = = 5 & & vstd : : contains ( town - > builtBuildings , 22 ) )
2010-08-24 17:26:57 +03:00
setPortalDwelling ( town , true , false ) ;
2010-07-10 19:50:23 +03:00
if ( ! gs - > getPlayer ( owner ) - > towns . size ( ) ) //player lost last town
{
InfoWindow iw ;
iw . player = oldOwner ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 6 ) ; //%s, you have lost your last town. If you do not conquer another town in the next week, you will be eliminated.
sendAndApply ( & iw ) ;
}
2010-02-02 01:30:03 +02:00
}
2010-07-10 19:50:23 +03:00
const CGObjectInstance * obj = getObj ( objid ) ;
2010-08-01 17:46:19 +03:00
const PlayerState * p = gs - > getPlayer ( owner ) ;
if ( ( obj - > ID = = 17 | | obj - > ID = = 20 ) & & p & & p - > dwellings . size ( ) = = 1 ) //first dwelling captured
2011-07-05 14:31:26 +03:00
{
2010-07-10 19:50:23 +03:00
BOOST_FOREACH ( const CGTownInstance * t , gs - > getPlayer ( owner ) - > towns )
2011-07-05 14:31:26 +03:00
{
2010-07-10 19:50:23 +03:00
if ( t - > subID = = 5 & & vstd : : contains ( t - > builtBuildings , 22 ) )
setPortalDwelling ( t ) ; //set initial creatures for all portals of summoning
2011-07-05 14:31:26 +03:00
}
}
2008-12-22 19:48:41 +02:00
}
2010-07-10 19:50:23 +03:00
2008-12-22 19:48:41 +02:00
void CGameHandler : : setHoverName ( int objid , MetaString * name )
{
SetHoverName shn ( objid , * name ) ;
sendAndApply ( & shn ) ;
}
2011-05-22 21:46:52 +03:00
2009-04-11 04:32:50 +03:00
void CGameHandler : : showBlockingDialog ( BlockingDialog * iw , const CFunctionList < void ( ui32 ) > & callback )
2008-12-22 19:48:41 +02:00
{
ask ( iw , iw - > player , callback ) ;
}
2009-04-11 04:32:50 +03:00
ui32 CGameHandler : : showBlockingDialog ( BlockingDialog * iw )
2008-12-22 19:48:41 +02:00
{
2009-04-11 04:32:50 +03:00
//TODO
//gsm.lock();
//int query = QID++;
//states.addQuery(player,query);
//sendToAllClients(iw);
//gsm.unlock();
//ui32 ret = getQueryResult(iw->player, query);
//gsm.lock();
//states.removeQuery(player, query);
//gsm.unlock();
return 0 ;
2008-12-22 19:48:41 +02:00
}
2009-04-11 04:32:50 +03:00
2012-01-26 19:48:53 +03:00
void CGameHandler : : giveResource ( int player , int which , int val ) //TODO: cap according to Bersy's suggestion
2008-12-22 19:48:41 +02:00
{
2009-07-19 06:10:24 +03:00
if ( ! val ) return ; //don't waste time on empty call
2008-12-22 19:48:41 +02:00
SetResource sr ;
sr . player = player ;
sr . resid = which ;
2011-04-23 00:51:10 +03:00
sr . val = gs - > players . find ( player ) - > second . resources [ which ] + val ;
2008-12-22 19:48:41 +02:00
sendAndApply ( & sr ) ;
}
2010-12-13 01:44:16 +02:00
void CGameHandler : : giveCreatures ( const CArmedInstance * obj , const CGHeroInstance * h , const CCreatureSet & creatures , bool remove )
{
boost : : function < void ( ) > removeOrNot = 0 ;
if ( remove )
removeOrNot = boost : : bind ( & CGameHandler : : removeObject , this , obj - > id ) ;
COMPLAIN_RET_IF ( ! creatures . stacksCount ( ) , " Strange, giveCreatures called without args! " ) ;
COMPLAIN_RET_IF ( obj - > stacksCount ( ) , " Cannot give creatures from not-cleared object! " ) ;
2011-12-14 00:23:17 +03:00
COMPLAIN_RET_IF ( creatures . stacksCount ( ) > GameConstants : : ARMY_SIZE , " Too many stacks to give! " ) ;
2010-12-13 01:44:16 +02:00
//first we move creatures to give to make them army of object-source
2011-03-08 15:27:32 +02:00
for ( TSlots : : const_iterator stack = creatures . Slots ( ) . begin ( ) ; stack ! = creatures . Slots ( ) . end ( ) ; stack + + )
2010-12-13 01:44:16 +02:00
{
2011-03-08 15:27:32 +02:00
addToSlot ( StackLocation ( obj , obj - > getSlotFor ( stack - > second - > type ) ) , stack - > second - > type , stack - > second - > count ) ;
}
2010-12-13 01:44:16 +02:00
2011-03-08 15:27:32 +02:00
tryJoiningArmy ( obj , h , remove , true ) ;
2009-08-11 10:50:29 +03:00
}
2010-12-06 01:10:02 +02:00
2011-05-10 01:20:47 +03:00
void CGameHandler : : takeCreatures ( int objid , const std : : vector < CStackBasicDescriptor > & creatures )
2010-02-07 18:05:27 +02:00
{
2011-05-10 01:20:47 +03:00
std : : vector < CStackBasicDescriptor > cres = creatures ;
if ( cres . size ( ) < = 0 )
2010-02-07 18:05:27 +02:00
return ;
const CArmedInstance * obj = static_cast < const CArmedInstance * > ( getObj ( objid ) ) ;
2010-11-22 02:34:46 +02:00
2011-05-10 01:20:47 +03:00
BOOST_FOREACH ( CStackBasicDescriptor & sbd , cres )
2010-12-06 01:10:02 +02:00
{
TQuantity collected = 0 ;
while ( collected < sbd . count )
{
TSlots : : const_iterator i = obj - > Slots ( ) . begin ( ) ;
for ( ; i ! = obj - > Slots ( ) . end ( ) ; i + + )
{
if ( i - > second - > type = = sbd . type )
{
2011-05-10 01:20:47 +03:00
TQuantity take = std : : min ( sbd . count - collected , i - > second - > count ) ; //collect as much cres as we can
2011-09-24 03:32:55 +03:00
changeStackCount ( StackLocation ( obj , i - > first ) , - take , false ) ;
2010-12-06 01:10:02 +02:00
collected + = take ;
break ;
}
}
2010-11-22 02:34:46 +02:00
2011-05-10 01:20:47 +03:00
if ( i = = obj - > Slots ( ) . end ( ) ) //we went through the whole loop and haven't found appropriate cres
2010-12-06 01:10:02 +02:00
{
complain ( " Unexpected failure during taking creatures! " ) ;
return ;
}
}
}
2010-11-22 02:34:46 +02:00
}
2008-12-22 19:48:41 +02:00
void CGameHandler : : showCompInfo ( ShowInInfobox * comp )
{
sendToAllClients ( comp ) ;
}
void CGameHandler : : heroVisitCastle ( int obj , int heroID )
{
HeroVisitCastle vc ;
vc . hid = heroID ;
vc . tid = obj ;
vc . flags | = 1 ;
sendAndApply ( & vc ) ;
2010-01-30 14:46:15 +02:00
const CGHeroInstance * h = getHero ( heroID ) ;
vistiCastleObjects ( getTown ( obj ) , h ) ;
2009-10-04 17:20:19 +03:00
giveSpells ( getTown ( obj ) , getHero ( heroID ) ) ;
2010-01-30 14:46:15 +02:00
2011-12-14 00:23:17 +03:00
if ( gs - > map - > victoryCondition . condition = = EVictoryConditionType : : TRANSPORTITEM )
2010-01-30 14:46:15 +02:00
checkLossVictory ( h - > tempOwner ) ; //transported artifact?
2009-10-04 17:20:19 +03:00
}
void CGameHandler : : vistiCastleObjects ( const CGTownInstance * t , const CGHeroInstance * h )
{
std : : vector < CGTownBuilding * > : : const_iterator i ;
for ( i = t - > bonusingBuildings . begin ( ) ; i ! = t - > bonusingBuildings . end ( ) ; i + + )
( * i ) - > onHeroVisit ( h ) ;
2008-12-22 19:48:41 +02:00
}
void CGameHandler : : stopHeroVisitCastle ( int obj , int heroID )
{
HeroVisitCastle vc ;
vc . hid = heroID ;
vc . tid = obj ;
sendAndApply ( & vc ) ;
}
2009-04-04 01:34:31 +03:00
2010-12-26 16:34:11 +02:00
void CGameHandler : : removeArtifact ( const ArtifactLocation & al )
{
2010-12-29 23:04:22 +02:00
assert ( al . getArt ( ) ) ;
EraseArtifact ea ;
ea . al = al ;
sendAndApply ( & ea ) ;
2010-12-26 16:34:11 +02:00
}
2009-08-22 16:59:15 +03:00
void CGameHandler : : startBattleI ( const CArmedInstance * army1 , const CArmedInstance * army2 , int3 tile , const CGHeroInstance * hero1 , const CGHeroInstance * hero2 , bool creatureBank , boost : : function < void ( BattleResult * ) > cb , const CGTownInstance * town ) //use hero=NULL for no hero
2009-07-21 02:34:06 +03:00
{
2009-08-04 02:53:18 +03:00
engageIntoBattle ( army1 - > tempOwner ) ;
engageIntoBattle ( army2 - > tempOwner ) ;
//block engaged players
2011-12-14 00:23:17 +03:00
if ( army2 - > tempOwner < GameConstants : : PLAYER_LIMIT )
2009-08-04 02:53:18 +03:00
states . setFlag ( army2 - > tempOwner , & PlayerStatus : : engagedIntoBattle , true ) ;
2010-12-29 23:04:22 +02:00
static const CArmedInstance * armies [ 2 ] ;
2010-12-22 22:14:40 +02:00
armies [ 0 ] = army1 ;
armies [ 1 ] = army2 ;
2010-12-29 23:04:22 +02:00
static const CGHeroInstance * heroes [ 2 ] ;
2010-12-22 22:14:40 +02:00
heroes [ 0 ] = hero1 ;
heroes [ 1 ] = hero2 ;
boost : : thread ( boost : : bind ( & CGameHandler : : startBattle , this , armies , tile , heroes , creatureBank , cb , town ) ) ;
2009-07-21 02:34:06 +03:00
}
2009-09-13 01:17:23 +03:00
void CGameHandler : : startBattleI ( const CArmedInstance * army1 , const CArmedInstance * army2 , int3 tile , boost : : function < void ( BattleResult * ) > cb , bool creatureBank )
2008-12-22 19:48:41 +02:00
{
2009-07-21 02:34:06 +03:00
startBattleI ( army1 , army2 , tile ,
2011-12-14 00:23:17 +03:00
army1 - > ID = = GameConstants : : HEROI_TYPE ? static_cast < const CGHeroInstance * > ( army1 ) : NULL ,
army2 - > ID = = GameConstants : : HEROI_TYPE ? static_cast < const CGHeroInstance * > ( army2 ) : NULL ,
2009-08-17 13:47:08 +03:00
creatureBank , cb ) ;
2008-12-22 19:48:41 +02:00
}
2009-07-21 02:34:06 +03:00
2009-09-13 01:17:23 +03:00
void CGameHandler : : startBattleI ( const CArmedInstance * army1 , const CArmedInstance * army2 , boost : : function < void ( BattleResult * ) > cb , bool creatureBank )
2008-12-22 19:48:41 +02:00
{
2010-05-15 11:33:32 +03:00
startBattleI ( army1 , army2 , army2 - > visitablePos ( ) , cb , creatureBank ) ;
2008-12-22 19:48:41 +02:00
}
void CGameHandler : : changeSpells ( int hid , bool give , const std : : set < ui32 > & spells )
{
ChangeSpells cs ;
cs . hid = hid ;
cs . spells = spells ;
cs . learn = give ;
sendAndApply ( & cs ) ;
}
2009-03-07 17:54:12 +02:00
void CGameHandler : : sendMessageTo ( CConnection & c , const std : : string & message )
2009-02-01 16:11:41 +02:00
{
2009-03-07 17:54:12 +02:00
SystemMessage sm ;
sm . text = message ;
2011-07-17 21:49:05 +03:00
boost : : unique_lock < boost : : mutex > lock ( * c . wmx ) ;
2009-03-07 17:54:12 +02:00
c < < & sm ;
2009-02-04 15:40:54 +02:00
}
void CGameHandler : : giveHeroBonus ( GiveBonus * bonus )
{
sendAndApply ( bonus ) ;
}
void CGameHandler : : setMovePoints ( SetMovePoints * smp )
{
sendAndApply ( smp ) ;
2009-02-06 13:15:39 +02:00
}
void CGameHandler : : setManaPoints ( int hid , int val )
{
SetMana sm ;
sm . hid = hid ;
sm . val = val ;
sendAndApply ( & sm ) ;
2009-02-14 21:12:40 +02:00
}
void CGameHandler : : giveHero ( int id , int player )
{
GiveHero gh ;
gh . id = id ;
gh . player = player ;
sendAndApply ( & gh ) ;
2009-02-20 12:36:15 +02:00
}
void CGameHandler : : changeObjPos ( int objid , int3 newPos , ui8 flags )
{
ChangeObjPos cop ;
cop . objid = objid ;
cop . nPos = newPos ;
cop . flags = flags ;
sendAndApply ( & cop ) ;
2009-03-07 00:11:17 +02:00
}
2010-02-06 15:27:58 +02:00
void CGameHandler : : useScholarSkill ( si32 fromHero , si32 toHero )
2010-02-28 12:01:36 +02:00
{
2010-02-06 15:27:58 +02:00
const CGHeroInstance * h1 = getHero ( fromHero ) ;
const CGHeroInstance * h2 = getHero ( toHero ) ;
2010-12-23 22:18:10 +02:00
if ( h1 - > getSecSkillLevel ( CGHeroInstance : : SCHOLAR ) < h2 - > getSecSkillLevel ( CGHeroInstance : : SCHOLAR ) )
2010-02-28 12:01:36 +02:00
{
std : : swap ( h1 , h2 ) ; //1st hero need to have higher scholar level for correct message
std : : swap ( fromHero , toHero ) ;
}
2010-02-06 15:27:58 +02:00
2010-12-23 22:18:10 +02:00
int ScholarLevel = h1 - > getSecSkillLevel ( CGHeroInstance : : SCHOLAR ) ; //heroes can trade up to this level
2011-01-18 20:56:14 +02:00
if ( ! ScholarLevel | | ! h1 - > hasSpellbook ( ) | | ! h2 - > hasSpellbook ( ) )
2010-02-28 12:01:36 +02:00
return ; //no scholar skill or no spellbook
2010-02-06 15:27:58 +02:00
2010-12-23 22:18:10 +02:00
int h1Lvl = std : : min ( ScholarLevel + 1 , h1 - > getSecSkillLevel ( CGHeroInstance : : WISDOM ) + 2 ) ,
h2Lvl = std : : min ( ScholarLevel + 1 , h2 - > getSecSkillLevel ( CGHeroInstance : : WISDOM ) + 2 ) ; //heroes can receive this levels
2010-02-06 15:27:58 +02:00
2010-02-28 12:01:36 +02:00
ChangeSpells cs1 ;
cs1 . learn = true ;
cs1 . hid = toHero ; //giving spells to first hero
for ( std : : set < ui32 > : : const_iterator it = h1 - > spells . begin ( ) ; it ! = h1 - > spells . end ( ) ; it + + )
2010-12-19 16:39:56 +02:00
if ( h2Lvl > = VLC - > spellh - > spells [ * it ] - > level & & ! vstd : : contains ( h2 - > spells , * it ) ) //hero can learn it and don't have it yet
2010-02-28 12:01:36 +02:00
cs1 . spells . insert ( * it ) ; //spell to learn
2010-02-06 15:27:58 +02:00
2010-02-28 12:01:36 +02:00
ChangeSpells cs2 ;
cs2 . learn = true ;
cs2 . hid = fromHero ;
2010-02-06 15:27:58 +02:00
2010-02-06 15:55:39 +02:00
for ( std : : set < ui32 > : : const_iterator it = h2 - > spells . begin ( ) ; it ! = h2 - > spells . end ( ) ; it + + )
2010-12-19 16:39:56 +02:00
if ( h1Lvl > = VLC - > spellh - > spells [ * it ] - > level & & ! vstd : : contains ( h1 - > spells , * it ) )
2010-02-28 12:01:36 +02:00
cs2 . spells . insert ( * it ) ;
if ( cs1 . spells . size ( ) | | cs2 . spells . size ( ) ) //create a message
{
InfoWindow iw ;
iw . player = h1 - > tempOwner ;
iw . components . push_back ( Component ( Component : : SEC_SKILL , 18 , ScholarLevel , 0 ) ) ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 139 ) ; //"%s, who has studied magic extensively,
iw . text . addReplacement ( h1 - > name ) ;
if ( cs2 . spells . size ( ) ) //if found new spell - apply
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 140 ) ; //learns
int size = cs2 . spells . size ( ) ;
for ( std : : set < ui32 > : : const_iterator it = cs2 . spells . begin ( ) ; it ! = cs2 . spells . end ( ) ; it + + )
{
iw . components . push_back ( Component ( Component : : SPELL , ( * it ) , 1 , 0 ) ) ;
iw . text . addTxt ( MetaString : : SPELL_NAME , ( * it ) ) ;
switch ( size - - )
{
case 2 : iw . text . addTxt ( MetaString : : GENERAL_TXT , 141 ) ;
case 1 : break ;
default : iw . text < < " , " ;
}
}
iw . text . addTxt ( MetaString : : GENERAL_TXT , 142 ) ; //from %s
iw . text . addReplacement ( h2 - > name ) ;
sendAndApply ( & cs2 ) ;
}
if ( cs1 . spells . size ( ) & & cs2 . spells . size ( ) )
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 141 ) ; //and
}
if ( cs1 . spells . size ( ) )
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 147 ) ; //teaches
int size = cs1 . spells . size ( ) ;
for ( std : : set < ui32 > : : const_iterator it = cs1 . spells . begin ( ) ; it ! = cs1 . spells . end ( ) ; it + + )
{
iw . components . push_back ( Component ( Component : : SPELL , ( * it ) , 1 , 0 ) ) ;
iw . text . addTxt ( MetaString : : SPELL_NAME , ( * it ) ) ;
switch ( size - - )
{
case 2 : iw . text . addTxt ( MetaString : : GENERAL_TXT , 141 ) ;
case 1 : break ;
default : iw . text < < " , " ;
} }
iw . text . addTxt ( MetaString : : GENERAL_TXT , 148 ) ; //from %s
iw . text . addReplacement ( h2 - > name ) ;
sendAndApply ( & cs1 ) ;
}
sendAndApply ( & iw ) ;
}
2010-02-06 15:27:58 +02:00
}
2009-06-16 14:18:14 +03:00
void CGameHandler : : heroExchange ( si32 hero1 , si32 hero2 )
{
ui8 player1 = getHero ( hero1 ) - > tempOwner ;
ui8 player2 = getHero ( hero2 ) - > tempOwner ;
2010-08-13 13:46:08 +03:00
if ( gameState ( ) - > getPlayerRelations ( player1 , player2 ) )
2009-06-16 14:18:14 +03:00
{
2009-07-06 22:41:27 +03:00
OpenWindow hex ;
hex . window = OpenWindow : : EXCHANGE_WINDOW ;
hex . id1 = hero1 ;
hex . id2 = hero2 ;
2009-06-16 14:18:14 +03:00
sendAndApply ( & hex ) ;
2010-02-28 12:01:36 +02:00
useScholarSkill ( hero1 , hero2 ) ;
2009-06-16 14:18:14 +03:00
}
}
2009-03-07 00:11:17 +02:00
void CGameHandler : : applyAndAsk ( Query * sel , ui8 player , boost : : function < void ( ui32 ) > & callback )
{
2009-04-12 04:48:50 +03:00
boost : : unique_lock < boost : : recursive_mutex > lock ( gsm ) ;
2009-03-07 00:11:17 +02:00
sel - > id = QID ;
callbacks [ QID ] = callback ;
states . addQuery ( player , QID ) ;
QID + + ;
sendAndApply ( sel ) ;
}
void CGameHandler : : ask ( Query * sel , ui8 player , const CFunctionList < void ( ui32 ) > & callback )
{
2009-04-12 04:48:50 +03:00
boost : : unique_lock < boost : : recursive_mutex > lock ( gsm ) ;
2009-03-07 00:11:17 +02:00
sel - > id = QID ;
callbacks [ QID ] = callback ;
states . addQuery ( player , QID ) ;
sendToAllClients ( sel ) ;
QID + + ;
2009-03-07 17:54:12 +02:00
}
2009-03-09 12:37:49 +02:00
void CGameHandler : : sendToAllClients ( CPackForClient * info )
2009-03-07 17:54:12 +02:00
{
2009-03-31 23:47:53 +03:00
tlog5 < < " Sending to all clients a package of type " < < typeid ( * info ) . name ( ) < < std : : endl ;
2009-03-07 17:54:12 +02:00
for ( std : : set < CConnection * > : : iterator i = conns . begin ( ) ; i ! = conns . end ( ) ; i + + )
{
2011-07-17 21:49:05 +03:00
boost : : unique_lock < boost : : mutex > lock ( * ( * i ) - > wmx ) ;
2009-03-07 17:54:12 +02:00
* * i < < info ;
}
}
2011-09-06 12:59:06 +03:00
void CGameHandler : : sendAndApply ( CPackForClient * info )
2009-03-07 17:54:12 +02:00
{
sendToAllClients ( info ) ;
2010-12-06 01:10:02 +02:00
gs - > apply ( info ) ;
2009-03-09 12:37:49 +02:00
}
2011-09-06 12:59:06 +03:00
void CGameHandler : : applyAndSend ( CPackForClient * info )
{
gs - > apply ( info ) ;
sendToAllClients ( info ) ;
}
2010-12-06 01:10:02 +02:00
void CGameHandler : : sendAndApply ( CGarrisonOperationPack * info )
2010-01-30 14:46:15 +02:00
{
sendAndApply ( ( CPackForClient * ) info ) ;
2011-12-14 00:23:17 +03:00
if ( gs - > map - > victoryCondition . condition = = EVictoryConditionType : : GATHERTROOP )
2010-12-06 01:10:02 +02:00
winLoseHandle ( ) ;
2010-01-30 14:46:15 +02:00
}
2010-12-06 01:10:02 +02:00
// void CGameHandler::sendAndApply( SetGarrisons * info )
// {
// sendAndApply((CPackForClient*)info);
// if(gs->map->victoryCondition.condition == gatherTroop)
// for(std::map<ui32,CCreatureSet>::const_iterator i = info->garrs.begin(); i != info->garrs.end(); i++)
// checkLossVictory(getObj(i->first)->tempOwner);
// }
2010-01-30 14:46:15 +02:00
void CGameHandler : : sendAndApply ( SetResource * info )
{
sendAndApply ( ( CPackForClient * ) info ) ;
2011-12-14 00:23:17 +03:00
if ( gs - > map - > victoryCondition . condition = = EVictoryConditionType : : GATHERRESOURCE )
2010-01-30 14:46:15 +02:00
checkLossVictory ( info - > player ) ;
}
void CGameHandler : : sendAndApply ( SetResources * info )
{
sendAndApply ( ( CPackForClient * ) info ) ;
2011-12-14 00:23:17 +03:00
if ( gs - > map - > victoryCondition . condition = = EVictoryConditionType : : GATHERRESOURCE )
2010-01-30 14:46:15 +02:00
checkLossVictory ( info - > player ) ;
}
void CGameHandler : : sendAndApply ( NewStructures * info )
{
sendAndApply ( ( CPackForClient * ) info ) ;
2011-12-14 00:23:17 +03:00
if ( gs - > map - > victoryCondition . condition = = EVictoryConditionType : : BUILDCITY )
2010-01-30 14:46:15 +02:00
checkLossVictory ( getTown ( info - > tid ) - > tempOwner ) ;
}
2010-12-06 01:10:02 +02:00
2009-03-09 12:37:49 +02:00
void CGameHandler : : save ( const std : : string & fname )
{
2009-03-28 20:46:20 +02:00
{
tlog0 < < " Ordering clients to serialize... \n " ;
SaveGame sg ( fname ) ;
2009-07-18 06:13:13 +03:00
sendToAllClients ( & sg ) ;
2009-03-28 20:46:20 +02:00
}
2009-03-09 12:37:49 +02:00
{
tlog0 < < " Serializing game info... \n " ;
2009-10-10 08:47:59 +03:00
CSaveFile save ( GVCMIDirs . UserPath + " /Games/ " + fname + " .vlgm1 " ) ;
2009-03-09 12:37:49 +02:00
char hlp [ 8 ] = " VCMISVG " ;
2010-02-24 15:03:36 +02:00
save < < hlp < < static_cast < CMapHeader & > ( * gs - > map ) < < gs - > scenarioOps < < * VLC < < gs ;
2009-03-09 12:37:49 +02:00
}
{
tlog0 < < " Serializing server info... \n " ;
2009-10-10 08:47:59 +03:00
CSaveFile save ( GVCMIDirs . UserPath + " /Games/ " + fname + " .vsgm1 " ) ;
2009-03-09 12:37:49 +02:00
save < < * this ;
}
2009-11-13 21:04:36 +02:00
tlog0 < < " Game has been successfully saved! \n " ;
2009-03-09 12:37:49 +02:00
}
void CGameHandler : : close ( )
{
2009-04-11 04:32:50 +03:00
tlog0 < < " We have been requested to close. \n " ;
2011-01-08 21:38:42 +02:00
if ( gs - > initialOpts - > mode = = StartInfo : : DUEL )
{
exit ( 0 ) ;
}
2009-04-11 04:32:50 +03:00
//BOOST_FOREACH(CConnection *cc, conns)
// if(cc && cc->socket && cc->socket->is_open())
// cc->socket->close();
//exit(0);
2009-03-09 12:37:49 +02:00
}
2010-08-12 18:54:25 +03:00
bool CGameHandler : : arrangeStacks ( si32 id1 , si32 id2 , ui8 what , ui8 p1 , ui8 p2 , si32 val , ui8 player )
2009-03-09 12:37:49 +02:00
{
2011-07-05 22:05:41 +03:00
const CArmedInstance * s1 = static_cast < CArmedInstance * > ( gs - > map - > objects [ id1 ] . get ( ) ) ,
2010-12-19 00:11:28 +02:00
* s2 = static_cast < CArmedInstance * > ( gs - > map - > objects [ id2 ] . get ( ) ) ;
2011-07-05 22:05:41 +03:00
const CCreatureSet & S1 = * s1 , & S2 = * s2 ;
2010-11-27 22:17:28 +02:00
StackLocation sl1 ( s1 , p1 ) , sl2 ( s2 , p2 ) ;
2009-03-09 12:37:49 +02:00
2009-04-12 03:58:41 +03:00
if ( ! isAllowedExchange ( id1 , id2 ) )
{
complain ( " Cannot exchange stacks between these two objects! \n " ) ;
2009-04-16 03:28:54 +03:00
return false ;
2009-04-12 03:58:41 +03:00
}
2009-03-09 12:37:49 +02:00
if ( what = = 1 ) //swap
{
2011-07-05 22:05:41 +03:00
if ( ( ( s1 - > tempOwner ! = player & & s1 - > tempOwner ! = 254 ) & & s1 - > getStackCount ( p1 ) ) //why 254??
| | ( ( s2 - > tempOwner ! = player & & s2 - > tempOwner ! = 254 ) & & s2 - > getStackCount ( p2 ) ) )
2010-08-12 18:54:25 +03:00
{
complain ( " Can't take troops from another player! " ) ;
return false ;
}
2009-03-09 12:37:49 +02:00
2010-11-27 22:17:28 +02:00
swapStacks ( sl1 , sl2 ) ;
2009-03-09 12:37:49 +02:00
}
else if ( what = = 2 ) //merge
{
2011-07-05 22:05:41 +03:00
if ( ( s1 - > getCreature ( p1 ) ! = s2 - > getCreature ( p2 ) & & complain ( " Cannot merge different creatures stacks! " ) )
| | ( ( ( s1 - > tempOwner ! = player & & s1 - > tempOwner ! = 254 ) & & s2 - > getStackCount ( p2 ) ) & & complain ( " Can't take troops from another player! " ) ) )
2009-04-16 03:28:54 +03:00
return false ;
2009-03-09 12:37:49 +02:00
2010-11-27 22:17:28 +02:00
moveStack ( sl1 , sl2 ) ;
2009-03-09 12:37:49 +02:00
}
else if ( what = = 3 ) //split
{
2011-07-05 22:05:41 +03:00
if ( ( s1 - > tempOwner ! = player & & s1 - > getStackCount ( p1 ) < s1 - > getStackCount ( p1 ) )
| | ( s2 - > tempOwner ! = player & & s2 - > getStackCount ( p2 ) < s2 - > getStackCount ( p2 ) ) )
2010-11-27 22:17:28 +02:00
{
complain ( " Can't move troops of another player! " ) ;
return false ;
}
2009-03-27 01:05:40 +02:00
//general conditions checking
2011-01-21 04:36:30 +02:00
if ( ( ! vstd : : contains ( S1 . stacks , p1 ) & & complain ( " no creatures to split " ) )
2009-03-27 01:05:40 +02:00
| | ( val < 1 & & complain ( " no creatures to split " ) ) )
2009-03-09 12:37:49 +02:00
{
2009-04-16 03:28:54 +03:00
return false ;
2009-03-27 01:05:40 +02:00
}
2011-01-21 04:36:30 +02:00
if ( vstd : : contains ( S2 . stacks , p2 ) ) //dest. slot not free - it must be "rebalancing"...
2009-03-27 01:05:40 +02:00
{
2011-07-05 22:05:41 +03:00
int total = s1 - > getStackCount ( p1 ) + s2 - > getStackCount ( p2 ) ;
2009-03-27 01:05:40 +02:00
if ( ( total < val & & complain ( " Cannot split that stack, not enough creatures! " ) )
2011-07-05 22:05:41 +03:00
| | ( s1 - > getCreature ( p1 ) ! = s2 - > getCreature ( p2 ) & & complain ( " Cannot rebalance different creatures stacks! " ) )
2009-03-27 01:05:40 +02:00
)
{
2009-04-16 03:28:54 +03:00
return false ;
2009-03-27 01:05:40 +02:00
}
2011-07-05 22:05:41 +03:00
moveStack ( sl1 , sl2 , val - s2 - > getStackCount ( p2 ) ) ;
2010-11-27 22:17:28 +02:00
//S2.slots[p2]->count = val;
//S1.slots[p1]->count = total - val;
2009-03-27 01:05:40 +02:00
}
else //split one stack to the two
{
2011-07-05 22:05:41 +03:00
if ( s1 - > getStackCount ( p1 ) < val ) //not enough creatures
2009-03-27 01:05:40 +02:00
{
complain ( " Cannot split that stack, not enough creatures! " ) ;
2009-04-16 03:28:54 +03:00
return false ;
2009-03-27 01:05:40 +02:00
}
2009-03-09 12:37:49 +02:00
2010-11-27 22:17:28 +02:00
moveStack ( sl1 , sl2 , val ) ;
2010-08-12 18:54:25 +03:00
}
2009-03-09 12:37:49 +02:00
}
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
int CGameHandler : : getPlayerAt ( CConnection * c ) const
{
std : : set < int > all ;
for ( std : : map < int , CConnection * > : : const_iterator i = connections . begin ( ) ; i ! = connections . end ( ) ; i + + )
if ( i - > second = = c )
all . insert ( i - > first ) ;
switch ( all . size ( ) )
{
case 0 :
return 255 ;
case 1 :
return * all . begin ( ) ;
default :
{
//if we have more than one player at this connection, try to pick active one
if ( vstd : : contains ( all , int ( gs - > currentPlayer ) ) )
return gs - > currentPlayer ;
else
return 253 ; //cannot say which player is it
}
}
}
2009-04-16 03:28:54 +03:00
bool CGameHandler : : disbandCreature ( si32 id , ui8 pos )
2009-03-09 12:37:49 +02:00
{
2010-12-19 00:11:28 +02:00
CArmedInstance * s1 = static_cast < CArmedInstance * > ( gs - > map - > objects [ id ] . get ( ) ) ;
2011-01-21 04:36:30 +02:00
if ( ! vstd : : contains ( s1 - > stacks , pos ) )
2009-03-09 12:37:49 +02:00
{
2009-03-27 01:05:40 +02:00
complain ( " Illegal call to disbandCreature - no such stack in army! " ) ;
2009-04-16 03:28:54 +03:00
return false ;
2009-03-09 12:37:49 +02:00
}
2010-11-27 22:17:28 +02:00
eraseStack ( StackLocation ( s1 , pos ) ) ;
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
2010-08-18 17:24:30 +03:00
bool CGameHandler : : buildStructure ( si32 tid , si32 bid , bool force /*=false*/ )
2009-03-09 12:37:49 +02:00
{
2010-12-19 00:11:28 +02:00
CGTownInstance * t = static_cast < CGTownInstance * > ( gs - > map - > objects [ tid ] . get ( ) ) ;
2009-03-09 12:37:49 +02:00
CBuilding * b = VLC - > buildh - > buildings [ t - > subID ] [ bid ] ;
2010-08-18 17:24:30 +03:00
if ( ! force & & gs - > canBuildStructure ( t , bid ) ! = 7 )
2009-03-09 12:37:49 +02:00
{
2010-02-24 15:03:36 +02:00
complain ( " Cannot build that building! " ) ;
2009-04-16 03:28:54 +03:00
return false ;
2009-03-09 12:37:49 +02:00
}
2010-02-24 15:03:36 +02:00
2010-08-18 17:24:30 +03:00
if ( ! force & & bid = = 26 ) //grail
2010-02-24 15:03:36 +02:00
{
if ( ! t - > visitingHero | | ! t - > visitingHero - > hasArt ( 2 ) )
{
complain ( " Cannot build grail - hero doesn't have it " ) ;
return false ;
}
2010-12-29 23:04:22 +02:00
//remove grail
removeArtifact ( ArtifactLocation ( t - > visitingHero , t - > visitingHero - > getArtPos ( 2 , false ) ) ) ;
2010-02-24 15:03:36 +02:00
}
2009-03-09 12:37:49 +02:00
NewStructures ns ;
ns . tid = tid ;
2010-01-01 20:54:31 +02:00
if ( ( bid = = 18 ) & & ( vstd : : contains ( t - > builtBuildings , ( t - > town - > hordeLvl [ 0 ] + 37 ) ) ) )
ns . bid . insert ( 19 ) ; //we have upgr. dwelling, upgr. horde will be builded as well
else if ( ( bid = = 24 ) & & ( vstd : : contains ( t - > builtBuildings , ( t - > town - > hordeLvl [ 1 ] + 37 ) ) ) )
ns . bid . insert ( 25 ) ;
else if ( bid > 36 ) //upg dwelling
{
if ( ( bid - 37 = = t - > town - > hordeLvl [ 0 ] ) & & ( vstd : : contains ( t - > builtBuildings , 18 ) ) )
ns . bid . insert ( 19 ) ; //we have horde, will be upgraded as well as dwelling
if ( ( bid - 37 = = t - > town - > hordeLvl [ 1 ] ) & & ( vstd : : contains ( t - > builtBuildings , 24 ) ) )
2009-03-09 12:37:49 +02:00
ns . bid . insert ( 25 ) ;
2009-07-11 02:40:10 +03:00
SetAvailableCreatures ssi ;
ssi . tid = tid ;
ssi . creatures = t - > creatures ;
ssi . creatures [ bid - 37 ] . second . push_back ( t - > town - > upgradedCreatures [ bid - 37 ] ) ;
2012-01-13 17:18:32 +03:00
//Test for 2nd upgrade - add sharpshooters if grand elves dwelling was constructed
//if (t->subID == 1 && bid == 39)
// ssi.creatures[bid-37].second.push_back(137);
2009-07-11 02:40:10 +03:00
sendAndApply ( & ssi ) ;
2009-03-09 12:37:49 +02:00
}
else if ( bid > = 30 ) //bas. dwelling
{
2009-07-11 02:40:10 +03:00
int crid = t - > town - > basicCreatures [ bid - 30 ] ;
2009-03-09 12:37:49 +02:00
SetAvailableCreatures ssi ;
ssi . tid = tid ;
2009-07-06 22:41:27 +03:00
ssi . creatures = t - > creatures ;
2010-05-02 21:20:26 +03:00
ssi . creatures [ bid - 30 ] . first = VLC - > creh - > creatures [ crid ] - > growth ;
2009-07-11 02:40:10 +03:00
ssi . creatures [ bid - 30 ] . second . push_back ( crid ) ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & ssi ) ;
2009-12-29 19:15:03 +02:00
}
else if ( bid = = 11 )
ns . bid . insert ( 27 ) ;
else if ( bid = = 12 )
ns . bid . insert ( 28 ) ;
else if ( bid = = 13 )
2009-12-29 15:40:16 +02:00
ns . bid . insert ( 29 ) ;
2010-06-07 08:28:12 +03:00
else if ( t - > subID = = 4 & & bid = = 17 ) //veil of darkness
{
2011-02-04 16:58:14 +02:00
//handled via town->reacreateBonuses in apply
// GiveBonus gb(GiveBonus::TOWN);
// gb.bonus.type = Bonus::DARKNESS;
// gb.bonus.val = 20;
// gb.id = t->id;
// gb.bonus.duration = Bonus::PERMANENT;
// gb.bonus.source = Bonus::TOWN_STRUCTURE;
// gb.bonus.id = 17;
// sendAndApply(&gb);
2010-06-07 08:28:12 +03:00
}
2010-07-10 19:50:23 +03:00
else if ( t - > subID = = 5 & & bid = = 22 )
{
setPortalDwelling ( t ) ;
}
2009-03-09 12:37:49 +02:00
ns . bid . insert ( bid ) ;
2010-08-25 17:57:58 +03:00
ns . builded = force ? t - > builded : ( t - > builded + 1 ) ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & ns ) ;
2009-11-11 19:55:07 +02:00
//reveal ground for lookout tower
2009-11-11 19:45:03 +02:00
FoWChange fw ;
2009-11-11 20:20:18 +02:00
fw . player = t - > tempOwner ;
fw . mode = 1 ;
getTilesInRange ( fw . tiles , t - > pos , t - > getSightRadious ( ) , t - > tempOwner , 1 ) ;
2009-11-11 19:45:03 +02:00
sendAndApply ( & fw ) ;
2009-03-09 12:37:49 +02:00
2010-08-18 17:24:30 +03:00
if ( ! force )
{
SetResources sr ;
sr . player = t - > tempOwner ;
2011-07-05 09:14:07 +03:00
sr . res = gs - > getPlayer ( t - > tempOwner ) - > resources - b - > resources ;
2010-08-18 17:24:30 +03:00
sendAndApply ( & sr ) ;
}
2009-03-09 12:37:49 +02:00
if ( bid < 5 ) //it's mage guild
{
if ( t - > visitingHero )
giveSpells ( t , t - > visitingHero ) ;
if ( t - > garrisonHero )
giveSpells ( t , t - > garrisonHero ) ;
}
2009-10-06 09:15:56 +03:00
if ( t - > visitingHero )
vistiCastleObjects ( t , t - > visitingHero ) ;
if ( t - > garrisonHero )
vistiCastleObjects ( t , t - > garrisonHero ) ;
2010-05-14 05:18:37 +03:00
checkLossVictory ( t - > tempOwner ) ;
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
2009-09-22 17:27:46 +03:00
bool CGameHandler : : razeStructure ( si32 tid , si32 bid )
{
///incomplete, simply erases target building
2010-12-19 00:11:28 +02:00
CGTownInstance * t = static_cast < CGTownInstance * > ( gs - > map - > objects [ tid ] . get ( ) ) ;
2009-09-22 17:27:46 +03:00
if ( t - > builtBuildings . find ( bid ) = = t - > builtBuildings . end ( ) )
return false ;
RazeStructures rs ;
rs . tid = tid ;
rs . bid . insert ( bid ) ;
2009-09-24 20:54:02 +03:00
rs . destroyed = t - > destroyed + 1 ;
2009-09-22 17:27:46 +03:00
sendAndApply ( & rs ) ;
2010-06-07 08:28:12 +03:00
//TODO: Remove dwellers
2011-02-04 16:58:14 +02:00
// if (t->subID == 4 && bid == 17) //Veil of Darkness
// {
// RemoveBonus rb(RemoveBonus::TOWN);
// rb.whoID = t->id;
// rb.source = Bonus::TOWN_STRUCTURE;
// rb.id = 17;
// sendAndApply(&rb);
// }
2009-09-22 17:27:46 +03:00
return true ;
}
2009-03-09 12:37:49 +02:00
void CGameHandler : : sendMessageToAll ( const std : : string & message )
{
SystemMessage sm ;
sm . text = message ;
sendToAllClients ( & sm ) ;
}
2010-07-10 19:50:23 +03:00
bool CGameHandler : : recruitCreatures ( si32 objid , ui32 crid , ui32 cram , si32 fromLvl )
2009-03-09 12:37:49 +02:00
{
2010-12-19 00:11:28 +02:00
const CGDwelling * dw = static_cast < CGDwelling * > ( gs - > map - > objects [ objid ] . get ( ) ) ;
2009-07-06 22:41:27 +03:00
const CArmedInstance * dst = NULL ;
2010-05-15 11:33:32 +03:00
const CCreature * c = VLC - > creh - > creatures [ crid ] ;
bool warMachine = c - > hasBonusOfType ( Bonus : : SIEGE_WEAPON ) ;
//TODO: test for owning
2009-07-06 22:41:27 +03:00
2011-12-14 00:23:17 +03:00
if ( dw - > ID = = GameConstants : : TOWNI_TYPE )
2011-02-25 19:57:30 +02:00
dst = ( static_cast < const CGTownInstance * > ( dw ) ) - > getUpperArmy ( ) ;
2010-07-24 00:05:49 +03:00
else if ( dw - > ID = = 17 | | dw - > ID = = 20 | | dw - > ID = = 78 ) //advmap dwelling
2009-07-06 22:41:27 +03:00
dst = getHero ( gs - > getPlayer ( dw - > tempOwner ) - > currentSelection ) ; //TODO: check if current hero is really visiting dwelling
2010-05-15 11:33:32 +03:00
else if ( dw - > ID = = 106 )
dst = dynamic_cast < const CGHeroInstance * > ( getTile ( dw - > visitablePos ( ) ) - > visitableObjects . back ( ) ) ;
2009-07-06 22:41:27 +03:00
assert ( dw & & dst ) ;
2009-03-09 12:37:49 +02:00
//verify
bool found = false ;
2010-07-10 19:50:23 +03:00
int level = 0 ;
2009-07-06 22:41:27 +03:00
2009-03-09 12:37:49 +02:00
typedef std : : pair < const int , int > Parka ;
2010-07-10 19:50:23 +03:00
for ( ; level < dw - > creatures . size ( ) ; level + + ) //iterate through all levels
2009-03-09 12:37:49 +02:00
{
2010-07-10 19:50:23 +03:00
if ( ( fromLvl ! = - 1 ) & & ( level ! = fromLvl ) )
continue ;
2009-07-06 22:41:27 +03:00
const std : : pair < ui32 , std : : vector < ui32 > > & cur = dw - > creatures [ level ] ; //current level info <amount, list of cr. ids>
int i = 0 ;
for ( ; i < cur . second . size ( ) ; i + + ) //look for crid among available creatures list on current level
if ( cur . second [ i ] = = crid )
break ;
if ( i < cur . second . size ( ) )
2009-03-09 12:37:49 +02:00
{
2009-07-06 22:41:27 +03:00
found = true ;
cram = std : : min ( cram , cur . first ) ; //reduce recruited amount up to available amount
2009-03-09 12:37:49 +02:00
break ;
}
}
2010-05-15 11:33:32 +03:00
int slot = dst - > getSlotFor ( crid ) ;
2009-03-09 12:37:49 +02:00
2011-07-05 14:31:26 +03:00
if ( ( ! found & & complain ( " Cannot recruit: no such creatures! " ) )
| | ( cram > VLC - > creh - > creatures [ crid ] - > maxAmount ( gs - > getPlayer ( dst - > tempOwner ) - > resources ) & & complain ( " Cannot recruit: lack of resources! " ) )
| | ( cram < = 0 & & complain ( " Cannot recruit: cram <= 0! " ) )
| | ( slot < 0 & & ! warMachine & & complain ( " Cannot recruit: no available slot! " ) ) )
2009-04-16 03:28:54 +03:00
{
return false ;
}
2009-03-09 12:37:49 +02:00
//recruit
SetResources sr ;
2009-07-06 22:41:27 +03:00
sr . player = dst - > tempOwner ;
2011-07-05 09:14:07 +03:00
sr . res = gs - > getPlayer ( dst - > tempOwner ) - > resources - ( c - > cost * cram ) ;
2009-03-09 12:37:49 +02:00
SetAvailableCreatures sac ;
sac . tid = objid ;
2009-07-06 22:41:27 +03:00
sac . creatures = dw - > creatures ;
sac . creatures [ level ] . first - = cram ;
2009-03-09 12:37:49 +02:00
2010-05-15 11:33:32 +03:00
sendAndApply ( & sr ) ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & sac ) ;
2010-05-15 11:33:32 +03:00
if ( warMachine )
{
2010-12-26 16:34:11 +02:00
const CGHeroInstance * h = dynamic_cast < const CGHeroInstance * > ( dst ) ;
if ( ! h )
COMPLAIN_RET ( " Only hero can buy war machines " ) ;
2010-05-15 11:33:32 +03:00
switch ( crid )
{
case 146 :
2011-12-14 00:23:17 +03:00
giveHeroNewArtifact ( h , VLC - > arth - > artifacts [ 4 ] , ArtifactPosition : : MACH1 ) ;
2010-05-15 11:33:32 +03:00
break ;
case 147 :
2011-12-14 00:23:17 +03:00
giveHeroNewArtifact ( h , VLC - > arth - > artifacts [ 6 ] , ArtifactPosition : : MACH3 ) ;
2010-05-15 11:33:32 +03:00
break ;
case 148 :
2011-12-14 00:23:17 +03:00
giveHeroNewArtifact ( h , VLC - > arth - > artifacts [ 5 ] , ArtifactPosition : : MACH2 ) ;
2010-05-15 11:33:32 +03:00
break ;
default :
complain ( " This war machine cannot be recruited! " ) ;
return false ;
}
}
else
{
2010-11-27 22:17:28 +02:00
addToSlot ( StackLocation ( dst , slot ) , c , cram ) ;
2010-05-15 11:33:32 +03:00
}
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
2009-04-16 03:28:54 +03:00
bool CGameHandler : : upgradeCreature ( ui32 objid , ui8 pos , ui32 upgID )
2009-03-09 12:37:49 +02:00
{
2010-12-19 00:11:28 +02:00
CArmedInstance * obj = static_cast < CArmedInstance * > ( gs - > map - > objects [ objid ] . get ( ) ) ;
2010-11-27 22:17:28 +02:00
assert ( obj - > hasStackAtSlot ( pos ) ) ;
2010-07-15 06:04:57 +03:00
UpgradeInfo ui = gs - > getUpgradeInfo ( obj - > getStack ( pos ) ) ;
2009-03-09 12:37:49 +02:00
int player = obj - > tempOwner ;
2011-07-05 09:14:07 +03:00
const PlayerState * p = getPlayer ( player ) ;
2011-01-21 04:36:30 +02:00
int crQuantity = obj - > stacks [ pos ] - > count ;
2011-12-14 00:23:17 +03:00
int newIDpos = vstd : : find_pos ( ui . newID , upgID ) ; //get position of new id in UpgradeInfo
2011-07-05 09:14:07 +03:00
TResources totalCost = ui . cost [ newIDpos ] * crQuantity ;
2009-03-09 12:37:49 +02:00
//check if upgrade is possible
2010-07-27 02:20:21 +03:00
if ( ( ui . oldID < 0 | | newIDpos = = - 1 ) & & complain ( " That upgrade is not possible! " ) )
2009-04-16 03:28:54 +03:00
{
return false ;
}
2010-07-27 02:20:21 +03:00
2009-03-09 12:37:49 +02:00
//check if player has enough resources
2011-07-05 09:14:07 +03:00
if ( ! p - > resources . canAfford ( totalCost ) )
COMPLAIN_RET ( " Cannot upgrade, not enough resources! " ) ;
2010-07-27 02:20:21 +03:00
2009-03-09 12:37:49 +02:00
//take resources
2011-07-05 09:14:07 +03:00
SetResources sr ;
sr . player = player ;
sr . res = p - > resources - totalCost ;
sendAndApply ( & sr ) ;
2010-07-27 02:20:21 +03:00
2009-03-09 12:37:49 +02:00
//upgrade creature
2010-11-27 22:17:28 +02:00
changeStackType ( StackLocation ( obj , pos ) , VLC - > creh - > creatures [ upgID ] ) ;
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
2010-11-27 22:17:28 +02:00
bool CGameHandler : : changeStackType ( const StackLocation & sl , CCreature * c )
2010-08-17 18:50:17 +03:00
{
2010-11-27 22:17:28 +02:00
if ( ! sl . army - > hasStackAtSlot ( sl . slot ) )
COMPLAIN_RET ( " Cannot find a stack to change type " ) ;
2010-11-27 03:46:19 +02:00
SetStackType sst ;
sst . sl = sl ;
sst . type = c ;
sendAndApply ( & sst ) ;
2010-11-27 22:17:28 +02:00
return true ;
2010-08-17 18:50:17 +03:00
}
2010-12-13 01:44:16 +02:00
void CGameHandler : : moveArmy ( const CArmedInstance * src , const CArmedInstance * dst , bool allowMerging )
{
assert ( src - > canBeMergedWith ( * dst , allowMerging ) ) ;
2011-02-04 16:58:14 +02:00
while ( src - > stacksCount ( ) ) //while there are unmoved creatures
2010-12-13 01:44:16 +02:00
{
TSlots : : const_iterator i = src - > Slots ( ) . begin ( ) ; //iterator to stack to move
StackLocation sl ( src , i - > first ) ; //location of stack to move
TSlot pos = dst - > getSlotFor ( i - > second - > type ) ;
if ( pos < 0 )
{
//try to merge two other stacks to make place
std : : pair < TSlot , TSlot > toMerge ;
if ( dst - > mergableStacks ( toMerge , i - > first ) & & allowMerging )
{
moveStack ( StackLocation ( dst , toMerge . first ) , StackLocation ( dst , toMerge . second ) ) ; //merge toMerge.first into toMerge.second
assert ( ! dst - > hasStackAtSlot ( toMerge . first ) ) ; //we have now a new free slot
moveStack ( sl , StackLocation ( dst , toMerge . first ) ) ; //move stack to freed slot
}
else
{
complain ( " Unexpected failure during an attempt to move army from " + src - > nodeName ( ) + " to " + dst - > nodeName ( ) + " ! " ) ;
return ;
}
}
else
{
moveStack ( sl , StackLocation ( dst , pos ) ) ;
}
}
}
2009-04-16 03:28:54 +03:00
bool CGameHandler : : garrisonSwap ( si32 tid )
2009-03-09 12:37:49 +02:00
{
CGTownInstance * town = gs - > getTown ( tid ) ;
2009-12-19 14:31:57 +02:00
if ( ! town - > garrisonHero & & town - > visitingHero ) //visiting => garrison, merge armies: town army => hero army
2009-03-09 12:37:49 +02:00
{
2010-11-27 22:17:28 +02:00
if ( ! town - > visitingHero - > canBeMergedWith ( * town ) )
{
complain ( " Cannot make garrison swap, not enough free slots! " ) ;
return false ;
}
2010-12-13 01:44:16 +02:00
moveArmy ( town , town - > visitingHero , true ) ;
2010-11-27 22:17:28 +02:00
2009-03-09 12:37:49 +02:00
SetHeroesInTown intown ;
intown . tid = tid ;
intown . visiting = - 1 ;
intown . garrison = town - > visitingHero - > id ;
sendAndApply ( & intown ) ;
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
else if ( town - > garrisonHero & & ! town - > visitingHero ) //move hero out of the garrison
{
2009-04-09 18:05:20 +03:00
//check if moving hero out of town will break 8 wandering heroes limit
2009-06-24 06:14:46 +03:00
if ( getHeroCount ( town - > garrisonHero - > tempOwner , false ) > = 8 )
2009-04-09 18:05:20 +03:00
{
complain ( " Cannot move hero out of the garrison, there are already 8 wandering heroes! " ) ;
2009-04-16 03:28:54 +03:00
return false ;
2009-04-09 18:05:20 +03:00
}
2009-03-09 12:37:49 +02:00
SetHeroesInTown intown ;
intown . tid = tid ;
intown . garrison = - 1 ;
intown . visiting = town - > garrisonHero - > id ;
sendAndApply ( & intown ) ;
2010-12-12 01:11:26 +02:00
return true ;
2009-03-09 12:37:49 +02:00
}
2011-02-04 16:58:14 +02:00
else if ( ! ! town - > garrisonHero & & town - > visitingHero ) //swap visiting and garrison hero
2009-03-09 12:37:49 +02:00
{
SetHeroesInTown intown ;
intown . tid = tid ;
intown . garrison = town - > visitingHero - > id ;
intown . visiting = town - > garrisonHero - > id ;
sendAndApply ( & intown ) ;
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
else
{
2009-03-27 01:05:40 +02:00
complain ( " Cannot swap garrison hero! " ) ;
2009-04-16 03:28:54 +03:00
return false ;
2009-03-09 12:37:49 +02:00
}
}
2010-02-08 23:17:22 +02:00
// With the amount of changes done to the function, it's more like transferArtifacts.
2010-12-29 23:04:22 +02:00
// Function moves artifact from src to dst. If dst is not a backpack and is already occupied, old dst art goes to backpack and is replaced.
bool CGameHandler : : moveArtifact ( si32 srcHeroID , si32 destHeroID , ui16 srcSlot , ui16 destSlot )
2009-03-09 12:37:49 +02:00
{
2011-05-03 06:14:18 +03:00
const CGHeroInstance * srcHero = getHero ( srcHeroID ) ;
const CGHeroInstance * destHero = getHero ( destHeroID ) ;
2010-12-29 23:04:22 +02:00
ArtifactLocation src ( srcHero , srcSlot ) , dst ( destHero , destSlot ) ;
2009-11-10 05:10:14 +02:00
// Make sure exchange is even possible between the two heroes.
2010-12-29 23:04:22 +02:00
if ( ! isAllowedExchange ( srcHeroID , destHeroID ) )
COMPLAIN_RET ( " That heroes cannot make any exchange! " ) ;
2009-04-16 03:28:54 +03:00
2010-12-29 23:04:22 +02:00
const CArtifactInstance * srcArtifact = src . getArt ( ) ;
const CArtifactInstance * destArtifact = dst . getArt ( ) ;
2009-04-04 01:34:31 +03:00
2010-07-20 21:34:32 +03:00
if ( srcArtifact = = NULL )
2010-12-29 23:04:22 +02:00
COMPLAIN_RET ( " No artifact to move! " ) ;
2010-08-12 18:54:25 +03:00
if ( destArtifact & & srcHero - > tempOwner ! = destHero - > tempOwner )
2010-12-29 23:04:22 +02:00
COMPLAIN_RET ( " Can't touch artifact on hero of another player! " ) ;
2011-03-05 18:38:22 +02:00
2009-11-10 05:10:14 +02:00
// Check if src/dest slots are appropriate for the artifacts exchanged.
// Moving to the backpack is always allowed.
2011-12-14 00:23:17 +03:00
if ( ( ! srcArtifact | | destSlot < GameConstants : : BACKPACK_START )
2011-01-06 22:00:19 +02:00
& & srcArtifact & & ! srcArtifact - > canBePutAt ( dst , true ) )
COMPLAIN_RET ( " Cannot move artifact! " ) ;
2009-11-10 05:10:14 +02:00
2011-12-14 00:23:17 +03:00
if ( ( srcArtifact & & srcArtifact - > artType - > id = = GameConstants : : ID_LOCK ) | | ( destArtifact & & destArtifact - > artType - > id = = GameConstants : : ID_LOCK ) )
2010-12-29 23:04:22 +02:00
COMPLAIN_RET ( " Cannot move artifact locks. " ) ;
2010-02-08 23:17:22 +02:00
2011-12-14 00:23:17 +03:00
if ( destSlot > = GameConstants : : BACKPACK_START & & srcArtifact - > artType - > isBig ( ) )
2010-12-29 23:04:22 +02:00
COMPLAIN_RET ( " Cannot put big artifacts in backpack! " ) ;
2011-12-14 00:23:17 +03:00
if ( srcSlot = = ArtifactPosition : : MACH4 | | destSlot = = ArtifactPosition : : MACH4 )
2010-12-29 23:04:22 +02:00
COMPLAIN_RET ( " Cannot move catapult! " ) ;
2009-12-30 17:33:28 +02:00
2011-12-14 00:23:17 +03:00
if ( dst . slot > = GameConstants : : BACKPACK_START )
vstd : : amin ( dst . slot , GameConstants : : BACKPACK_START + dst . hero - > artifactsInBackpack . size ( ) ) ;
2011-01-06 22:00:19 +02:00
2011-02-27 21:58:14 +02:00
if ( src . slot = = dst . slot & & src . hero = = dst . hero )
2011-01-06 22:00:19 +02:00
COMPLAIN_RET ( " Won't move artifact: Dest same as source! " ) ;
2010-12-29 23:04:22 +02:00
//moving art to backpack is always allowed (we've ruled out exceptions)
2011-12-14 00:23:17 +03:00
if ( destSlot > = GameConstants : : BACKPACK_START )
2010-07-20 21:34:32 +03:00
{
2010-12-29 23:04:22 +02:00
moveArtifact ( src , dst ) ;
2009-04-04 01:34:31 +03:00
}
2010-12-29 23:04:22 +02:00
else //moving art to another slot
2010-07-20 21:34:32 +03:00
{
2010-12-29 23:04:22 +02:00
if ( destArtifact ) //old artifact must be removed first
{
2011-12-14 00:23:17 +03:00
moveArtifact ( dst , ArtifactLocation ( destHero , destHero - > artifactsInBackpack . size ( ) + GameConstants : : BACKPACK_START ) ) ;
2010-12-29 23:04:22 +02:00
}
moveArtifact ( src , dst ) ;
}
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
2012-01-30 19:07:52 +03:00
bool CGameHandler : : moveArtifact ( StackLocation s1 , StackLocation s2 , ui16 srcSlot , ui16 destSlot )
{
ArtifactLocation src ( s1 . getStack ( ) , srcSlot ) , dst ( s2 . getStack ( ) , destSlot ) ;
moveArtifact ( src , dst ) ;
return true ;
}
bool CGameHandler : : moveArtifact ( si32 srcHeroID , StackLocation s2 , ui16 srcSlot , ui16 destSlot )
{
ArtifactLocation src ( getHero ( srcHeroID ) , srcSlot ) ;
ArtifactLocation dst ( s2 . getStack ( ) , destSlot ) ;
moveArtifact ( src , dst ) ;
return true ;
}
bool CGameHandler : : moveArtifact ( StackLocation s1 , si32 destHeroID , ui16 srcSlot , ui16 destSlot )
{
ArtifactLocation src ( s1 . getStack ( ) , srcSlot ) ;
ArtifactLocation dst ( getHero ( destHeroID ) , destSlot ) ;
//hero should not wear stack artifact
vstd : : amin ( dst . slot , GameConstants : : BACKPACK_START + dst . hero - > artifactsInBackpack . size ( ) ) ; //put on first free position
moveArtifact ( src , dst ) ;
return true ;
}
void CGameHandler : : moveArtifact ( const ArtifactLocation & al1 , const ArtifactLocation & al2 )
{
MoveArtifact ma ;
ma . src = al1 ;
ma . dst = al2 ;
sendAndApply ( & ma ) ;
}
2010-02-16 16:39:56 +02:00
/**
* Assembles or disassembles a combination artifact .
* @ param heroID ID of hero holding the artifact ( s ) .
* @ param artifactSlot The worn slot ID of the combination - or constituent artifact .
* @ param assemble True for assembly operation , false for disassembly .
* @ param assembleTo If assemble is true , this represents the artifact ID of the combination
* artifact to assemble to . Otherwise it ' s not used .
*/
bool CGameHandler : : assembleArtifacts ( si32 heroID , ui16 artifactSlot , bool assemble , ui32 assembleTo )
{
CGHeroInstance * hero = gs - > getHero ( heroID ) ;
2010-12-29 23:04:22 +02:00
const CArtifactInstance * destArtifact = hero - > getArt ( artifactSlot ) ;
2011-01-22 05:43:20 +02:00
if ( ! destArtifact )
COMPLAIN_RET ( " assembleArtifacts: there is no such artifact instance! " ) ;
2011-01-24 01:49:17 +02:00
if ( assemble )
{
CArtifact * combinedArt = VLC - > arth - > artifacts [ assembleTo ] ;
if ( ! combinedArt - > constituents )
COMPLAIN_RET ( " assembleArtifacts: Artifact being attempted to assemble is not a combined artifacts! " ) ;
if ( ! vstd : : contains ( destArtifact - > assemblyPossibilities ( hero ) , combinedArt ) )
COMPLAIN_RET ( " assembleArtifacts: It's impossible to assemble requested artifact! " ) ;
2011-01-22 05:43:20 +02:00
2011-01-24 01:49:17 +02:00
AssembledArtifact aa ;
aa . al = ArtifactLocation ( hero , artifactSlot ) ;
aa . builtArt = combinedArt ;
sendAndApply ( & aa ) ;
}
else
{
if ( ! destArtifact - > artType - > constituents )
COMPLAIN_RET ( " assembleArtifacts: Artifact being attempted to disassemble is not a combined artifact! " ) ;
2011-01-22 05:43:20 +02:00
2011-01-24 01:49:17 +02:00
DisassembledArtifact da ;
da . al = ArtifactLocation ( hero , artifactSlot ) ;
sendAndApply ( & da ) ;
}
2010-02-16 16:39:56 +02:00
2010-12-29 23:04:22 +02:00
return false ;
2010-02-16 16:39:56 +02:00
}
2009-04-16 03:28:54 +03:00
bool CGameHandler : : buyArtifact ( ui32 hid , si32 aid )
2009-03-09 12:37:49 +02:00
{
CGHeroInstance * hero = gs - > getHero ( hid ) ;
2011-02-22 11:47:25 +02:00
CGTownInstance * town = hero - > visitedTown ;
2009-03-09 12:37:49 +02:00
if ( aid = = 0 ) //spellbook
{
2011-12-14 00:23:17 +03:00
if ( ( ! vstd : : contains ( town - > builtBuildings , si32 ( EBuilding : : MAGES_GUILD_1 ) ) & & complain ( " Cannot buy a spellbook, no mage guild in the town! " ) )
| | ( getResource ( hero - > getOwner ( ) , Res : : GOLD ) < GameConstants : : SPELLBOOK_GOLD_COST & & complain ( " Cannot buy a spellbook, not enough gold! " ) )
| | ( hero - > getArt ( ArtifactPosition : : SPELLBOOK ) & & complain ( " Cannot buy a spellbook, hero already has a one! " ) )
2011-07-05 14:31:26 +03:00
)
2009-04-16 03:28:54 +03:00
return false ;
2009-03-09 12:37:49 +02:00
2011-12-14 00:23:17 +03:00
giveResource ( hero - > getOwner ( ) , Res : : GOLD , - GameConstants : : SPELLBOOK_GOLD_COST ) ;
giveHeroNewArtifact ( hero , VLC - > arth - > artifacts [ 0 ] , ArtifactPosition : : SPELLBOOK ) ;
assert ( hero - > getArt ( ArtifactPosition : : SPELLBOOK ) ) ;
2009-03-09 12:37:49 +02:00
giveSpells ( town , hero ) ;
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
else if ( aid < 7 & & aid > 3 ) //war machine
{
2010-06-26 19:02:10 +03:00
int price = VLC - > arth - > artifacts [ aid ] - > price ;
2011-07-05 14:31:26 +03:00
if ( ( hero - > getArt ( 9 + aid ) & & complain ( " Hero already has this machine! " ) )
2011-12-14 00:23:17 +03:00
| | ( ! vstd : : contains ( town - > builtBuildings , si32 ( EBuilding : : BLACKSMITH ) ) & & complain ( " No blackismith! " ) )
2011-07-05 14:31:26 +03:00
| | ( gs - > getPlayer ( hero - > getOwner ( ) ) - > resources [ Res : : GOLD ] < price & & complain ( " Not enough gold! " ) ) //no gold
2011-12-14 00:23:17 +03:00
| | ( ( ! ( town - > subID = = 6 & & vstd : : contains ( town - > builtBuildings , si32 ( EBuilding : : SPECIAL_3 ) ) )
2011-07-05 14:31:26 +03:00
& & town - > town - > warMachine ! = aid ) & & complain ( " This machine is unavailable here! " ) ) )
2009-03-09 12:37:49 +02:00
{
2009-04-16 03:28:54 +03:00
return false ;
2009-03-09 12:37:49 +02:00
}
2009-04-04 01:34:31 +03:00
2010-12-29 23:04:22 +02:00
giveResource ( hero - > getOwner ( ) , Res : : GOLD , - price ) ;
2010-12-26 16:34:11 +02:00
giveHeroNewArtifact ( hero , VLC - > arth - > artifacts [ aid ] , 9 + aid ) ;
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
2009-04-16 03:28:54 +03:00
return false ;
2009-03-09 12:37:49 +02:00
}
2010-06-27 19:03:01 +03:00
bool CGameHandler : : buyArtifact ( const IMarket * m , const CGHeroInstance * h , int rid , int aid )
{
2011-12-14 00:23:17 +03:00
if ( ! vstd : : contains ( m - > availableItemsIds ( EMarketMode : : RESOURCE_ARTIFACT ) , aid ) )
2010-06-27 19:03:01 +03:00
COMPLAIN_RET ( " That artifact is unavailable! " ) ;
int b1 , b2 ;
2011-12-14 00:23:17 +03:00
m - > getOffer ( rid , aid , b1 , b2 , EMarketMode : : RESOURCE_ARTIFACT ) ;
2010-06-27 19:03:01 +03:00
if ( getResource ( h - > tempOwner , rid ) < b1 )
COMPLAIN_RET ( " You can't afford to buy this artifact! " ) ;
SetResource sr ;
sr . player = h - > tempOwner ;
sr . resid = rid ;
sr . val = getResource ( h - > tempOwner , rid ) - b1 ;
sendAndApply ( & sr ) ;
SetAvailableArtifacts saa ;
2011-12-14 00:23:17 +03:00
if ( m - > o - > ID = = GameConstants : : TOWNI_TYPE )
2010-06-27 19:03:01 +03:00
{
saa . id = - 1 ;
saa . arts = CGTownInstance : : merchantArtifacts ;
}
else if ( const CGBlackMarket * bm = dynamic_cast < const CGBlackMarket * > ( m - > o ) ) //black market
{
saa . id = bm - > id ;
saa . arts = bm - > artifacts ;
}
else
COMPLAIN_RET ( " Wrong marktet... " ) ;
bool found = false ;
BOOST_FOREACH ( const CArtifact * & art , saa . arts )
{
if ( art & & art - > id = = aid )
{
art = NULL ;
found = true ;
break ;
}
}
if ( ! found )
COMPLAIN_RET ( " Cannot find selected artifact on the list " ) ;
sendAndApply ( & saa ) ;
2010-12-26 16:34:11 +02:00
giveHeroNewArtifact ( h , VLC - > arth - > artifacts [ aid ] , - 2 ) ;
2010-08-02 14:06:49 +03:00
return true ;
2010-06-27 19:03:01 +03:00
}
2010-05-18 10:01:54 +03:00
2011-04-23 00:51:10 +03:00
bool CGameHandler : : sellArtifact ( const IMarket * m , const CGHeroInstance * h , int aid , int rid )
{
const CArtifactInstance * art = h - > getArtByInstanceId ( aid ) ;
if ( ! art )
COMPLAIN_RET ( " There is no artifact to sell! " ) ;
if ( art - > artType - > id < 7 )
COMPLAIN_RET ( " Cannot sell a war machine or spellbook! " ) ;
int resVal = 0 , dump = 1 ;
2011-12-14 00:23:17 +03:00
m - > getOffer ( art - > artType - > id , rid , dump , resVal , EMarketMode : : ARTIFACT_RESOURCE ) ;
2011-04-23 00:51:10 +03:00
removeArtifact ( ArtifactLocation ( h , h - > getArtPos ( art ) ) ) ;
giveResource ( h - > tempOwner , rid , resVal ) ;
return true ;
}
2010-07-20 17:08:13 +03:00
bool CGameHandler : : buySecSkill ( const IMarket * m , const CGHeroInstance * h , int skill )
{
if ( ! h )
COMPLAIN_RET ( " You need hero to buy a skill! " ) ;
2010-12-23 22:18:10 +02:00
if ( h - > getSecSkillLevel ( static_cast < CGHeroInstance : : SecondarySkill > ( skill ) ) )
2010-07-20 17:08:13 +03:00
COMPLAIN_RET ( " Hero already know this skill " ) ;
2011-12-14 00:23:17 +03:00
if ( h - > secSkills . size ( ) > = GameConstants : : SKILL_PER_HERO ) //can't learn more skills
2010-07-20 17:08:13 +03:00
COMPLAIN_RET ( " Hero can't learn any more skills " ) ;
2010-07-22 03:32:45 +03:00
if ( h - > type - > heroClass - > proSec [ skill ] = = 0 ) //can't learn this skill (like necromancy for most of non-necros)
COMPLAIN_RET ( " The hero can't learn this skill! " ) ;
2010-07-20 17:08:13 +03:00
2011-12-14 00:23:17 +03:00
if ( ! vstd : : contains ( m - > availableItemsIds ( EMarketMode : : RESOURCE_SKILL ) , skill ) )
2010-07-20 17:08:13 +03:00
COMPLAIN_RET ( " That skill is unavailable! " ) ;
2011-07-05 14:31:26 +03:00
if ( getResource ( h - > tempOwner , Res : : GOLD ) < 2000 ) //TODO: remove hardcoded resource\summ?
2010-07-20 17:08:13 +03:00
COMPLAIN_RET ( " You can't afford to buy this skill " ) ;
SetResource sr ;
sr . player = h - > tempOwner ;
2011-07-05 14:31:26 +03:00
sr . resid = Res : : GOLD ;
sr . val = getResource ( h - > tempOwner , Res : : GOLD ) - 2000 ;
2010-07-20 17:08:13 +03:00
sendAndApply ( & sr ) ;
2010-08-02 14:06:49 +03:00
changeSecSkill ( h - > id , skill , 1 , true ) ;
return true ;
2010-07-20 17:08:13 +03:00
}
2010-05-18 10:01:54 +03:00
bool CGameHandler : : tradeResources ( const IMarket * market , ui32 val , ui8 player , ui32 id1 , ui32 id2 )
2009-03-09 12:37:49 +02:00
{
2010-05-18 10:01:54 +03:00
int r1 = gs - > getPlayer ( player ) - > resources [ id1 ] ,
r2 = gs - > getPlayer ( player ) - > resources [ id2 ] ;
2011-12-14 00:23:17 +03:00
vstd : : amin ( val , r1 ) ; //can't trade more resources than have
2010-05-18 10:01:54 +03:00
int b1 , b2 ; //base quantities for trade
2011-12-14 00:23:17 +03:00
market - > getOffer ( id1 , id2 , b1 , b2 , EMarketMode : : RESOURCE_RESOURCE ) ;
2010-05-18 10:01:54 +03:00
int units = val / b1 ; //how many base quantities we trade
if ( val % b1 ) //all offered units of resource should be used, if not -> somewhere in calculations must be an error
{
//TODO: complain?
assert ( 0 ) ;
}
2009-03-09 12:37:49 +02:00
SetResource sr ;
sr . player = player ;
sr . resid = id1 ;
2010-05-18 10:01:54 +03:00
sr . val = r1 - b1 * units ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & sr ) ;
sr . resid = id2 ;
2010-05-18 10:01:54 +03:00
sr . val = r2 + b2 * units ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & sr ) ;
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
2010-05-26 12:47:53 +03:00
bool CGameHandler : : sellCreatures ( ui32 count , const IMarket * market , const CGHeroInstance * hero , ui32 slot , ui32 resourceID )
{
if ( ! vstd : : contains ( hero - > Slots ( ) , slot ) )
COMPLAIN_RET ( " Hero doesn't have any creature in that slot! " ) ;
const CStackInstance & s = hero - > getStack ( slot ) ;
2011-07-05 14:31:26 +03:00
if ( s . count < count //can't sell more creatures than have
| | ( hero - > Slots ( ) . size ( ) = = 1 & & hero - > needsLastStack ( ) & & s . count = = count ) ) //can't sell last stack
2010-05-26 12:47:53 +03:00
{
COMPLAIN_RET ( " Not enough creatures in army! " ) ;
}
int b1 , b2 ; //base quantities for trade
2011-12-14 00:23:17 +03:00
market - > getOffer ( s . type - > idNumber , resourceID , b1 , b2 , EMarketMode : : CREATURE_RESOURCE ) ;
2010-05-26 12:47:53 +03:00
int units = count / b1 ; //how many base quantities we trade
if ( count % b1 ) //all offered units of resource should be used, if not -> somewhere in calculations must be an error
{
//TODO: complain?
assert ( 0 ) ;
}
2010-11-27 22:17:28 +02:00
changeStackCount ( StackLocation ( hero , slot ) , - count ) ;
2010-05-26 12:47:53 +03:00
SetResource sr ;
sr . player = hero - > tempOwner ;
sr . resid = resourceID ;
sr . val = getResource ( hero - > tempOwner , resourceID ) + b2 * units ;
sendAndApply ( & sr ) ;
return true ;
}
2010-05-18 10:01:54 +03:00
2010-07-03 15:00:53 +03:00
bool CGameHandler : : transformInUndead ( const IMarket * market , const CGHeroInstance * hero , ui32 slot )
{
const CArmedInstance * army = NULL ;
if ( hero )
army = hero ;
else
army = dynamic_cast < const CGTownInstance * > ( market - > o ) ;
2010-11-27 22:17:28 +02:00
2010-07-03 15:00:53 +03:00
if ( ! army )
COMPLAIN_RET ( " Incorrect call to transform in undead! " ) ;
2010-11-27 22:17:28 +02:00
if ( ! army - > hasStackAtSlot ( slot ) )
2010-07-03 15:00:53 +03:00
COMPLAIN_RET ( " Army doesn't have any creature in that slot! " ) ;
2010-11-27 22:17:28 +02:00
2010-07-03 15:00:53 +03:00
const CStackInstance & s = army - > getStack ( slot ) ;
int resCreature ; //resulting creature - bone dragons or skeletons
2010-08-06 16:14:10 +03:00
if ( s . hasBonusOfType ( Bonus : : DRAGON_NATURE ) )
2010-07-03 15:00:53 +03:00
resCreature = 68 ;
else
resCreature = 56 ;
2010-11-27 22:17:28 +02:00
changeStackType ( StackLocation ( army , slot ) , VLC - > creh - > creatures [ resCreature ] ) ;
2010-07-03 15:00:53 +03:00
return true ;
}
2010-05-08 01:10:32 +03:00
bool CGameHandler : : sendResources ( ui32 val , ui8 player , ui32 r1 , ui32 r2 )
{
const PlayerState * p2 = gs - > getPlayer ( r2 , false ) ;
if ( ! p2 | | p2 - > status ! = PlayerState : : INGAME )
{
complain ( " Dest player must be in game! " ) ;
return false ;
}
si32 curRes1 = gs - > getPlayer ( player ) - > resources [ r1 ] , curRes2 = gs - > getPlayer ( r2 ) - > resources [ r1 ] ;
val = std : : min ( si32 ( val ) , curRes1 ) ;
SetResource sr ;
sr . player = player ;
sr . resid = r1 ;
sr . val = curRes1 - val ;
sendAndApply ( & sr ) ;
sr . player = r2 ;
sr . val = curRes2 + val ;
sendAndApply ( & sr ) ;
return true ;
}
2009-04-16 03:28:54 +03:00
bool CGameHandler : : setFormation ( si32 hid , ui8 formation )
2009-03-09 12:37:49 +02:00
{
2010-05-02 21:20:26 +03:00
gs - > getHero ( hid ) - > formation = formation ;
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
2010-07-09 02:03:27 +03:00
bool CGameHandler : : hireHero ( const CGObjectInstance * obj , ui8 hid , ui8 player )
2009-03-09 12:37:49 +02:00
{
2010-07-09 02:03:27 +03:00
const PlayerState * p = gs - > getPlayer ( player ) ;
const CGTownInstance * t = gs - > getTown ( obj - > id ) ;
2010-07-08 08:52:11 +03:00
2011-07-05 09:14:07 +03:00
//common preconditions
2011-07-05 14:31:26 +03:00
if ( ( p - > resources [ Res : : GOLD ] < 2500 & & complain ( " Not enough gold for buying hero! " ) )
| | ( getHeroCount ( player , false ) > = 8 & & complain ( " Cannot hire hero, only 8 wandering heroes are allowed! " ) ) )
2009-04-16 03:28:54 +03:00
return false ;
2010-07-09 02:03:27 +03:00
if ( t ) //tavern in town
{
2011-12-14 00:23:17 +03:00
if ( ( ! vstd : : contains ( t - > builtBuildings , EBuilding : : TAVERN ) & & complain ( " No tavern! " ) )
2011-07-05 14:31:26 +03:00
| | ( t - > visitingHero & & complain ( " There is visiting hero - no place! " ) ) )
2010-07-09 02:03:27 +03:00
return false ;
}
else if ( obj - > ID = = 95 ) //Tavern on adv map
{
if ( getTile ( obj - > visitablePos ( ) ) - > visitableObjects . back ( ) ! = obj & & complain ( " Tavern entry must be unoccupied! " ) )
return false ;
}
2010-12-20 15:04:24 +02:00
const CGHeroInstance * nh = p - > availableHeroes [ hid ] ;
2009-08-05 03:05:37 +03:00
assert ( nh ) ;
2009-03-09 12:37:49 +02:00
HeroRecruited hr ;
2010-07-09 02:03:27 +03:00
hr . tid = obj - > id ;
2009-03-09 12:37:49 +02:00
hr . hid = nh - > subID ;
2010-07-09 02:03:27 +03:00
hr . player = player ;
hr . tile = obj - > visitablePos ( ) + nh - > getVisitableOffset ( ) ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & hr ) ;
2009-12-01 23:05:57 +02:00
2010-12-20 15:04:24 +02:00
bmap < ui32 , ConstTransitivePtr < CGHeroInstance > > pool = gs - > unusedHeroesFromPool ( ) ;
2010-07-08 08:52:11 +03:00
const CGHeroInstance * theOtherHero = p - > availableHeroes [ ! hid ] ;
2010-07-09 02:03:27 +03:00
const CGHeroInstance * newHero = gs - > hpool . pickHeroFor ( false , player , getNativeTown ( player ) , pool , theOtherHero - > type - > heroClass ) ;
2009-08-05 03:05:37 +03:00
2009-03-09 12:37:49 +02:00
SetAvailableHeroes sah ;
2010-07-09 02:03:27 +03:00
sah . player = player ;
2010-07-08 08:52:11 +03:00
if ( newHero )
{
sah . hid [ hid ] = newHero - > subID ;
2011-01-28 04:11:58 +02:00
sah . army [ hid ] . clear ( ) ;
sah . army [ hid ] . setCreature ( 0 , VLC - > creh - > nameToID [ newHero - > type - > refTypeStack [ 0 ] ] , 1 ) ;
2010-07-08 08:52:11 +03:00
}
else
sah . hid [ hid ] = - 1 ;
sah . hid [ ! hid ] = theOtherHero ? theOtherHero - > subID : - 1 ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & sah ) ;
SetResource sr ;
2010-07-09 02:03:27 +03:00
sr . player = player ;
2011-07-05 09:14:07 +03:00
sr . resid = Res : : GOLD ;
sr . val = p - > resources [ Res : : GOLD ] - 2500 ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & sr ) ;
2009-03-20 20:51:48 +02:00
2010-07-09 02:03:27 +03:00
if ( t )
{
vistiCastleObjects ( t , nh ) ;
giveSpells ( t , nh ) ;
}
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
2011-09-06 09:00:32 +03:00
bool CGameHandler : : queryReply ( ui32 qid , ui32 answer , ui8 player )
2009-03-09 12:37:49 +02:00
{
2009-04-12 04:48:50 +03:00
boost : : unique_lock < boost : : recursive_mutex > lock ( gsm ) ;
2011-09-06 09:00:32 +03:00
states . removeQuery ( player , qid ) ;
2009-04-12 03:58:41 +03:00
if ( vstd : : contains ( callbacks , qid ) )
{
CFunctionList < void ( ui32 ) > callb = callbacks [ qid ] ;
callbacks . erase ( qid ) ;
if ( callb )
callb ( answer ) ;
}
else if ( vstd : : contains ( garrisonCallbacks , qid ) )
{
if ( garrisonCallbacks [ qid ] )
garrisonCallbacks [ qid ] ( ) ;
garrisonCallbacks . erase ( qid ) ;
allowedExchanges . erase ( qid ) ;
}
else
{
tlog1 < < " Unknown query reply... \n " ;
2009-04-16 03:28:54 +03:00
return false ;
2009-04-12 03:58:41 +03:00
}
2009-04-16 03:28:54 +03:00
return true ;
2009-03-09 12:37:49 +02:00
}
2011-06-11 07:54:41 +03:00
static EndAction end_action ;
2009-04-16 03:28:54 +03:00
bool CGameHandler : : makeBattleAction ( BattleAction & ba )
2009-03-09 12:37:49 +02:00
{
2009-11-28 03:42:08 +02:00
tlog1 < < " \t Making action of type " < < ba . actionType < < std : : endl ;
2009-04-16 03:28:54 +03:00
bool ok = true ;
2011-02-12 18:12:48 +02:00
2009-03-09 12:37:49 +02:00
switch ( ba . actionType )
{
2011-02-12 18:12:48 +02:00
case BattleAction : : END_TACTIC_PHASE : //wait
{
2011-06-11 07:54:41 +03:00
StartAction start_action ( ba ) ;
sendAndApply ( & start_action ) ;
sendAndApply ( & end_action ) ;
2011-02-12 18:12:48 +02:00
break ;
}
2011-01-08 20:33:40 +02:00
case BattleAction : : WALK : //walk
2009-03-09 12:37:49 +02:00
{
2011-06-11 07:54:41 +03:00
StartAction start_action ( ba ) ;
sendAndApply ( & start_action ) ; //start movement
2009-03-09 12:37:49 +02:00
moveStack ( ba . stackNumber , ba . destinationTile ) ; //move
2011-06-11 07:54:41 +03:00
sendAndApply ( & end_action ) ;
2009-03-09 12:37:49 +02:00
break ;
}
2011-01-08 20:33:40 +02:00
case BattleAction : : DEFEND : //defend
2011-01-18 19:23:31 +02:00
{
2011-07-16 19:40:38 +03:00
//defensive stance //TODO: remove this bonus when stack becomes active
2011-01-18 19:23:31 +02:00
SetStackEffect sse ;
2011-01-20 21:57:12 +02:00
sse . effect . push_back ( Bonus ( Bonus : : STACK_GETS_TURN , Bonus : : PRIMARY_SKILL , Bonus : : OTHER , 20 , - 1 , PrimarySkill : : DEFENSE , Bonus : : PERCENT_TO_ALL ) ) ;
2011-07-16 19:40:38 +03:00
sse . effect . push_back ( Bonus ( Bonus : : STACK_GETS_TURN , Bonus : : PRIMARY_SKILL , Bonus : : OTHER , gs - > curB - > stacks [ ba . stackNumber ] - > valOfBonuses ( Bonus : : DEFENSIVE_STANCE ) ,
- 1 , PrimarySkill : : DEFENSE , Bonus : : ADDITIVE_VALUE ) ) ;
2011-01-18 19:23:31 +02:00
sse . stacks . push_back ( ba . stackNumber ) ;
sendAndApply ( & sse ) ;
//don't break - we share code with next case
}
2011-01-08 20:33:40 +02:00
case BattleAction : : WAIT : //wait
2009-03-09 12:37:49 +02:00
{
2011-06-11 07:54:41 +03:00
StartAction start_action ( ba ) ;
sendAndApply ( & start_action ) ;
sendAndApply ( & end_action ) ;
2009-03-09 12:37:49 +02:00
break ;
}
2011-01-08 20:33:40 +02:00
case BattleAction : : RETREAT : //retreat/flee
2009-03-09 12:37:49 +02:00
{
2011-03-05 18:38:22 +02:00
if ( ! gs - > curB - > battleCanFlee ( gs - > curB - > sides [ ba . side ] ) )
complain ( " Cannot retreat! " ) ;
else
setBattleResult ( 1 , ! ba . side ) ; //surrendering side loses
2009-03-09 12:37:49 +02:00
break ;
}
2011-03-05 18:38:22 +02:00
case BattleAction : : SURRENDER :
{
int player = gs - > curB - > sides [ ba . side ] ;
int cost = gs - > curB - > getSurrenderingCost ( player ) ;
if ( cost < 0 )
complain ( " Cannot surrender! " ) ;
else if ( getResource ( player , Res : : GOLD ) < cost )
complain ( " Not enough gold to surrender! " ) ;
else
{
giveResource ( player , Res : : GOLD , - cost ) ;
setBattleResult ( 2 , ! ba . side ) ; //surrendering side loses
}
break ;
}
break ;
2011-01-08 20:33:40 +02:00
case BattleAction : : WALK_AND_ATTACK : //walk or attack
2009-03-09 12:37:49 +02:00
{
2011-06-11 07:54:41 +03:00
StartAction start_action ( ba ) ;
sendAndApply ( & start_action ) ; //start movement and attack
2009-09-16 13:59:56 +03:00
int startingPos = gs - > curB - > getStack ( ba . stackNumber ) - > position ;
2009-08-22 18:29:30 +03:00
int distance = moveStack ( ba . stackNumber , ba . destinationTile ) ;
2009-03-09 12:37:49 +02:00
CStack * curStack = gs - > curB - > getStack ( ba . stackNumber ) ,
* stackAtEnd = gs - > curB - > getStackT ( ba . additionalInfo ) ;
2009-08-18 14:49:34 +03:00
if ( curStack - > position ! = ba . destinationTile //we wasn't able to reach destination tile
2010-05-02 21:20:26 +03:00
& & ! ( curStack - > doubleWide ( )
2009-08-18 14:49:34 +03:00
& & ( curStack - > position = = ba . destinationTile + ( curStack - > attackerOwned ? + 1 : - 1 ) )
) //nor occupy specified hex
)
2009-03-09 12:37:49 +02:00
{
2010-11-20 19:36:02 +02:00
std : : string problem = " We cannot move this stack to its destination " + curStack - > getCreature ( ) - > namePl ;
2009-08-03 17:29:29 +03:00
tlog3 < < problem < < std : : endl ;
complain ( problem ) ;
2009-04-16 03:28:54 +03:00
ok = false ;
2011-06-11 07:54:41 +03:00
sendAndApply ( & end_action ) ;
2009-08-03 17:29:29 +03:00
break ;
2009-03-09 12:37:49 +02:00
}
2011-01-08 20:33:40 +02:00
if ( stackAtEnd & & curStack - > ID = = stackAtEnd - > ID ) //we should just move, it will be handled by following check
2009-08-02 17:21:18 +03:00
{
stackAtEnd = NULL ;
}
2009-03-09 12:37:49 +02:00
if ( ! stackAtEnd )
{
2011-01-08 20:33:40 +02:00
complain ( boost : : str ( boost : : format ( " walk and attack error: no stack at additionalInfo tile (%d)! \n " ) % ba . additionalInfo ) ) ;
2009-04-16 03:28:54 +03:00
ok = false ;
2011-06-11 07:54:41 +03:00
sendAndApply ( & end_action ) ;
2009-03-09 12:37:49 +02:00
break ;
}
2011-01-08 20:33:40 +02:00
if ( ! CStack : : isMeleeAttackPossible ( curStack , stackAtEnd ) )
2009-03-09 12:37:49 +02:00
{
2011-01-15 04:17:56 +02:00
complain ( " Attack cannot be performed! " ) ;
2011-06-11 07:54:41 +03:00
sendAndApply ( & end_action ) ;
2009-04-16 03:28:54 +03:00
ok = false ;
2011-01-15 04:17:56 +02:00
break ;
2009-03-09 12:37:49 +02:00
}
//attack
2011-09-01 04:40:46 +03:00
{
BattleAttack bat ;
prepareAttack ( bat , curStack , stackAtEnd , distance , ba . additionalInfo ) ;
handleAttackBeforeCasting ( bat ) ; //only before first attack
sendAndApply ( & bat ) ;
handleAfterAttackCasting ( bat ) ;
}
2009-03-09 12:37:49 +02:00
//counterattack
2010-05-02 21:20:26 +03:00
if ( ! curStack - > hasBonusOfType ( Bonus : : BLOCKS_RETALIATION )
2011-09-01 04:40:46 +03:00
& & stackAtEnd - > ableToRetaliate ( ) )
2009-03-09 12:37:49 +02:00
{
2011-09-01 04:40:46 +03:00
BattleAttack bat ;
2011-07-05 09:07:36 +03:00
prepareAttack ( bat , stackAtEnd , curStack , 0 , curStack - > position ) ;
2011-02-24 17:33:03 +02:00
bat . flags | = BattleAttack : : COUNTER ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & bat ) ;
2010-02-23 17:39:31 +02:00
handleAfterAttackCasting ( bat ) ;
2009-03-09 12:37:49 +02:00
}
//second attack
2010-05-02 21:20:26 +03:00
if ( curStack - > valOfBonuses ( Bonus : : ADDITIONAL_ATTACK ) > 0
& & ! curStack - > hasBonusOfType ( Bonus : : SHOOTER )
2009-03-09 12:37:49 +02:00
& & curStack - > alive ( )
& & stackAtEnd - > alive ( ) )
{
2011-09-01 04:40:46 +03:00
BattleAttack bat ;
2011-07-05 09:07:36 +03:00
prepareAttack ( bat , curStack , stackAtEnd , 0 , ba . additionalInfo ) ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & bat ) ;
2010-02-23 17:39:31 +02:00
handleAfterAttackCasting ( bat ) ;
2009-03-09 12:37:49 +02:00
}
2009-09-16 13:59:56 +03:00
//return
2010-05-02 21:20:26 +03:00
if ( curStack - > hasBonusOfType ( Bonus : : RETURN_AFTER_STRIKE ) & & startingPos ! = curStack - > position & & curStack - > alive ( ) )
2009-09-16 13:59:56 +03:00
{
moveStack ( ba . stackNumber , startingPos ) ;
2010-01-29 20:17:07 +02:00
//NOTE: curStack->ID == ba.stackNumber (rev 1431)
2009-09-16 13:59:56 +03:00
}
2011-06-11 07:54:41 +03:00
sendAndApply ( & end_action ) ;
2009-03-09 12:37:49 +02:00
break ;
}
2011-01-08 20:33:40 +02:00
case BattleAction : : SHOOT : //shoot
2009-03-09 12:37:49 +02:00
{
CStack * curStack = gs - > curB - > getStack ( ba . stackNumber ) ,
* destStack = gs - > curB - > getStackT ( ba . destinationTile ) ;
2011-01-07 12:48:31 +02:00
if ( ! gs - > curB - > battleCanShoot ( curStack , ba . destinationTile ) )
2009-05-16 17:49:06 +03:00
break ;
2009-03-09 12:37:49 +02:00
2011-06-11 07:54:41 +03:00
StartAction start_action ( ba ) ;
sendAndApply ( & start_action ) ; //start shooting
2009-03-09 12:37:49 +02:00
2011-09-01 04:40:46 +03:00
{
BattleAttack bat ;
bat . flags | = BattleAttack : : SHOT ;
prepareAttack ( bat , curStack , destStack , 0 , ba . destinationTile ) ;
handleAttackBeforeCasting ( bat ) ;
sendAndApply ( & bat ) ;
handleAfterAttackCasting ( bat ) ;
}
2009-03-09 12:37:49 +02:00
2011-02-23 19:10:35 +02:00
//ballista & artillery handling
if ( destStack - > alive ( ) & & curStack - > getCreature ( ) - > idNumber = = 146 )
{
2011-02-24 17:33:03 +02:00
BattleAttack bat2 ;
bat2 . flags | = BattleAttack : : SHOT ;
2011-07-05 09:07:36 +03:00
prepareAttack ( bat2 , curStack , destStack , 0 , ba . destinationTile ) ;
2011-02-24 17:33:03 +02:00
sendAndApply ( & bat2 ) ;
2011-02-23 19:10:35 +02:00
}
2011-07-16 09:42:44 +03:00
//TODO: allow more than one additional attack
2010-05-02 21:20:26 +03:00
if ( curStack - > valOfBonuses ( Bonus : : ADDITIONAL_ATTACK ) > 0 //if unit shots twice let's make another shot
2009-03-09 12:37:49 +02:00
& & curStack - > alive ( )
& & destStack - > alive ( )
& & curStack - > shots
)
{
2011-09-01 04:40:46 +03:00
BattleAttack bat ;
bat . flags | = BattleAttack : : SHOT ;
2011-07-05 09:07:36 +03:00
prepareAttack ( bat , curStack , destStack , 0 , ba . destinationTile ) ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & bat ) ;
2010-02-23 17:39:31 +02:00
handleAfterAttackCasting ( bat ) ;
2009-03-09 12:37:49 +02:00
}
2011-06-11 07:54:41 +03:00
sendAndApply ( & end_action ) ;
2009-03-09 12:37:49 +02:00
break ;
}
2011-01-08 20:33:40 +02:00
case BattleAction : : CATAPULT : //catapult
2009-09-01 16:54:13 +03:00
{
2011-06-11 07:54:41 +03:00
StartAction start_action ( ba ) ;
sendAndApply ( & start_action ) ;
2009-10-06 03:32:33 +03:00
const CGHeroInstance * attackingHero = gs - > curB - > heroes [ ba . side ] ;
2010-12-23 22:18:10 +02:00
CHeroHandler : : SBallisticsLevelInfo sbi = VLC - > heroh - > ballistics [ attackingHero - > getSecSkillLevel ( CGHeroInstance : : BALLISTICS ) ] ;
2009-09-01 16:54:13 +03:00
int attackedPart = gs - > curB - > hexToWallPart ( ba . destinationTile ) ;
2011-10-20 20:41:40 +03:00
if ( attackedPart < 0 )
2009-09-01 16:54:13 +03:00
{
complain ( " catapult tried to attack non-catapultable hex! " ) ;
break ;
}
2010-01-28 19:23:01 +02:00
int wallInitHP = gs - > curB - > si . wallState [ attackedPart ] ;
2011-09-24 04:15:36 +03:00
int dmgAlreadyDealt = 0 ; //in successive iterations damage is dealt but not yet subtracted from wall's HPs
2009-09-01 16:54:13 +03:00
for ( int g = 0 ; g < sbi . shots ; + + g )
{
2010-01-28 19:23:01 +02:00
if ( wallInitHP + dmgAlreadyDealt = = 3 ) //it's not destroyed
2009-09-01 16:54:13 +03:00
continue ;
CatapultAttack ca ; //package for clients
2010-01-28 19:23:01 +02:00
std : : pair < std : : pair < ui8 , si16 > , ui8 > attack ; //<< attackedPart , destination tile >, damageDealt >
2009-09-24 16:44:55 +03:00
attack . first . first = attackedPart ;
attack . first . second = ba . destinationTile ;
2009-09-07 15:30:10 +03:00
attack . second = 0 ;
2009-09-01 16:54:13 +03:00
int chanceForHit = 0 ;
2011-12-14 00:23:17 +03:00
int dmgChance [ ] = { sbi . noDmg , sbi . oneDmg , sbi . twoDmg } ; //dmgChance[i] - chance for doing i dmg when hit is successful
2009-09-01 16:54:13 +03:00
switch ( attackedPart )
{
case 0 : //keep
chanceForHit = sbi . keep ;
break ;
case 1 : //bottom tower
case 6 : //upper tower
chanceForHit = sbi . tower ;
break ;
case 2 : //bottom wall
case 3 : //below gate
case 4 : //over gate
case 5 : //upper wall
chanceForHit = sbi . wall ;
break ;
case 7 : //gate
chanceForHit = sbi . gate ;
break ;
}
2009-09-05 17:10:26 +03:00
if ( rand ( ) % 100 < = chanceForHit ) //hit is successful
2009-09-01 16:54:13 +03:00
{
int dmgRand = rand ( ) % 100 ;
//accumulating dmgChance
dmgChance [ 1 ] + = dmgChance [ 0 ] ;
dmgChance [ 2 ] + = dmgChance [ 1 ] ;
//calculating dealt damage
for ( int v = 0 ; v < ARRAY_COUNT ( dmgChance ) ; + + v )
{
if ( dmgRand < = dmgChance [ v ] )
{
2010-01-28 19:23:01 +02:00
attack . second = std : : min ( 3 - dmgAlreadyDealt - wallInitHP , v ) ;
dmgAlreadyDealt + = attack . second ;
2009-09-01 16:54:13 +03:00
break ;
}
}
2010-01-28 19:23:01 +02:00
//removing creatures in turrets / keep if one is destroyed
2009-09-07 15:30:10 +03:00
if ( attack . second > 0 & & ( attackedPart = = 0 | | attackedPart = = 1 | | attackedPart = = 6 ) )
2009-09-05 17:10:26 +03:00
{
int posRemove = - 1 ;
switch ( attackedPart )
{
case 0 : //keep
posRemove = - 2 ;
break ;
case 1 : //bottom tower
posRemove = - 3 ;
break ;
case 6 : //upper tower
posRemove = - 4 ;
break ;
}
BattleStacksRemoved bsr ;
for ( int g = 0 ; g < gs - > curB - > stacks . size ( ) ; + + g )
{
if ( gs - > curB - > stacks [ g ] - > position = = posRemove )
{
bsr . stackIDs . insert ( gs - > curB - > stacks [ g ] - > ID ) ;
break ;
}
}
sendAndApply ( & bsr ) ;
}
2009-09-01 16:54:13 +03:00
}
2009-09-24 16:44:55 +03:00
ca . attacker = ba . stackNumber ;
2009-09-07 15:30:10 +03:00
ca . attackedParts . insert ( attack ) ;
2009-09-01 16:54:13 +03:00
sendAndApply ( & ca ) ;
}
2011-06-11 07:54:41 +03:00
sendAndApply ( & end_action ) ;
2010-03-07 19:40:33 +02:00
break ;
}
2011-04-25 12:03:13 +03:00
case BattleAction : : STACK_HEAL : //healing with First Aid Tent
2010-03-07 19:40:33 +02:00
{
2011-06-11 07:54:41 +03:00
StartAction start_action ( ba ) ;
sendAndApply ( & start_action ) ;
2010-03-07 19:40:33 +02:00
const CGHeroInstance * attackingHero = gs - > curB - > heroes [ ba . side ] ;
CStack * healer = gs - > curB - > getStack ( ba . stackNumber ) ,
* destStack = gs - > curB - > getStackT ( ba . destinationTile ) ;
2010-05-02 21:20:26 +03:00
if ( healer = = NULL | | destStack = = NULL | | ! healer - > hasBonusOfType ( Bonus : : HEALER ) )
2010-03-07 19:40:33 +02:00
{
complain ( " There is either no healer, no destination, or healer cannot heal :P " ) ;
}
int maxHealable = destStack - > MaxHealth ( ) - destStack - > firstHPleft ;
2011-05-15 09:37:57 +03:00
int maxiumHeal = healer - > count * std : : max ( 10 , attackingHero - > valOfBonuses ( Bonus : : SECONDARY_SKILL_PREMY , 27 ) ) ;
2010-03-07 19:40:33 +02:00
int healed = std : : min ( maxHealable , maxiumHeal ) ;
if ( healed = = 0 )
{
//nothing to heal.. should we complain?
}
else
{
StacksHealedOrResurrected shr ;
2011-06-21 15:45:57 +03:00
shr . lifeDrain = ( ui8 ) false ;
shr . tentHealing = ( ui8 ) true ;
shr . drainedFrom = ba . stackNumber ;
2010-03-07 19:40:33 +02:00
StacksHealedOrResurrected : : HealInfo hi ;
hi . healedHP = healed ;
hi . lowLevelResurrection = 0 ;
hi . stackID = destStack - > ID ;
shr . healedStacks . push_back ( hi ) ;
sendAndApply ( & shr ) ;
}
2011-10-01 22:56:54 +03:00
sendAndApply ( & end_action ) ;
break ;
}
case BattleAction : : DAEMON_SUMMONING :
//TODO: From Strategija:
//Summon Demon is a level 2 spell.
{
StartAction start_action ( ba ) ;
sendAndApply ( & start_action ) ;
CStack * summoner = gs - > curB - > getStack ( ba . stackNumber ) ,
* destStack = gs - > curB - > getStackT ( ba . destinationTile , false ) ;
BattleStackAdded bsa ;
bsa . attacker = summoner - > attackerOwned ;
bsa . creID = summoner - > getBonus ( Selector : : type ( Bonus : : DAEMON_SUMMONING ) ) - > subtype ; //in case summoner can summon more than one type of monsters... scream!
ui64 risedHp = summoner - > count * summoner - > valOfBonuses ( Bonus : : DAEMON_SUMMONING , bsa . creID ) ;
2011-10-02 10:16:34 +03:00
bsa . amount = std : : min ( ( ui32 ) ( risedHp / VLC - > creh - > creatures [ bsa . creID ] - > MaxHealth ( ) ) , destStack - > baseAmount ) ;
2011-10-01 22:56:54 +03:00
bsa . pos = gs - > curB - > getAvaliableHex ( bsa . creID , bsa . attacker , destStack - > position ) ;
bsa . summoned = false ;
2011-10-02 10:16:34 +03:00
if ( bsa . amount ) //there's rare possibility single creature cannot rise desired type
{
BattleStacksRemoved bsr ; //remove body
bsr . stackIDs . insert ( destStack - > ID ) ;
sendAndApply ( & bsr ) ;
sendAndApply ( & bsa ) ;
2011-10-02 16:59:12 +03:00
BattleSetStackProperty ssp ;
ssp . stackID = ba . stackNumber ;
2011-10-08 12:11:36 +03:00
ssp . which = BattleSetStackProperty : : CASTS ; //reduce number of casts
2011-10-02 16:59:12 +03:00
ssp . val = - 1 ;
ssp . absolute = false ;
sendAndApply ( & ssp ) ;
2011-10-02 10:16:34 +03:00
}
2011-10-01 22:56:54 +03:00
2011-10-08 12:11:36 +03:00
sendAndApply ( & end_action ) ;
break ;
}
case BattleAction : : MONSTER_SPELL :
{
StartAction start_action ( ba ) ;
sendAndApply ( & start_action ) ;
CStack * stack = gs - > curB - > getStack ( ba . stackNumber ) ;
int spellID = ba . additionalInfo ;
2011-12-22 16:05:19 +03:00
BattleHex destination ( ba . destinationTile ) ;
2011-10-08 12:11:36 +03:00
int spellLvl = 0 ;
Bonus * bonus = stack - > getBonus ( Selector : : typeSubtype ( Bonus : : SPELLCASTER , spellID ) ) ;
if ( bonus )
2011-12-14 00:23:17 +03:00
vstd : : amax ( spellLvl , bonus - > val ) ;
2011-10-08 12:11:36 +03:00
bonus = stack - > getBonus ( Selector : : type ( Bonus : : RANDOM_SPELLCASTER ) ) ;
if ( bonus )
2011-12-14 00:23:17 +03:00
vstd : : amax ( spellLvl , bonus - > val ) ;
vstd : : amin ( spellLvl , 3 ) ;
2011-10-08 12:11:36 +03:00
int casterSide = gs - > curB - > whatSide ( stack - > owner ) ;
const CGHeroInstance * secHero = gs - > curB - > getHero ( gs - > curB - > theOtherPlayer ( stack - > owner ) ) ;
2011-12-14 00:23:17 +03:00
handleSpellCasting ( spellID , spellLvl , destination , casterSide , stack - > owner , NULL , secHero , 0 , ECastingMode : : CREATURE_ACTIVE_CASTING , stack ) ;
2011-10-08 12:11:36 +03:00
2011-06-11 07:54:41 +03:00
sendAndApply ( & end_action ) ;
2009-09-01 16:54:13 +03:00
break ;
}
2009-03-09 12:37:49 +02:00
}
2010-02-02 01:30:03 +02:00
if ( ba . stackNumber = = gs - > curB - > activeStack | | battleResult . get ( ) ) //active stack has moved or battle has finished
2010-02-01 21:19:42 +02:00
battleMadeAction . setn ( true ) ;
2009-04-16 03:28:54 +03:00
return ok ;
2009-03-09 12:37:49 +02:00
}
void CGameHandler : : playerMessage ( ui8 player , const std : : string & message )
{
bool cheated = true ;
2011-06-11 07:54:41 +03:00
PlayerMessage temp_message ( player , message ) ;
sendAndApply ( & temp_message ) ;
2009-03-09 12:37:49 +02:00
if ( message = = " vcmiistari " ) //give all spells and 999 mana
{
SetMana sm ;
ChangeSpells cs ;
2009-08-04 02:53:18 +03:00
CGHeroInstance * h = gs - > getHero ( gs - > getPlayer ( player ) - > currentSelection ) ;
if ( ! h & & complain ( " Cannot realize cheat, no hero selected! " ) ) return ;
sm . hid = cs . hid = h - > id ;
//give all spells
2009-03-09 12:37:49 +02:00
cs . learn = 1 ;
for ( int i = 0 ; i < VLC - > spellh - > spells . size ( ) ; i + + )
{
2010-12-19 16:39:56 +02:00
if ( ! VLC - > spellh - > spells [ i ] - > creatureAbility )
2009-03-09 12:37:49 +02:00
cs . spells . insert ( i ) ;
}
2009-08-04 02:53:18 +03:00
//give mana
2009-03-09 12:37:49 +02:00
sm . val = 999 ;
2009-08-04 02:53:18 +03:00
2011-01-18 20:56:14 +02:00
if ( ! h - > hasSpellbook ( ) ) //hero doesn't have spellbook
2011-12-14 00:23:17 +03:00
giveHeroNewArtifact ( h , VLC - > arth - > artifacts [ 0 ] , ArtifactPosition : : SPELLBOOK ) ; //give spellbook
2009-08-04 02:53:18 +03:00
sendAndApply ( & cs ) ;
sendAndApply ( & sm ) ;
2009-03-09 12:37:49 +02:00
}
2012-02-16 20:10:58 +03:00
else if ( message = = " vcmiarmenelos " ) //build all buildings in selected town
{
CGTownInstance * town = gs - > getTown ( gs - > getPlayer ( player ) - > currentSelection ) ;
if ( town )
{
BOOST_FOREACH ( CBuildingHandler : : TBuildingsMap : : value_type & build , VLC - > buildh - > buildings [ town - > subID ] )
{
if ( ! vstd : : contains ( town - > builtBuildings , build . first )
& & ! build . second - > Name ( ) . empty ( ) )
{
buildStructure ( town - > id , build . first , true ) ;
}
}
}
}
2009-03-09 12:37:49 +02:00
else if ( message = = " vcmiainur " ) //gives 5 archangels into each slot
{
CGHeroInstance * hero = gs - > getHero ( gs - > getPlayer ( player ) - > currentSelection ) ;
2010-11-27 22:17:28 +02:00
const CCreature * archangel = VLC - > creh - > creatures [ 13 ] ;
2009-03-09 12:37:49 +02:00
if ( ! hero ) return ;
2010-05-02 21:20:26 +03:00
2011-12-14 00:23:17 +03:00
for ( int i = 0 ; i < GameConstants : : ARMY_SIZE ; i + + )
2010-11-27 22:17:28 +02:00
if ( ! hero - > hasStackAtSlot ( i ) )
2011-07-27 13:41:27 +03:00
insertNewStack ( StackLocation ( hero , i ) , archangel , 5 ) ;
2009-03-09 12:37:49 +02:00
}
2009-08-04 02:53:18 +03:00
else if ( message = = " vcmiangband " ) //gives 10 black knight into each slot
2009-03-09 12:37:49 +02:00
{
CGHeroInstance * hero = gs - > getHero ( gs - > getPlayer ( player ) - > currentSelection ) ;
2010-11-27 22:17:28 +02:00
const CCreature * blackKnight = VLC - > creh - > creatures [ 66 ] ;
2009-03-09 12:37:49 +02:00
if ( ! hero ) return ;
2010-05-02 21:20:26 +03:00
2011-12-14 00:23:17 +03:00
for ( int i = 0 ; i < GameConstants : : ARMY_SIZE ; i + + )
2010-11-27 22:17:28 +02:00
if ( ! hero - > hasStackAtSlot ( i ) )
insertNewStack ( StackLocation ( hero , i ) , blackKnight , 10 ) ;
2009-03-09 12:37:49 +02:00
}
else if ( message = = " vcminoldor " ) //all war machines
{
CGHeroInstance * hero = gs - > getHero ( gs - > getPlayer ( player ) - > currentSelection ) ;
if ( ! hero ) return ;
2011-01-18 20:56:14 +02:00
2011-12-14 00:23:17 +03:00
if ( ! hero - > getArt ( ArtifactPosition : : MACH1 ) )
giveHeroNewArtifact ( hero , VLC - > arth - > artifacts [ 4 ] , ArtifactPosition : : MACH1 ) ;
if ( ! hero - > getArt ( ArtifactPosition : : MACH2 ) )
giveHeroNewArtifact ( hero , VLC - > arth - > artifacts [ 5 ] , ArtifactPosition : : MACH2 ) ;
if ( ! hero - > getArt ( ArtifactPosition : : MACH3 ) )
giveHeroNewArtifact ( hero , VLC - > arth - > artifacts [ 6 ] , ArtifactPosition : : MACH3 ) ;
2009-03-09 12:37:49 +02:00
}
else if ( message = = " vcminahar " ) //1000000 movement points
{
CGHeroInstance * hero = gs - > getHero ( gs - > getPlayer ( player ) - > currentSelection ) ;
if ( ! hero ) return ;
SetMovePoints smp ;
smp . hid = hero - > id ;
smp . val = 1000000 ;
sendAndApply ( & smp ) ;
}
else if ( message = = " vcmiformenos " ) //give resources
{
SetResources sr ;
sr . player = player ;
sr . res = gs - > getPlayer ( player ) - > resources ;
for ( int i = 0 ; i < 7 ; i + + )
sr . res [ i ] + = 100 ;
sr . res [ 6 ] + = 19900 ;
sendAndApply ( & sr ) ;
}
else if ( message = = " vcmieagles " ) //reveal FoW
{
FoWChange fc ;
2009-08-04 02:53:18 +03:00
fc . mode = 1 ;
2009-03-09 12:37:49 +02:00
fc . player = player ;
2011-01-20 19:25:15 +02:00
int3 * hlp_tab = new int3 [ gs - > map - > width * gs - > map - > height * ( gs - > map - > twoLevel + 1 ) ] ;
int lastUnc = 0 ;
2009-03-09 12:37:49 +02:00
for ( int i = 0 ; i < gs - > map - > width ; i + + )
for ( int j = 0 ; j < gs - > map - > height ; j + + )
for ( int k = 0 ; k < gs - > map - > twoLevel + 1 ; k + + )
2010-08-03 15:34:06 +03:00
if ( ! gs - > getPlayerTeam ( fc . player ) - > fogOfWarMap [ i ] [ j ] [ k ] )
2011-01-20 19:25:15 +02:00
hlp_tab [ lastUnc + + ] = int3 ( i , j , k ) ;
fc . tiles . insert ( hlp_tab , hlp_tab + lastUnc ) ;
delete [ ] hlp_tab ;
2009-03-09 12:37:49 +02:00
sendAndApply ( & fc ) ;
}
2010-07-30 14:29:42 +03:00
else if ( message = = " vcmiglorfindel " ) //selected hero gains a new level
2009-03-09 12:37:49 +02:00
{
CGHeroInstance * hero = gs - > getHero ( gs - > getPlayer ( player ) - > currentSelection ) ;
changePrimSkill ( hero - > id , 4 , VLC - > heroh - > reqExp ( hero - > level + 1 ) - VLC - > heroh - > reqExp ( hero - > level ) ) ;
}
2010-07-30 14:29:42 +03:00
else if ( message = = " vcmisilmaril " ) //player wins
{
gs - > getPlayer ( player ) - > enteredWinningCheatCode = 1 ;
}
else if ( message = = " vcmimelkor " ) //player looses
{
gs - > getPlayer ( player ) - > enteredLosingCheatCode = 1 ;
}
2010-08-05 12:47:12 +03:00
else if ( message = = " vcmiforgeofnoldorking " ) //hero gets all artifacts except war machines, spell scrolls and spell book
{
CGHeroInstance * hero = gs - > getHero ( gs - > getPlayer ( player ) - > currentSelection ) ;
if ( ! hero ) return ;
for ( int g = 7 ; g < = 140 ; + + g )
2011-01-18 20:56:14 +02:00
giveHeroNewArtifact ( hero , VLC - > arth - > artifacts [ g ] , - 1 ) ;
2010-08-05 12:47:12 +03:00
}
2009-03-09 12:37:49 +02:00
else
cheated = false ;
if ( cheated )
{
2011-06-11 07:54:41 +03:00
SystemMessage temp_message ( VLC - > generaltexth - > allTexts [ 260 ] ) ;
sendAndApply ( & temp_message ) ;
2012-02-16 20:10:58 +03:00
checkLossVictory ( player ) ; //Player enter win code or got required art\creature
2009-03-09 12:37:49 +02:00
}
}
2011-12-22 16:05:19 +03:00
void CGameHandler : : handleSpellCasting ( int spellID , int spellLvl , BattleHex destination , ui8 casterSide , ui8 casterColor , const CGHeroInstance * caster , const CGHeroInstance * secHero , int usedSpellPower , ECastingMode : : ECastingMode mode , const CStack * stack )
2010-02-23 17:39:31 +02:00
{
2010-12-19 16:39:56 +02:00
const CSpell * spell = VLC - > spellh - > spells [ spellID ] ;
2010-02-23 17:39:31 +02:00
2010-05-16 16:42:19 +03:00
BattleSpellCast sc ;
2010-02-23 17:39:31 +02:00
sc . side = casterSide ;
sc . id = spellID ;
sc . skill = spellLvl ;
sc . tile = destination ;
sc . dmgToDisplay = 0 ;
sc . castedByHero = ( bool ) caster ;
2011-05-13 13:45:36 +03:00
sc . attackerType = ( stack ? stack - > type - > idNumber : - 1 ) ;
2011-10-09 14:23:24 +03:00
sc . manaGained = 0 ;
sc . spellCost = 0 ;
if ( caster ) //calculate spell cost
{
sc . spellCost = gs - > curB - > getSpellCost ( VLC - > spellh - > spells [ spellID ] , caster ) ;
2011-12-14 00:23:17 +03:00
if ( secHero & & mode = = ECastingMode : : HERO_CASTING ) //handle mana channel
2011-10-09 14:23:24 +03:00
{
int manaChannel = 0 ;
BOOST_FOREACH ( CStack * stack , gs - > curB - > stacks ) //TODO: shouldn't bonus system handle it somehow?
{
if ( stack - > owner = = secHero - > tempOwner )
{
2011-12-14 00:23:17 +03:00
vstd : : amax ( manaChannel , stack - > valOfBonuses ( Bonus : : MANA_CHANNELING ) ) ;
2011-10-09 14:23:24 +03:00
}
}
sc . manaGained = ( manaChannel * sc . spellCost ) / 100 ;
}
}
2010-02-23 17:39:31 +02:00
//calculating affected creatures for all spells
2011-10-09 10:20:23 +03:00
std : : set < CStack * > attackedCres ;
2011-12-14 00:23:17 +03:00
if ( mode ! = ECastingMode : : ENCHANTER_CASTING )
2011-10-09 10:20:23 +03:00
{
attackedCres = gs - > curB - > getAttackedCreatures ( spell , spellLvl , casterColor , destination ) ;
for ( std : : set < CStack * > : : const_iterator it = attackedCres . begin ( ) ; it ! = attackedCres . end ( ) ; + + it )
{
sc . affectedCres . insert ( ( * it ) - > ID ) ;
}
}
else //enchanter - hit all possible stacks
2010-02-23 17:39:31 +02:00
{
2011-10-09 10:20:23 +03:00
BOOST_FOREACH ( CStack * stack , gs - > curB - > stacks )
{
/*if it's non negative spell and our unit or non positive spell and hostile unit */
2012-02-17 00:19:07 +03:00
if ( ( ! spell - > isNegative ( ) & & stack - > owner = = casterColor )
| | ( ! spell - > isPositive ( ) & & stack - > owner ! = casterColor ) )
2011-10-09 10:20:23 +03:00
{
2012-02-17 00:19:07 +03:00
if ( stack - > isValidTarget ( ) ) //TODO: allow dead targets somewhere in the future
2011-10-09 10:20:23 +03:00
attackedCres . insert ( stack ) ;
}
}
2010-02-23 17:39:31 +02:00
}
//checking if creatures resist
2012-01-03 04:55:26 +03:00
sc . resisted = gs - > curB - > calculateResistedStacks ( spell , caster , secHero , attackedCres , casterColor , mode , usedSpellPower , spellLvl ) ;
2010-02-23 17:39:31 +02:00
//calculating dmg to display
for ( std : : set < CStack * > : : iterator it = attackedCres . begin ( ) ; it ! = attackedCres . end ( ) ; + + it )
{
if ( vstd : : contains ( sc . resisted , ( * it ) - > ID ) ) //this creature resisted the spell
continue ;
2010-02-24 20:11:08 +02:00
sc . dmgToDisplay + = gs - > curB - > calculateSpellDmg ( spell , caster , * it , spellLvl , usedSpellPower ) ;
2010-02-23 17:39:31 +02:00
}
2011-11-13 12:29:22 +03:00
if ( spellID = = Spells : : DEATH_STARE | | spellID = = Spells : : ACID_BREATH_DAMAGE )
2011-04-23 20:10:54 +03:00
{
sc . dmgToDisplay = usedSpellPower ;
2011-11-13 12:29:22 +03:00
if ( spellID = = Spells : : DEATH_STARE )
2011-12-14 00:23:17 +03:00
vstd : : amin ( sc . dmgToDisplay , ( * attackedCres . begin ( ) ) - > count ) ; //stack is already reduced after attack
2011-04-23 20:10:54 +03:00
}
2011-08-12 22:38:30 +03:00
StacksInjured si ;
2010-02-23 17:39:31 +02:00
//applying effects
2011-11-13 12:29:22 +03:00
switch ( spellID )
{ //damage spells
case Spells : : MAGIC_ARROW :
case Spells : : ICE_BOLT :
case Spells : : LIGHTNING_BOLT :
case Spells : : IMPLOSION :
case Spells : : FROST_RING :
case Spells : : FIREBALL :
case Spells : : INFERNO :
case Spells : : METEOR_SHOWER :
case Spells : : DEATH_RIPPLE :
case Spells : : DESTROY_UNDEAD :
case Spells : : ARMAGEDDON :
case Spells : : TITANS_LIGHTNING_BOLT :
case Spells : : THUNDERBOLT : //(thunderbirds)
2010-02-23 17:39:31 +02:00
{
2011-07-08 10:00:11 +03:00
int spellDamage = 0 ;
2011-12-14 00:23:17 +03:00
if ( stack & & mode ! = ECastingMode : : MAGIC_MIRROR )
2011-07-08 10:00:11 +03:00
{
int unitSpellPower = stack - > valOfBonuses ( Bonus : : SPECIFIC_SPELL_POWER , spellID ) ;
if ( unitSpellPower )
sc . dmgToDisplay = spellDamage = stack - > count * unitSpellPower ; //TODO: handle immunities
2011-07-08 16:17:05 +03:00
else //Faerie Dragon
2011-10-08 12:11:36 +03:00
{
2011-07-08 16:17:05 +03:00
usedSpellPower = stack - > valOfBonuses ( Bonus : : CREATURE_SPELL_POWER ) * stack - > count / 100 ;
2011-10-08 12:11:36 +03:00
sc . dmgToDisplay = 0 ;
}
2011-07-08 10:00:11 +03:00
}
2010-02-23 17:39:31 +02:00
for ( std : : set < CStack * > : : iterator it = attackedCres . begin ( ) ; it ! = attackedCres . end ( ) ; + + it )
{
if ( vstd : : contains ( sc . resisted , ( * it ) - > ID ) ) //this creature resisted the spell
continue ;
BattleStackAttacked bsa ;
2011-09-23 18:58:18 +03:00
if ( ( destination > - 1 & & ( * it ) - > coversPos ( destination ) ) | | spell - > range [ spellLvl ] = = " X " ) //display effect only upon primary target of area spell
2011-07-03 08:55:57 +03:00
{
bsa . flags | = BattleStackAttacked : : EFFECT ;
bsa . effect = spell - > mainEffectAnim ;
}
2011-07-08 10:00:11 +03:00
if ( spellDamage )
bsa . damageAmount = spellDamage ;
else
2011-10-08 12:11:36 +03:00
{
2011-07-08 10:00:11 +03:00
bsa . damageAmount = gs - > curB - > calculateSpellDmg ( spell , caster , * it , spellLvl , usedSpellPower ) ;
2011-10-08 12:11:36 +03:00
sc . dmgToDisplay + = bsa . damageAmount ;
}
2010-02-23 17:39:31 +02:00
bsa . stackAttacked = ( * it ) - > ID ;
bsa . attackerID = - 1 ;
2011-01-08 20:33:40 +02:00
( * it ) - > prepareAttacked ( bsa ) ;
2010-02-23 17:39:31 +02:00
si . stacks . push_back ( bsa ) ;
}
break ;
}
2011-11-13 12:29:22 +03:00
// permanent effects
case Spells : : SHIELD :
case Spells : : AIR_SHIELD :
case Spells : : FIRE_SHIELD :
case Spells : : PROTECTION_FROM_AIR :
case Spells : : PROTECTION_FROM_FIRE :
case Spells : : PROTECTION_FROM_WATER :
case Spells : : PROTECTION_FROM_EARTH :
case Spells : : ANTI_MAGIC :
case Spells : : MAGIC_MIRROR :
case Spells : : BLESS :
case Spells : : CURSE :
case Spells : : BLOODLUST :
case Spells : : PRECISION :
case Spells : : WEAKNESS :
case Spells : : STONE_SKIN :
case Spells : : DISRUPTING_RAY :
case Spells : : PRAYER :
case Spells : : MIRTH :
case Spells : : SORROW :
case Spells : : FORTUNE :
case Spells : : MISFORTUNE :
case Spells : : HASTE :
case Spells : : SLOW :
case Spells : : SLAYER :
case Spells : : FRENZY :
case Spells : : COUNTERSTRIKE :
case Spells : : BERSERK :
case Spells : : HYPNOTIZE :
case Spells : : FORGETFULNESS :
case Spells : : BLIND :
case Spells : : STONE_GAZE :
case Spells : : POISON :
case Spells : : BIND :
case Spells : : DISEASE :
case Spells : : PARALYZE :
case Spells : : AGE :
case Spells : : ACID_BREATH_DEFENSE :
2010-02-23 17:39:31 +02:00
{
2011-07-08 16:17:05 +03:00
int stackSpellPower = 0 ;
2011-12-14 00:23:17 +03:00
if ( stack & & mode ! = ECastingMode : : MAGIC_MIRROR )
2011-07-08 16:17:05 +03:00
{
stackSpellPower = stack - > valOfBonuses ( Bonus : : CREATURE_ENCHANT_POWER ) ;
}
2010-02-23 17:39:31 +02:00
SetStackEffect sse ;
2011-04-23 12:57:51 +03:00
Bonus pseudoBonus ;
pseudoBonus . sid = spellID ;
pseudoBonus . val = spellLvl ;
2011-07-08 16:17:05 +03:00
pseudoBonus . turnsRemain = gs - > curB - > calculateSpellDuration ( spell , caster , stackSpellPower ? stackSpellPower : usedSpellPower ) ;
2011-04-23 12:57:51 +03:00
CStack : : stackEffectToFeature ( sse . effect , pseudoBonus ) ;
2011-10-17 11:24:51 +03:00
if ( spellID = = 72 & & stack ) //bind
{
sse . effect . back ( ) . additionalInfo = stack - > ID ; //we need to know who casted Bind
}
2011-04-25 12:03:13 +03:00
const Bonus * bonus = NULL ;
if ( caster )
2011-06-24 20:43:02 +03:00
bonus = caster - > getBonus ( Selector : : typeSubtype ( Bonus : : SPECIAL_PECULIAR_ENCHANT , spellID ) ) ;
2011-04-23 12:57:51 +03:00
2011-06-24 10:12:52 +03:00
si32 power = 0 ;
2010-02-23 17:39:31 +02:00
for ( std : : set < CStack * > : : iterator it = attackedCres . begin ( ) ; it ! = attackedCres . end ( ) ; + + it )
{
if ( vstd : : contains ( sc . resisted , ( * it ) - > ID ) ) //this creature resisted the spell
continue ;
sse . stacks . push_back ( ( * it ) - > ID ) ;
2011-04-23 12:57:51 +03:00
//Apply hero specials - peculiar enchants
2011-05-21 10:07:07 +03:00
if ( ( * it ) - > base ) // no war machines - TODO: make it work
{
ui8 tier = ( * it ) - > base - > type - > level ;
if ( bonus )
{
switch ( bonus - > additionalInfo )
{
case 0 : //normal
{
switch ( tier )
{
case 1 : case 2 :
power = 3 ;
break ;
case 3 : case 4 :
power = 2 ;
break ;
case 5 : case 6 :
power = 1 ;
break ;
}
Bonus specialBonus ( sse . effect . back ( ) ) ;
specialBonus . val = power ; //it doesn't necessarily make sense for some spells, use it wisely
sse . uniqueBonuses . push_back ( std : : pair < ui32 , Bonus > ( ( * it ) - > ID , specialBonus ) ) ; //additional premy to given effect
}
break ;
case 1 : //only Coronius as yet
{
power = std : : max ( 5 - tier , 0 ) ;
Bonus specialBonus = CStack : : featureGenerator ( Bonus : : PRIMARY_SKILL , PrimarySkill : : ATTACK , power , pseudoBonus . turnsRemain ) ;
specialBonus . sid = spellID ;
sse . uniqueBonuses . push_back ( std : : pair < ui32 , Bonus > ( ( * it ) - > ID , specialBonus ) ) ; //additional attack to Slayer effect
}
break ;
}
}
if ( caster & & caster - > hasBonusOfType ( Bonus : : SPECIAL_BLESS_DAMAGE , spellID ) ) //TODO: better handling of bonus percentages
2011-04-23 12:57:51 +03:00
{
2011-05-21 10:07:07 +03:00
int damagePercent = caster - > level * caster - > valOfBonuses ( Bonus : : SPECIAL_BLESS_DAMAGE , spellID ) / tier ;
Bonus specialBonus = CStack : : featureGenerator ( Bonus : : CREATURE_DAMAGE , 0 , damagePercent , pseudoBonus . turnsRemain ) ;
specialBonus . valType = Bonus : : PERCENT_TO_ALL ;
specialBonus . sid = spellID ;
sse . uniqueBonuses . push_back ( std : : pair < ui32 , Bonus > ( ( * it ) - > ID , specialBonus ) ) ;
2011-04-23 12:57:51 +03:00
}
2011-05-21 10:07:07 +03:00
}
2010-02-23 17:39:31 +02:00
}
2011-04-23 12:57:51 +03:00
2010-02-23 17:39:31 +02:00
if ( ! sse . stacks . empty ( ) )
sendAndApply ( & sse ) ;
break ;
}
2011-11-13 12:29:22 +03:00
case Spells : : TELEPORT :
2010-05-07 17:05:48 +03:00
{
BattleStackMoved bsm ;
bsm . distance = - 1 ;
bsm . stack = gs - > curB - > activeStack ;
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > tiles ;
2011-08-01 20:36:18 +03:00
tiles . push_back ( destination ) ;
bsm . tilesToMove = tiles ;
2010-05-07 17:05:48 +03:00
bsm . teleporting = true ;
sendAndApply ( & bsm ) ;
break ;
}
2011-11-13 12:29:22 +03:00
case Spells : : CURE :
case Spells : : RESURRECTION :
case Spells : : ANIMATE_DEAD :
2010-02-23 17:39:31 +02:00
{
2011-07-08 16:17:05 +03:00
int hpGained = 0 ;
if ( stack )
{
int unitSpellPower = stack - > valOfBonuses ( Bonus : : SPECIFIC_SPELL_POWER , spellID ) ;
if ( unitSpellPower )
hpGained = stack - > count * unitSpellPower ; //Archangel
else //Faerie Dragon-like effect - unused fo far
usedSpellPower = stack - > valOfBonuses ( Bonus : : CREATURE_SPELL_POWER ) * stack - > count / 100 ;
}
2010-02-23 17:39:31 +02:00
StacksHealedOrResurrected shr ;
2011-06-21 15:45:57 +03:00
shr . lifeDrain = ( ui8 ) false ;
shr . tentHealing = ( ui8 ) false ;
2010-02-23 17:39:31 +02:00
for ( std : : set < CStack * > : : iterator it = attackedCres . begin ( ) ; it ! = attackedCres . end ( ) ; + + it )
{
if ( vstd : : contains ( sc . resisted , ( * it ) - > ID ) //this creature resisted the spell
2011-11-13 12:29:22 +03:00
| | ( spellID = = Spells : : ANIMATE_DEAD & & ! ( * it ) - > hasBonusOfType ( Bonus : : UNDEAD ) ) //we try to cast animate dead on living stack
2010-02-23 17:39:31 +02:00
)
continue ;
StacksHealedOrResurrected : : HealInfo hi ;
hi . stackID = ( * it ) - > ID ;
2011-07-08 16:17:05 +03:00
if ( stack )
{
if ( hpGained )
{
hi . healedHP = gs - > curB - > calculateHealedHP ( hpGained , spell , * it ) ;
}
else
2012-01-03 04:55:26 +03:00
hi . healedHP = gs - > curB - > calculateHealedHP ( spell , usedSpellPower , spellLvl , * it ) ;
2011-07-08 16:17:05 +03:00
}
else
hi . healedHP = gs - > curB - > calculateHealedHP ( caster , spell , * it ) ;
2010-02-23 17:39:31 +02:00
hi . lowLevelResurrection = spellLvl < = 1 ;
shr . healedStacks . push_back ( hi ) ;
}
if ( ! shr . healedStacks . empty ( ) )
sendAndApply ( & shr ) ;
break ;
}
2011-11-13 12:29:22 +03:00
case Spells : : SUMMON_FIRE_ELEMENTAL :
case Spells : : SUMMON_EARTH_ELEMENTAL :
case Spells : : SUMMON_WATER_ELEMENTAL :
case Spells : : SUMMON_AIR_ELEMENTAL :
2011-10-01 22:56:54 +03:00
{ //elemental summoning
int creID ;
switch ( spellID )
{
2011-11-13 12:29:22 +03:00
case Spells : : SUMMON_FIRE_ELEMENTAL :
creID = 114 ;
2011-10-01 22:56:54 +03:00
break ;
2011-11-13 12:29:22 +03:00
case Spells : : SUMMON_EARTH_ELEMENTAL :
creID = 113 ;
2011-10-01 22:56:54 +03:00
break ;
2011-11-13 12:29:22 +03:00
case Spells : : SUMMON_WATER_ELEMENTAL :
creID = 115 ;
2011-10-01 22:56:54 +03:00
break ;
2011-11-13 12:29:22 +03:00
case Spells : : SUMMON_AIR_ELEMENTAL :
creID = 112 ;
2011-10-01 22:56:54 +03:00
break ;
}
BattleStackAdded bsa ;
bsa . creID = creID ;
bsa . attacker = ! ( bool ) casterSide ;
bsa . summoned = true ;
2012-01-03 04:55:26 +03:00
bsa . pos = gs - > curB - > getAvaliableHex ( creID , ! ( bool ) casterSide ) ; //TODO: unify it
//TODO stack casting -> probably power will be zero; set the proper number of creatures manually
int percentBonus = caster ? caster - > valOfBonuses ( Bonus : : SPECIFIC_SPELL_DAMAGE , spellID ) : 0 ;
bsa . amount = usedSpellPower * VLC - > spellh - > spells [ spellID ] - > powers [ spellLvl ] *
( 100 + percentBonus ) / 100.0 ; //new feature - percentage bonus
if ( bsa . amount )
sendAndApply ( & bsa ) ;
else
complain ( " Summoning elementals didn't summon any! " ) ;
2011-10-01 22:56:54 +03:00
}
break ;
2012-02-10 16:13:24 +03:00
case Spells : : CLONE :
{
CStack * clonedStack = NULL ;
if ( attackedCres . size ( ) )
clonedStack = * attackedCres . begin ( ) ;
if ( ! clonedStack )
{
complain ( " No target stack to clone! " ) ;
return ;
}
BattleStackAdded bsa ;
bsa . creID = clonedStack - > type - > idNumber ;
bsa . attacker = ! ( bool ) casterSide ;
bsa . summoned = true ;
bsa . pos = gs - > curB - > getAvaliableHex ( bsa . creID , ! ( bool ) casterSide ) ; //TODO: unify it
bsa . amount = clonedStack - > count ;
sendAndApply ( & bsa ) ;
BattleSetStackProperty ssp ;
ssp . stackID = gs - > curB - > stacks . back ( ) - > ID ; //how to get recent stack?
ssp . which = BattleSetStackProperty : : CLONED ; //using enum values
ssp . val = 0 ;
ssp . absolute = 1 ;
sendAndApply ( & ssp ) ;
}
break ;
2011-11-13 12:29:22 +03:00
case Spells : : REMOVE_OBSTACLE :
2010-02-23 17:39:31 +02:00
{
ObstaclesRemoved obr ;
for ( int g = 0 ; g < gs - > curB - > obstacles . size ( ) ; + + g )
{
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > blockedHexes = VLC - > heroh - > obstacles [ gs - > curB - > obstacles [ g ] . ID ] . getBlocked ( gs - > curB - > obstacles [ g ] . pos ) ;
2010-02-23 17:39:31 +02:00
if ( vstd : : contains ( blockedHexes , destination ) ) //this obstacle covers given hex
{
obr . obstacles . insert ( gs - > curB - > obstacles [ g ] . uniqueID ) ;
}
}
if ( ! obr . obstacles . empty ( ) )
sendAndApply ( & obr ) ;
break ;
}
2011-04-25 12:03:13 +03:00
break ;
2011-11-13 12:29:22 +03:00
case Spells : : DEATH_STARE : //handled in a bit different way
2011-04-23 20:10:54 +03:00
{
for ( std : : set < CStack * > : : iterator it = attackedCres . begin ( ) ; it ! = attackedCres . end ( ) ; + + it )
{
2011-04-25 12:03:13 +03:00
if ( ( * it ) - > hasBonusOfType ( Bonus : : UNDEAD ) | | ( * it ) - > hasBonusOfType ( Bonus : : NON_LIVING ) ) //this creature is immune
{
sc . dmgToDisplay = 0 ; //TODO: handle Death Stare for multiple targets (?)
2011-04-23 20:10:54 +03:00
continue ;
2011-04-25 12:03:13 +03:00
}
2011-04-23 20:10:54 +03:00
BattleStackAttacked bsa ;
bsa . flags | = BattleStackAttacked : : EFFECT ;
2011-04-25 12:03:13 +03:00
bsa . effect = spell - > mainEffectAnim ; //from config\spell-Info.txt
2011-04-23 20:10:54 +03:00
bsa . damageAmount = usedSpellPower * ( * it ) - > valOfBonuses ( Bonus : : STACK_HEALTH ) ;
bsa . stackAttacked = ( * it ) - > ID ;
bsa . attackerID = - 1 ;
( * it ) - > prepareAttacked ( bsa ) ;
si . stacks . push_back ( bsa ) ;
}
}
break ;
2011-11-13 12:29:22 +03:00
case Spells : : ACID_BREATH_DAMAGE : //new effect, separate from acid breath defense reduction
2011-05-13 12:02:16 +03:00
{
for ( std : : set < CStack * > : : iterator it = attackedCres . begin ( ) ; it ! = attackedCres . end ( ) ; + + it ) //no immunities
{
BattleStackAttacked bsa ;
bsa . flags | = BattleStackAttacked : : EFFECT ;
bsa . effect = VLC - > spellh - > spells [ 80 ] - > mainEffectAnim ; //use acid breath
bsa . damageAmount = usedSpellPower ; //damage times the number of attackers
bsa . stackAttacked = ( * it ) - > ID ;
bsa . attackerID = - 1 ;
( * it ) - > prepareAttacked ( bsa ) ;
si . stacks . push_back ( bsa ) ;
}
}
break ;
2010-02-23 17:39:31 +02:00
}
2011-04-25 12:03:13 +03:00
sendAndApply ( & sc ) ;
2011-08-12 22:38:30 +03:00
if ( ! si . stacks . empty ( ) ) //after spellcast info shows
sendAndApply ( & si ) ;
2011-10-01 22:56:54 +03:00
2011-12-14 00:23:17 +03:00
if ( mode = = ECastingMode : : CREATURE_ACTIVE_CASTING | | mode = = ECastingMode : : ENCHANTER_CASTING ) //reduce number of casts remaining
2011-10-09 10:20:23 +03:00
{
BattleSetStackProperty ssp ;
ssp . stackID = stack - > ID ;
ssp . which = BattleSetStackProperty : : CASTS ;
ssp . val = - 1 ;
ssp . absolute = false ;
sendAndApply ( & ssp ) ;
}
2011-07-16 11:28:01 +03:00
//Magic Mirror effect
2012-02-17 00:19:07 +03:00
if ( spell - > isNegative ( ) & & mode ! = ECastingMode : : MAGIC_MIRROR & & spell - > level & & spell - > range [ 0 ] = = " 0 " ) //it is actual spell and can be reflected to single target, no recurrence
2011-07-16 11:28:01 +03:00
{
for ( std : : set < CStack * > : : iterator it = attackedCres . begin ( ) ; it ! = attackedCres . end ( ) ; + + it )
{
int mirrorChance = ( * it ) - > valOfBonuses ( Bonus : : MAGIC_MIRROR ) ;
if ( mirrorChance > rand ( ) % 100 )
{
2011-07-16 19:40:38 +03:00
std : : vector < CStack * > mirrorTargets ;
std : : vector < CStack * > & battleStacks = gs - > curB - > stacks ;
2011-07-30 18:04:34 +03:00
for ( size_t j = 0 ; j < battleStacks . size ( ) ; + + j )
2011-07-16 19:40:38 +03:00
{
2011-07-30 18:04:34 +03:00
if ( battleStacks [ j ] - > owner = = casterSide ) //get enemy stacks which can be affected by this spell
2011-07-16 19:40:38 +03:00
{
2011-12-14 00:23:17 +03:00
if ( ! gs - > curB - > battleIsImmune ( NULL , spell , ECastingMode : : MAGIC_MIRROR , battleStacks [ j ] - > position ) )
2011-07-30 18:04:34 +03:00
mirrorTargets . push_back ( battleStacks [ j ] ) ;
2011-07-16 19:40:38 +03:00
}
}
if ( mirrorTargets . size ( ) )
2011-07-16 11:28:01 +03:00
{
2011-07-16 19:40:38 +03:00
int targetHex = mirrorTargets [ rand ( ) % mirrorTargets . size ( ) ] - > position ;
2011-12-14 00:23:17 +03:00
handleSpellCasting ( spellID , 0 , targetHex , 1 - casterSide , ( * it ) - > owner , NULL , ( caster ? caster : NULL ) , usedSpellPower , ECastingMode : : MAGIC_MIRROR , ( * it ) ) ;
2011-07-16 11:28:01 +03:00
}
}
}
}
2010-02-23 17:39:31 +02:00
}
2009-04-16 03:28:54 +03:00
bool CGameHandler : : makeCustomAction ( BattleAction & ba )
2009-03-09 12:37:49 +02:00
{
switch ( ba . actionType )
{
2011-03-05 18:38:22 +02:00
case BattleAction : : HERO_SPELL : //hero casts spell
2009-03-09 12:37:49 +02:00
{
2009-10-06 03:32:33 +03:00
const CGHeroInstance * h = gs - > curB - > heroes [ ba . side ] ;
const CGHeroInstance * secondHero = gs - > curB - > heroes [ ! ba . side ] ;
2009-03-09 12:37:49 +02:00
if ( ! h )
{
tlog2 < < " Wrong caster! \n " ;
2009-04-16 03:28:54 +03:00
return false ;
2009-03-09 12:37:49 +02:00
}
if ( ba . additionalInfo > = VLC - > spellh - > spells . size ( ) )
{
tlog2 < < " Wrong spell id ( " < < ba . additionalInfo < < " )! \n " ;
2009-04-16 03:28:54 +03:00
return false ;
2009-03-09 12:37:49 +02:00
}
2010-12-19 16:39:56 +02:00
const CSpell * s = VLC - > spellh - > spells [ ba . additionalInfo ] ;
2012-02-10 16:13:24 +03:00
if ( s - > mainEffectAnim > - 1 | | ( s - > id > = 66 | | s - > id < = 69 ) | | s - > id = = Spells : : CLONE ) //allow summon elementals
2011-10-01 22:56:54 +03:00
//TODO: special effects, like Clone
2009-03-09 12:37:49 +02:00
{
2011-06-19 12:07:15 +03:00
ui8 skill = h - > getSpellSchoolLevel ( s ) ; //skill level
2009-03-09 12:37:49 +02:00
2011-12-14 00:23:17 +03:00
ESpellCastProblem : : ESpellCastProblem escp = gs - > curB - > battleCanCastThisSpell ( h - > tempOwner , s , ECastingMode : : HERO_CASTING ) ;
if ( escp ! = ESpellCastProblem : : OK )
2011-06-19 12:07:15 +03:00
{
tlog2 < < " Spell cannot be cast! \n " ;
tlog2 < < " Problem : " < < escp < < std : : endl ;
return false ;
}
2009-03-09 12:37:49 +02:00
2011-06-19 12:07:15 +03:00
StartAction start_action ( ba ) ;
sendAndApply ( & start_action ) ; //start spell casting
2009-04-21 20:32:43 +03:00
2011-12-14 00:23:17 +03:00
handleSpellCasting ( ba . additionalInfo , skill , ba . destinationTile , ba . side , h - > tempOwner , h , secondHero , h - > getPrimSkillLevel ( 2 ) , ECastingMode : : HERO_CASTING , NULL ) ;
2011-06-19 12:07:15 +03:00
sendAndApply ( & end_action ) ;
if ( ! gs - > curB - > getStack ( gs - > curB - > activeStack , false ) - > alive ( ) )
{
battleMadeAction . setn ( true ) ;
}
checkForBattleEnd ( gs - > curB - > stacks ) ;
if ( battleResult . get ( ) )
{
battleMadeAction . setn ( true ) ;
//battle will be ended by startBattle function
//endBattle(gs->curB->tile, gs->curB->heroes[0], gs->curB->heroes[1]);
}
return true ;
2009-09-28 18:05:35 +03:00
}
2011-06-19 12:07:15 +03:00
else
2009-09-28 17:21:48 +03:00
{
2011-06-19 12:07:15 +03:00
tlog2 < < " Spell " < < s - > name < < " is not yet supported! \n " ;
return false ;
2009-09-28 17:21:48 +03:00
}
2009-03-09 12:37:49 +02:00
}
}
2009-04-16 03:28:54 +03:00
return false ;
2009-03-09 21:40:43 +02:00
}
2011-10-08 16:02:58 +03:00
void CGameHandler : : stackTurnTrigger ( const CStack * st )
{
BattleTriggerEffect bte ;
bte . stackID = st - > ID ;
bte . effect = - 1 ;
bte . val = 0 ;
bte . additionalInfo = 0 ;
if ( st - > alive ( ) )
{
2011-10-17 11:24:51 +03:00
//unbind
if ( st - > getEffect ( 72 ) )
{
bool unbind = true ;
BonusList bl = * ( st - > getBonuses ( Selector : : type ( Bonus : : BIND_EFFECT ) ) ) ;
std : : set < CStack * > stacks = gs - > curB - > getAdjacentCreatures ( st ) ;
BOOST_FOREACH ( Bonus * b , bl )
{
const CStack * stack = gs - > curB - > getStack ( b - > additionalInfo ) ; //binding stack must be alive and adjacent
if ( stack )
{
if ( vstd : : contains ( stacks , stack ) ) //binding stack is still present
{
unbind = false ;
}
}
}
if ( unbind )
{
BattleSetStackProperty ssp ;
ssp . which = BattleSetStackProperty : : UNBIND ;
ssp . stackID = st - > ID ;
sendAndApply ( & ssp ) ;
}
}
2011-10-08 16:02:58 +03:00
//regeneration
if ( st - > hasBonusOfType ( Bonus : : HP_REGENERATION ) )
{
bte . effect = Bonus : : HP_REGENERATION ;
bte . val = std : : min ( ( int ) ( st - > MaxHealth ( ) - st - > firstHPleft ) , st - > valOfBonuses ( Bonus : : HP_REGENERATION ) ) ;
}
if ( st - > hasBonusOfType ( Bonus : : FULL_HP_REGENERATION ) )
{
bte . effect = Bonus : : HP_REGENERATION ;
bte . val = st - > MaxHealth ( ) - st - > firstHPleft ;
}
if ( bte . val ) //anything to heal
sendAndApply ( & bte ) ;
if ( st - > hasBonusOfType ( Bonus : : POISON ) )
{
const Bonus * b = st - > getBonus ( Selector : : source ( Bonus : : SPELL_EFFECT , 71 ) & & Selector : : type ( Bonus : : STACK_HEALTH ) ) ;
if ( b ) //TODO: what if not?...
{
bte . val = std : : max ( b - > val - 10 , - ( st - > valOfBonuses ( Bonus : : POISON ) ) ) ;
if ( bte . val < b - > val ) //(negative) poison effect increases - update it
{
bte . effect = Bonus : : POISON ;
sendAndApply ( & bte ) ;
}
}
}
if ( st - > hasBonusOfType ( Bonus : : MANA_DRAIN ) )
{
const CGHeroInstance * enemy = gs - > curB - > getHero ( gs - > curB - > theOtherPlayer ( st - > owner ) ) ;
if ( enemy )
{
ui32 manaDrained = st - > valOfBonuses ( Bonus : : MANA_DRAIN ) ;
2011-12-14 00:23:17 +03:00
vstd : : amin ( manaDrained , gs - > curB - > heroes [ 0 ] - > mana ) ;
2011-10-08 16:02:58 +03:00
if ( manaDrained )
{
bte . effect = Bonus : : MANA_DRAIN ;
bte . val = manaDrained ;
bte . additionalInfo = enemy - > id ; //for sanity
sendAndApply ( & bte ) ;
}
}
}
2011-10-08 20:10:43 +03:00
if ( ! st - > hasBonusOfType ( Bonus : : FEARLESS ) )
{
bool fearsomeCreature = false ;
BOOST_FOREACH ( CStack * stack , gs - > curB - > stacks )
{
if ( stack - > owner ! = st - > owner & & stack - > hasBonusOfType ( Bonus : : FEAR ) )
{
fearsomeCreature = true ;
break ;
}
}
if ( fearsomeCreature )
{
if ( rand ( ) % 100 < 10 ) //fixed 10%
{
bte . effect = Bonus : : FEAR ;
sendAndApply ( & bte ) ;
}
}
}
2011-10-09 10:20:23 +03:00
BonusList bl = * ( st - > getBonuses ( Selector : : type ( Bonus : : ENCHANTER ) ) ) ;
int side = gs - > curB - > whatSide ( st - > owner ) ;
if ( bl . size ( ) & & st - > casts & & ! gs - > curB - > enchanterCounter [ side ] )
2011-10-08 16:02:58 +03:00
{
2011-10-09 10:20:23 +03:00
int index = rand ( ) % bl . size ( ) ;
int spellID = bl [ index ] - > subtype ; //spell ID
2011-12-14 00:23:17 +03:00
if ( gs - > curB - > battleCanCastThisSpell ( st - > owner , VLC - > spellh - > spells [ spellID ] , ECastingMode : : ENCHANTER_CASTING ) ) ; //TODO: select another?
2011-10-09 10:20:23 +03:00
{
int spellLeveL = bl [ index ] - > val ; //spell level
const CGHeroInstance * enemyHero = gs - > curB - > getHero ( gs - > curB - > theOtherPlayer ( st - > owner ) ) ;
2011-12-14 00:23:17 +03:00
handleSpellCasting ( spellID , spellLeveL , - 1 , side , st - > owner , NULL , enemyHero , 0 , ECastingMode : : ENCHANTER_CASTING , st ) ;
2011-10-09 10:20:23 +03:00
BattleSetStackProperty ssp ;
ssp . which = BattleSetStackProperty : : ENCHANTER_COUNTER ;
ssp . absolute = false ;
ssp . val = bl [ index ] - > additionalInfo ; //increase cooldown counter
ssp . stackID = st - > ID ;
sendAndApply ( & ssp ) ;
}
2011-10-08 16:02:58 +03:00
}
}
}
2009-03-09 21:40:43 +02:00
void CGameHandler : : handleTimeEvents ( )
{
2009-07-18 00:53:28 +03:00
gs - > map - > events . sort ( evntCmp ) ;
2009-03-09 21:40:43 +02:00
while ( gs - > map - > events . size ( ) & & gs - > map - > events . front ( ) - > firstOccurence + 1 = = gs - > day )
{
CMapEvent * ev = gs - > map - > events . front ( ) ;
2011-12-14 00:23:17 +03:00
for ( int player = 0 ; player < GameConstants : : PLAYER_LIMIT ; player + + )
2009-03-09 21:40:43 +02:00
{
PlayerState * pinfo = gs - > getPlayer ( player ) ;
if ( pinfo //player exists
& & ( ev - > players & 1 < < player ) //event is enabled to this player
& & ( ( ev - > computerAffected & & ! pinfo - > human )
2009-03-14 13:25:25 +02:00
| | ( ev - > humanAffected & & pinfo - > human )
2009-03-09 21:40:43 +02:00
)
2009-03-14 13:25:25 +02:00
)
2009-03-09 21:40:43 +02:00
{
//give resources
SetResources sr ;
sr . player = player ;
2011-07-05 09:14:07 +03:00
sr . res = pinfo - > resources + ev - > resources ;
2009-03-09 21:40:43 +02:00
//prepare dialog
InfoWindow iw ;
iw . player = player ;
iw . text < < ev - > message ;
2011-07-05 09:14:07 +03:00
2009-03-09 21:40:43 +02:00
for ( int i = 0 ; i < ev - > resources . size ( ) ; i + + )
{
if ( ev - > resources [ i ] ) //if resource is changed, we add it to the dialog
2011-07-05 09:14:07 +03:00
iw . components . push_back ( Component ( Component : : RESOURCE , i , ev - > resources [ i ] , 0 ) ) ;
2009-03-09 21:40:43 +02:00
}
2011-07-05 09:14:07 +03:00
2009-03-09 21:40:43 +02:00
if ( iw . components . size ( ) )
{
2011-07-05 09:14:07 +03:00
sr . res . amax ( 0 ) ; // If removing too much resources, adjust the amount so the total doesn't become negative.
2009-03-09 21:40:43 +02:00
sendAndApply ( & sr ) ; //update player resources if changed
}
sendAndApply ( & iw ) ; //show dialog
}
} //PLAYERS LOOP
if ( ev - > nextOccurence )
{
2011-01-01 22:26:39 +02:00
gs - > map - > events . pop_front ( ) ;
2009-03-09 21:40:43 +02:00
ev - > firstOccurence + = ev - > nextOccurence ;
2011-01-15 04:36:10 +02:00
std : : list < ConstTransitivePtr < CMapEvent > > : : iterator it = gs - > map - > events . begin ( ) ;
2011-01-01 22:26:39 +02:00
while ( it ! = gs - > map - > events . end ( ) & & * * it < = * ev )
it + + ;
gs - > map - > events . insert ( it , ev ) ;
2009-03-09 21:40:43 +02:00
}
else
{
delete ev ;
gs - > map - > events . pop_front ( ) ;
}
}
2009-03-27 01:05:40 +02:00
}
2010-08-25 17:57:58 +03:00
void CGameHandler : : handleTownEvents ( CGTownInstance * town , NewTurn & n , std : : map < si32 , std : : map < si32 , si32 > > & newCreas )
2010-08-18 17:24:30 +03:00
{
2011-08-26 23:32:05 +03:00
//TODO event removing desync!!!
2010-08-18 17:24:30 +03:00
town - > events . sort ( evntCmp ) ;
while ( town - > events . size ( ) & & town - > events . front ( ) - > firstOccurence = = gs - > day )
{
ui8 player = town - > tempOwner ;
CCastleEvent * ev = town - > events . front ( ) ;
PlayerState * pinfo = gs - > getPlayer ( player ) ;
if ( pinfo //player exists
& & ( ev - > players & 1 < < player ) //event is enabled to this player
& & ( ( ev - > computerAffected & & ! pinfo - > human )
| | ( ev - > humanAffected & & pinfo - > human ) ) )
{
2011-07-05 09:14:07 +03:00
2010-08-18 17:24:30 +03:00
// dialog
InfoWindow iw ;
iw . player = player ;
iw . text < < ev - > message ;
2011-07-05 09:14:07 +03:00
if ( ev - > resources . nonZero ( ) )
{
TResources was = n . res [ player ] ;
n . res [ player ] + = ev - > resources ;
n . res [ player ] . amax ( 0 ) ;
for ( int i = 0 ; i < ev - > resources . size ( ) ; i + + )
if ( ev - > resources [ i ] & & pinfo - > resources [ i ] ! = n . res [ player ] [ i ] ) //if resource had changed, we add it to the dialog
iw . components . push_back ( Component ( Component : : RESOURCE , i , n . res [ player ] [ i ] - was [ i ] , 0 ) ) ;
}
2010-08-18 17:24:30 +03:00
for ( std : : set < si32 > : : iterator i = ev - > buildings . begin ( ) ; i ! = ev - > buildings . end ( ) ; i + + )
if ( ! vstd : : contains ( town - > builtBuildings , * i ) )
{
buildStructure ( town - > id , * i , true ) ;
iw . components . push_back ( Component ( Component : : BUILDING , town - > subID , * i , 0 ) ) ;
}
2010-08-25 17:57:58 +03:00
for ( si32 i = 0 ; i < ev - > creatures . size ( ) ; i + + ) //creature growths
2010-08-18 17:24:30 +03:00
{
if ( town - > creatureDwelling ( i ) & & ev - > creatures [ i ] ) //there is dwelling
{
2010-08-25 17:57:58 +03:00
newCreas [ town - > id ] [ i ] + = ev - > creatures [ i ] ;
2010-08-18 17:24:30 +03:00
iw . components . push_back ( Component ( Component : : CREATURE ,
town - > creatures [ i ] . second . back ( ) , ev - > creatures [ i ] , 0 ) ) ;
}
}
sendAndApply ( & iw ) ; //show dialog
}
if ( ev - > nextOccurence )
2011-01-18 18:24:06 +02:00
{
town - > events . pop_front ( ) ;
2011-01-01 22:26:39 +02:00
2010-08-18 17:24:30 +03:00
ev - > firstOccurence + = ev - > nextOccurence ;
2011-01-01 22:26:39 +02:00
std : : list < CCastleEvent * > : : iterator it = town - > events . begin ( ) ;
while ( it ! = town - > events . end ( ) & & * * it < = * ev )
it + + ;
town - > events . insert ( it , ev ) ;
2010-08-18 17:24:30 +03:00
}
else
{
delete ev ;
town - > events . pop_front ( ) ;
}
}
}
2009-03-27 01:05:40 +02:00
bool CGameHandler : : complain ( const std : : string & problem )
{
sendMessageToAll ( " Server encountered a problem: " + problem ) ;
tlog1 < < problem < < std : : endl ;
return true ;
2009-04-11 04:32:50 +03:00
}
ui32 CGameHandler : : getQueryResult ( ui8 player , int queryID )
{
//TODO: write
return 0 ;
2009-04-12 03:58:41 +03:00
}
2009-09-09 20:49:03 +03:00
void CGameHandler : : showGarrisonDialog ( int upobj , int hid , bool removableUnits , const boost : : function < void ( ) > & cb )
2009-04-12 03:58:41 +03:00
{
ui8 player = getOwner ( hid ) ;
GarrisonDialog gd ;
gd . hid = hid ;
gd . objid = upobj ;
2009-04-12 04:48:50 +03:00
{
boost : : unique_lock < boost : : recursive_mutex > lock ( gsm ) ;
gd . id = QID ;
garrisonCallbacks [ QID ] = cb ;
allowedExchanges [ QID ] = std : : pair < si32 , si32 > ( upobj , hid ) ;
states . addQuery ( player , QID ) ;
QID + + ;
2009-09-09 20:49:03 +03:00
gd . removableUnits = removableUnits ;
2009-04-12 04:48:50 +03:00
sendAndApply ( & gd ) ;
}
2009-04-12 03:58:41 +03:00
}
2010-02-06 15:49:14 +02:00
void CGameHandler : : showThievesGuildWindow ( int requestingObjId )
{
2010-02-07 17:06:14 +02:00
OpenWindow ow ;
ow . window = OpenWindow : : THIEVES_GUILD ;
ow . id1 = requestingObjId ;
sendAndApply ( & ow ) ;
2010-02-06 15:49:14 +02:00
}
2011-09-17 21:15:10 +03:00
bool CGameHandler : : isAllowedArrangePack ( const ArrangeStacks * pack )
{
return isAllowedExchangeForQuery ( pack - > id1 , pack - > id2 ) ;
}
bool CGameHandler : : isAllowedExchangeForQuery ( int id1 , int id2 ) {
boost : : unique_lock < boost : : recursive_mutex > lock ( gsm ) ;
for ( std : : map < ui32 , std : : pair < si32 , si32 > > : : const_iterator i = allowedExchanges . begin ( ) ; i ! = allowedExchanges . end ( ) ; i + + )
if ( ( id1 = = i - > second . first & & id2 = = i - > second . second ) | |
( id2 = = i - > second . first & & id1 = = i - > second . second ) )
return true ;
2011-09-24 04:15:36 +03:00
return false ;
2011-09-17 21:15:10 +03:00
}
2009-04-12 03:58:41 +03:00
bool CGameHandler : : isAllowedExchange ( int id1 , int id2 )
{
if ( id1 = = id2 )
return true ;
2011-09-17 21:15:10 +03:00
if ( isAllowedExchangeForQuery ( id1 , id2 ) )
return true ;
2009-04-12 03:58:41 +03:00
const CGObjectInstance * o1 = getObj ( id1 ) , * o2 = getObj ( id2 ) ;
2012-02-04 19:34:29 +03:00
if ( o1 & & o2 )
2009-04-12 03:58:41 +03:00
{
2012-02-04 19:34:29 +03:00
if ( o1 - > ID = = GameConstants : : TOWNI_TYPE )
{
const CGTownInstance * t = static_cast < const CGTownInstance * > ( o1 ) ;
if ( t - > visitingHero = = o2 | | t - > garrisonHero = = o2 )
return true ;
}
if ( o2 - > ID = = GameConstants : : TOWNI_TYPE )
{
const CGTownInstance * t = static_cast < const CGTownInstance * > ( o2 ) ;
if ( t - > visitingHero = = o1 | | t - > garrisonHero = = o1 )
return true ;
}
if ( o1 - > ID = = GameConstants : : HEROI_TYPE & & o2 - > ID = = GameConstants : : HEROI_TYPE
& & distance ( o1 - > pos , o2 - > pos ) < 2 ) //hero stands on the same tile or on the neighbouring tiles
{
//TODO: it's workaround, we should check if first hero visited second and player hasn't closed exchange window
//(to block moving stacks for free [without visiting] between heroes)
2009-04-12 03:58:41 +03:00
return true ;
2012-02-04 19:34:29 +03:00
}
2009-04-12 03:58:41 +03:00
}
2012-02-04 19:34:29 +03:00
else //not exchanging between heroes, TODO: more sophisticated logic
2009-07-01 10:04:21 +03:00
{
2012-02-04 19:34:29 +03:00
return true ;
2009-07-01 10:04:21 +03:00
}
2009-04-12 03:58:41 +03:00
return false ;
2009-05-12 06:13:07 +03:00
}
2009-07-06 22:41:27 +03:00
void CGameHandler : : objectVisited ( const CGObjectInstance * obj , const CGHeroInstance * h )
{
2011-05-10 01:20:47 +03:00
HeroVisit hv ;
hv . obj = obj ;
hv . hero = h ;
hv . starting = true ;
sendAndApply ( & hv ) ;
2009-07-06 22:41:27 +03:00
obj - > onHeroVisit ( h ) ;
2011-05-10 01:20:47 +03:00
hv . obj = NULL ; //not necessary, moreover may have been deleted in the meantime
hv . starting = false ;
sendAndApply ( & hv ) ;
2009-07-07 06:48:00 +03:00
}
2009-07-26 06:33:13 +03:00
bool CGameHandler : : buildBoat ( ui32 objid )
{
const IShipyard * obj = IShipyard : : castFrom ( getObj ( objid ) ) ;
if ( obj - > state ( ) )
{
complain ( " Cannot build boat in this shipyard! " ) ;
return false ;
}
2011-12-14 00:23:17 +03:00
else if ( obj - > o - > ID = = GameConstants : : TOWNI_TYPE
2009-07-26 06:33:13 +03:00
& & ! vstd : : contains ( ( static_cast < const CGTownInstance * > ( obj ) ) - > builtBuildings , 6 ) )
{
complain ( " Cannot build boat in the town - no shipyard! " ) ;
return false ;
}
//TODO use "real" cost via obj->getBoatCost
if ( getResource ( obj - > o - > tempOwner , 6 ) < 1000 | | getResource ( obj - > o - > tempOwner , 0 ) < 10 )
{
complain ( " Not enough resources to build a boat! " ) ;
return false ;
}
int3 tile = obj - > bestLocation ( ) ;
if ( ! gs - > map - > isInTheMap ( tile ) )
{
complain ( " Cannot find appropriate tile for a boat! " ) ;
return false ;
}
//take boat cost
SetResources sr ;
sr . player = obj - > o - > tempOwner ;
sr . res = gs - > getPlayer ( obj - > o - > tempOwner ) - > resources ;
2011-07-05 09:14:07 +03:00
sr . res [ Res : : WOOD ] - = 10 ;
sr . res [ Res : : GOLD ] - = 1000 ;
2009-07-26 06:33:13 +03:00
sendAndApply ( & sr ) ;
//create boat
NewObject no ;
no . ID = 8 ;
2010-02-13 06:47:31 +02:00
no . subID = obj - > getBoatType ( ) ;
2009-07-26 06:33:13 +03:00
no . pos = tile + int3 ( 1 , 0 , 0 ) ;
sendAndApply ( & no ) ;
2009-07-31 23:10:22 +03:00
return true ;
2009-08-04 02:53:18 +03:00
}
void CGameHandler : : engageIntoBattle ( ui8 player )
{
if ( vstd : : contains ( states . players , player ) )
states . setFlag ( player , & PlayerStatus : : engagedIntoBattle , true ) ;
//notify interfaces
PlayerBlocked pb ;
pb . player = player ;
pb . reason = PlayerBlocked : : UPCOMING_BATTLE ;
sendAndApply ( & pb ) ;
2009-10-04 05:02:45 +03:00
}
2010-01-29 22:52:45 +02:00
void CGameHandler : : winLoseHandle ( ui8 players )
{
2012-02-16 20:10:58 +03:00
2011-12-14 00:23:17 +03:00
for ( size_t i = 0 ; i < GameConstants : : PLAYER_LIMIT ; i + + )
2010-01-29 22:52:45 +02:00
{
if ( players & 1 < < i & & gs - > getPlayer ( i ) )
{
checkLossVictory ( i ) ;
}
}
}
void CGameHandler : : checkLossVictory ( ui8 player )
{
2010-02-01 21:19:42 +02:00
const PlayerState * p = gs - > getPlayer ( player ) ;
if ( p - > status ) //player already won / lost
return ;
2010-01-29 22:52:45 +02:00
int loss = gs - > lossCheck ( player ) ;
int vic = gs - > victoryCheck ( player ) ;
if ( ! loss & & ! vic )
return ;
2012-02-16 20:10:58 +03:00
2010-01-29 22:52:45 +02:00
InfoWindow iw ;
2010-02-02 01:30:03 +02:00
getLossVicMessage ( player , vic ? vic : loss , vic , iw ) ;
2010-01-29 22:52:45 +02:00
sendAndApply ( & iw ) ;
PlayerEndsGame peg ;
peg . player = player ;
peg . victory = vic ;
sendAndApply ( & peg ) ;
2010-08-12 18:54:25 +03:00
if ( vic > 0 ) //one player won -> all enemies lost
2010-02-01 19:07:46 +02:00
{
iw . text . localStrings . front ( ) . second + + ; //message about losing because enemy won first is just after victory message
2010-12-20 23:22:53 +02:00
for ( bmap < ui8 , PlayerState > : : const_iterator i = gs - > players . begin ( ) ; i ! = gs - > players . end ( ) ; i + + )
2010-02-01 19:07:46 +02:00
{
2011-12-14 00:23:17 +03:00
if ( i - > first < GameConstants : : PLAYER_LIMIT & & i - > first ! = player ) //FIXME: skip already eliminated players?
2010-02-01 19:07:46 +02:00
{
iw . player = i - > first ;
sendAndApply ( & iw ) ;
peg . player = i - > first ;
2010-08-13 13:46:08 +03:00
peg . victory = gameState ( ) - > getPlayerRelations ( player , i - > first ) = = 1 ; // ally of winner
2010-02-01 19:07:46 +02:00
sendAndApply ( & peg ) ;
}
}
}
2010-02-01 21:19:42 +02:00
else //player lost -> all his objects become unflagged (neutral)
{
2010-12-20 15:04:24 +02:00
std : : vector < ConstTransitivePtr < CGHeroInstance > > hlp = p - > heroes ;
for ( std : : vector < ConstTransitivePtr < CGHeroInstance > > : : const_iterator i = hlp . begin ( ) ; i ! = hlp . end ( ) ; i + + ) //eliminate heroes
2010-02-02 01:30:03 +02:00
removeObject ( ( * i ) - > id ) ;
2010-12-17 20:47:07 +02:00
for ( std : : vector < ConstTransitivePtr < CGObjectInstance > > : : const_iterator i = gs - > map - > objects . begin ( ) ; i ! = gs - > map - > objects . end ( ) ; i + + ) //unflag objs
2010-02-01 21:19:42 +02:00
{
if ( * i & & ( * i ) - > tempOwner = = player )
2011-12-14 00:23:17 +03:00
setOwner ( ( * * i ) . id , GameConstants : : NEUTRAL_PLAYER ) ;
2010-02-01 21:19:42 +02:00
}
2010-02-02 01:30:03 +02:00
2010-02-07 17:06:14 +02:00
//eliminating one player may cause victory of another:
2011-12-14 00:23:17 +03:00
winLoseHandle ( GameConstants : : ALL_PLAYERS & ~ ( 1 < < player ) ) ;
2012-02-16 20:10:58 +03:00
}
2010-02-01 19:07:46 +02:00
2010-01-29 22:52:45 +02:00
if ( vic )
2012-02-16 20:10:58 +03:00
{
2010-01-29 22:52:45 +02:00
end2 = true ;
2010-08-20 16:34:39 +03:00
if ( gs - > campaign )
2012-02-16 20:10:58 +03:00
{
2010-09-04 17:49:15 +03:00
std : : vector < CGHeroInstance * > hes ;
BOOST_FOREACH ( CGHeroInstance * ghi , gs - > map - > heroes )
2012-02-16 20:10:58 +03:00
{
2012-01-19 17:33:22 +03:00
if ( ghi - > tempOwner = = vic )
2012-02-16 20:10:58 +03:00
{
2010-09-04 17:49:15 +03:00
hes . push_back ( ghi ) ;
}
2012-02-16 20:10:58 +03:00
}
2010-09-04 17:49:15 +03:00
gs - > campaign - > mapConquered ( hes ) ;
2010-08-20 16:34:39 +03:00
UpdateCampaignState ucs ;
ucs . camp = gs - > campaign ;
sendAndApply ( & ucs ) ;
2012-02-16 20:10:58 +03:00
}
2010-08-20 16:34:39 +03:00
}
2010-01-29 22:52:45 +02:00
}
2010-02-02 01:30:03 +02:00
void CGameHandler : : getLossVicMessage ( ui8 player , ui8 standard , bool victory , InfoWindow & out ) const
2010-01-29 22:52:45 +02:00
{
2010-10-31 00:53:41 +03:00
// const PlayerState *p = gs->getPlayer(player);
2010-02-01 19:07:46 +02:00
// if(!p->human)
// return; //AI doesn't need text info of loss
2010-01-29 22:52:45 +02:00
out . player = player ;
if ( victory )
{
2010-02-02 01:30:03 +02:00
if ( standard < 0 ) //not std loss
2010-01-29 22:52:45 +02:00
{
switch ( gs - > map - > victoryCondition . condition )
{
2011-12-14 00:23:17 +03:00
case EVictoryConditionType : : ARTIFACT :
2010-01-29 22:52:45 +02:00
out . text . addTxt ( MetaString : : GENERAL_TXT , 280 ) ; //Congratulations! You have found the %s, and can claim victory!
2010-01-30 14:46:15 +02:00
out . text . addReplacement ( MetaString : : ART_NAMES , gs - > map - > victoryCondition . ID ) ; //artifact name
2010-01-29 22:52:45 +02:00
break ;
2011-12-14 00:23:17 +03:00
case EVictoryConditionType : : GATHERTROOP :
2010-01-29 22:52:45 +02:00
out . text . addTxt ( MetaString : : GENERAL_TXT , 276 ) ; //Congratulations! You have over %d %s in your armies. Your enemies have no choice but to bow down before your power!
out . text . addReplacement ( gs - > map - > victoryCondition . count ) ;
out . text . addReplacement ( MetaString : : CRE_PL_NAMES , gs - > map - > victoryCondition . ID ) ;
break ;
2011-12-14 00:23:17 +03:00
case EVictoryConditionType : : GATHERRESOURCE :
2010-01-29 22:52:45 +02:00
out . text . addTxt ( MetaString : : GENERAL_TXT , 278 ) ; //Congratulations! You have collected over %d %s in your treasury. Victory is yours!
out . text . addReplacement ( gs - > map - > victoryCondition . count ) ;
out . text . addReplacement ( MetaString : : RES_NAMES , gs - > map - > victoryCondition . ID ) ;
break ;
2011-12-14 00:23:17 +03:00
case EVictoryConditionType : : BUILDCITY :
2010-01-29 22:52:45 +02:00
out . text . addTxt ( MetaString : : GENERAL_TXT , 282 ) ; //Congratulations! You have successfully upgraded your town, and can claim victory!
break ;
2011-12-14 00:23:17 +03:00
case EVictoryConditionType : : BUILDGRAIL :
2010-01-29 22:52:45 +02:00
out . text . addTxt ( MetaString : : GENERAL_TXT , 284 ) ; //Congratulations! You have constructed a permanent home for the Grail, and can claim victory!
break ;
2011-12-14 00:23:17 +03:00
case EVictoryConditionType : : BEATHERO :
2010-01-29 22:52:45 +02:00
{
out . text . addTxt ( MetaString : : GENERAL_TXT , 252 ) ; //Congratulations! You have completed your quest to defeat the enemy hero %s. Victory is yours!
const CGHeroInstance * h = dynamic_cast < const CGHeroInstance * > ( gs - > map - > victoryCondition . obj ) ;
assert ( h ) ;
out . text . addReplacement ( h - > name ) ;
}
break ;
2011-12-14 00:23:17 +03:00
case EVictoryConditionType : : CAPTURECITY :
2010-01-29 22:52:45 +02:00
{
out . text . addTxt ( MetaString : : GENERAL_TXT , 249 ) ; //Congratulations! You captured %s, and are victorious!
const CGTownInstance * t = dynamic_cast < const CGTownInstance * > ( gs - > map - > victoryCondition . obj ) ;
assert ( t ) ;
out . text . addReplacement ( t - > name ) ;
}
break ;
2011-12-14 00:23:17 +03:00
case EVictoryConditionType : : BEATMONSTER :
2010-01-29 22:52:45 +02:00
out . text . addTxt ( MetaString : : GENERAL_TXT , 286 ) ; //Congratulations! You have completed your quest to kill the fearsome beast, and can claim victory!
break ;
2011-12-14 00:23:17 +03:00
case EVictoryConditionType : : TAKEDWELLINGS :
2010-01-29 22:52:45 +02:00
out . text . addTxt ( MetaString : : GENERAL_TXT , 288 ) ; //Congratulations! Your flag flies on the dwelling of every creature. Victory is yours!
break ;
2011-12-14 00:23:17 +03:00
case EVictoryConditionType : : TAKEMINES :
2010-01-29 22:52:45 +02:00
out . text . addTxt ( MetaString : : GENERAL_TXT , 290 ) ; //Congratulations! Your flag flies on every mine. Victory is yours!
break ;
2011-12-14 00:23:17 +03:00
case EVictoryConditionType : : TRANSPORTITEM :
2010-01-29 22:52:45 +02:00
out . text . addTxt ( MetaString : : GENERAL_TXT , 292 ) ; //Congratulations! You have reached your destination, precious cargo intact, and can claim victory!
break ;
}
}
else
{
2010-02-02 01:30:03 +02:00
out . text . addTxt ( MetaString : : GENERAL_TXT , 659 ) ; //Congratulations! You have reached your destination, precious cargo intact, and can claim victory!
2010-01-29 22:52:45 +02:00
}
}
else
{
2010-02-02 01:30:03 +02:00
if ( standard < 0 ) //not std loss
2010-01-29 22:52:45 +02:00
{
switch ( gs - > map - > lossCondition . typeOfLossCon )
{
2011-12-14 00:23:17 +03:00
case ELossConditionType : : LOSSCASTLE :
2010-01-29 22:52:45 +02:00
{
out . text . addTxt ( MetaString : : GENERAL_TXT , 251 ) ; //The town of %s has fallen - all is lost!
const CGTownInstance * t = dynamic_cast < const CGTownInstance * > ( gs - > map - > lossCondition . obj ) ;
assert ( t ) ;
out . text . addReplacement ( t - > name ) ;
}
break ;
2011-12-14 00:23:17 +03:00
case ELossConditionType : : LOSSHERO :
2010-01-29 22:52:45 +02:00
{
out . text . addTxt ( MetaString : : GENERAL_TXT , 253 ) ; //The hero, %s, has suffered defeat - your quest is over!
const CGHeroInstance * h = dynamic_cast < const CGHeroInstance * > ( gs - > map - > lossCondition . obj ) ;
assert ( h ) ;
out . text . addReplacement ( h - > name ) ;
}
break ;
2011-12-14 00:23:17 +03:00
case ELossConditionType : : TIMEEXPIRES :
2010-01-29 22:52:45 +02:00
out . text . addTxt ( MetaString : : GENERAL_TXT , 254 ) ; //Alas, time has run out on your quest. All is lost.
break ;
}
}
2010-02-02 01:30:03 +02:00
else if ( standard = = 2 )
{
out . text . addTxt ( MetaString : : GENERAL_TXT , 7 ) ; //%s, your heroes abandon you, and you are banished from this land.
out . text . addReplacement ( MetaString : : COLOR , player ) ;
out . components . push_back ( Component ( Component : : FLAG , player , 0 , 0 ) ) ;
}
2010-01-29 22:52:45 +02:00
else //lost all towns and heroes
{
2010-01-30 17:00:05 +02:00
out . text . addTxt ( MetaString : : GENERAL_TXT , 660 ) ; //All your forces have been defeated, and you are banished from this land!
2010-01-29 22:52:45 +02:00
}
}
2010-02-21 17:03:30 +02:00
}
bool CGameHandler : : dig ( const CGHeroInstance * h )
{
2010-12-17 20:47:07 +02:00
for ( std : : vector < ConstTransitivePtr < CGObjectInstance > > : : const_iterator i = gs - > map - > objects . begin ( ) ; i ! = gs - > map - > objects . end ( ) ; i + + ) //unflag objs
2010-02-21 17:03:30 +02:00
{
2010-03-02 22:12:32 +02:00
if ( * i & & ( * i ) - > ID = = 124 & & ( * i ) - > pos = = h - > getPosition ( ) )
2010-02-21 17:03:30 +02:00
{
complain ( " Cannot dig - there is already a hole under the hero! " ) ;
return false ;
}
}
2012-01-03 04:55:26 +03:00
if ( h - > diggingStatus ( ) ! = CGHeroInstance : : CAN_DIG ) //checks for terrain and movement
COMPLAIN_RETF ( " Hero cannot dig (error code %d)! " , h - > diggingStatus ( ) ) ;
//create a hole
2010-02-21 17:03:30 +02:00
NewObject no ;
no . ID = 124 ;
2010-02-24 15:03:36 +02:00
no . pos = h - > getPosition ( ) ;
no . subID = getTile ( no . pos ) - > tertype ;
2010-02-21 17:03:30 +02:00
sendAndApply ( & no ) ;
2012-01-03 04:55:26 +03:00
//take MPs
2010-02-24 15:03:36 +02:00
SetMovePoints smp ;
smp . hid = h - > id ;
smp . val = 0 ;
sendAndApply ( & smp ) ;
InfoWindow iw ;
iw . player = h - > tempOwner ;
if ( gs - > map - > grailPos = = h - > getPosition ( ) )
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 58 ) ; //"Congratulations! After spending many hours digging here, your hero has uncovered the "
2010-03-01 20:22:22 +02:00
iw . text . addTxt ( MetaString : : ART_NAMES , 2 ) ;
2010-02-24 15:03:36 +02:00
iw . soundID = soundBase : : ULTIMATEARTIFACT ;
2010-12-26 16:34:11 +02:00
giveHeroNewArtifact ( h , VLC - > arth - > artifacts [ 2 ] , - 1 ) ; //give grail
2010-03-01 20:22:22 +02:00
sendAndApply ( & iw ) ;
2012-02-20 00:03:43 +03:00
iw . soundID = soundBase : : invalid ;
2010-03-01 20:22:22 +02:00
iw . text . clear ( ) ;
iw . text . addTxt ( MetaString : : ART_DESCR , 2 ) ;
sendAndApply ( & iw ) ;
2010-02-24 15:03:36 +02:00
}
else
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 59 ) ; //"Nothing here. \n Where could it be?"
iw . soundID = soundBase : : Dig ;
2010-03-01 20:22:22 +02:00
sendAndApply ( & iw ) ;
2010-02-24 15:03:36 +02:00
}
2010-02-21 17:03:30 +02:00
return true ;
2010-02-23 17:39:31 +02:00
}
2011-07-16 09:42:44 +03:00
void CGameHandler : : attackCasting ( const BattleAttack & bat , Bonus : : BonusType attackMode , const CStack * attacker )
2010-02-23 17:39:31 +02:00
{
2011-07-16 09:42:44 +03:00
if ( attacker - > hasBonusOfType ( attackMode ) )
2010-02-23 17:39:31 +02:00
{
2011-04-25 12:03:13 +03:00
std : : set < ui32 > spellsToCast ;
2011-09-06 16:59:26 +03:00
TBonusListPtr spells = attacker - > getBonuses ( Selector : : type ( attackMode ) ) ;
2011-06-25 16:53:15 +03:00
BOOST_FOREACH ( const Bonus * sf , * spells )
2010-02-23 17:39:31 +02:00
{
2011-04-25 12:03:13 +03:00
spellsToCast . insert ( sf - > subtype ) ;
}
BOOST_FOREACH ( ui32 spellID , spellsToCast )
{
const CStack * oneOfAttacked = NULL ;
2011-07-02 19:49:22 +03:00
for ( int g = 0 ; g < bat . bsa . size ( ) ; + + g )
2010-02-23 17:39:31 +02:00
{
2011-07-02 19:49:22 +03:00
if ( bat . bsa [ g ] . newAmount > 0 & & ! bat . bsa [ g ] . isSecondary ( ) ) //apply effects only to first target stack if it's alive
2010-02-23 17:39:31 +02:00
{
2011-04-25 12:03:13 +03:00
oneOfAttacked = gs - > curB - > getStack ( bat . bsa [ g ] . stackAttacked ) ;
break ;
2010-02-23 17:39:31 +02:00
}
2011-04-25 12:03:13 +03:00
}
bool castMe = false ;
int meleeRanged ;
if ( oneOfAttacked = = NULL ) //all attacked creatures have been killed
return ;
int spellLevel = 0 ;
2011-09-06 16:59:26 +03:00
TBonusListPtr spellsByType = attacker - > getBonuses ( Selector : : typeSubtype ( attackMode , spellID ) ) ;
2011-06-25 16:53:15 +03:00
BOOST_FOREACH ( const Bonus * sf , * spellsByType )
2011-04-25 12:03:13 +03:00
{
2011-12-14 00:23:17 +03:00
vstd : : amax ( spellLevel , sf - > additionalInfo % 1000 ) ; //pick highest level
2011-04-25 12:03:13 +03:00
meleeRanged = sf - > additionalInfo / 1000 ;
if ( meleeRanged = = 0 | | ( meleeRanged = = 1 & & bat . shot ( ) ) | | ( meleeRanged = = 2 & & ! bat . shot ( ) ) )
castMe = true ;
}
2011-07-16 09:42:44 +03:00
int chance = attacker - > valOfBonuses ( ( Selector : : typeSubtype ( attackMode , spellID ) ) ) ;
2011-12-14 00:23:17 +03:00
vstd : : amin ( chance , 100 ) ;
2011-04-25 12:03:13 +03:00
int destination = oneOfAttacked - > position ;
2011-02-20 20:32:39 +02:00
2011-04-25 12:03:13 +03:00
const CSpell * spell = VLC - > spellh - > spells [ spellID ] ;
2011-12-14 00:23:17 +03:00
if ( gs - > curB - > battleCanCastThisSpellHere ( attacker - > owner , spell , ECastingMode : : AFTER_ATTACK_CASTING , oneOfAttacked - > position ) ! = ESpellCastProblem : : OK )
2011-04-25 12:03:13 +03:00
continue ;
2010-02-23 17:39:31 +02:00
2011-04-25 12:03:13 +03:00
//check if spell should be casted (probability handling)
if ( rand ( ) % 100 > = chance )
continue ;
2011-07-16 09:42:44 +03:00
//casting //TODO: check if spell can be blocked or target is immune
2011-07-08 17:54:20 +03:00
if ( castMe ) //stacks use 0 spell power. If needed, default = 3 or custom value is used
2011-12-14 00:23:17 +03:00
handleSpellCasting ( spellID , spellLevel , destination , ! attacker - > attackerOwned , attacker - > owner , NULL , NULL , 0 , ECastingMode : : AFTER_ATTACK_CASTING , attacker ) ;
2010-02-23 17:39:31 +02:00
}
}
2011-07-16 09:42:44 +03:00
}
void CGameHandler : : handleAttackBeforeCasting ( const BattleAttack & bat )
{
const CStack * attacker = gs - > curB - > getStack ( bat . stackAttacking ) ;
attackCasting ( bat , Bonus : : SPELL_BEFORE_ATTACK , attacker ) ; //no detah stare / acid bretah needed?
}
void CGameHandler : : handleAfterAttackCasting ( const BattleAttack & bat )
{
const CStack * attacker = gs - > curB - > getStack ( bat . stackAttacking ) ;
attackCasting ( bat , Bonus : : SPELL_AFTER_ATTACK , attacker ) ;
2011-04-23 20:10:54 +03:00
if ( attacker - > hasBonusOfType ( Bonus : : DEATH_STARE ) ) // spell id 79
{
int staredCreatures = 0 ;
double mean = attacker - > count * attacker - > valOfBonuses ( Bonus : : DEATH_STARE , 0 ) / 100 ;
if ( mean > = 1 )
{
boost : : poisson_distribution < int , double > p ( ( int ) mean ) ;
boost : : mt19937 rng ;
2011-04-30 21:16:58 +03:00
boost : : variate_generator < boost : : mt19937 & , boost : : poisson_distribution < int , double > > dice ( rng , p ) ;
2011-04-23 20:10:54 +03:00
staredCreatures + = dice ( ) ;
}
if ( ( ( int ) ( mean * 100 ) ) < rand ( ) % 100 ) //fractional chance for one last kill
+ + staredCreatures ;
staredCreatures + = attacker - > type - > level * attacker - > valOfBonuses ( Bonus : : DEATH_STARE , 1 ) ;
if ( staredCreatures )
{
2011-09-24 04:15:36 +03:00
if ( bat . bsa . size ( ) & & bat . bsa [ 0 ] . newAmount > 0 ) //TODO: death stare was not originally available for multiple-hex attacks, but...
2011-04-23 20:10:54 +03:00
handleSpellCasting ( 79 , 0 , gs - > curB - > getStack ( bat . bsa [ 0 ] . stackAttacked ) - > position ,
2011-12-14 00:23:17 +03:00
! attacker - > attackerOwned , attacker - > owner , NULL , NULL , staredCreatures , ECastingMode : : AFTER_ATTACK_CASTING , attacker ) ;
2011-04-23 20:10:54 +03:00
}
}
2011-05-13 12:02:16 +03:00
int acidDamage = 0 ;
2011-09-06 16:59:26 +03:00
TBonusListPtr acidBreath = attacker - > getBonuses ( Selector : : type ( Bonus : : ACID_BREATH ) ) ;
2011-06-25 16:53:15 +03:00
BOOST_FOREACH ( const Bonus * b , * acidBreath )
2011-05-13 12:02:16 +03:00
{
if ( b - > additionalInfo > rand ( ) % 100 )
acidDamage + = b - > val ;
}
if ( acidDamage )
{
handleSpellCasting ( 81 , 0 , gs - > curB - > getStack ( bat . bsa [ 0 ] . stackAttacked ) - > position ,
! attacker - > attackerOwned , attacker - > owner , NULL , NULL ,
2011-12-14 00:23:17 +03:00
acidDamage * attacker - > count , ECastingMode : : AFTER_ATTACK_CASTING , attacker ) ;
2011-05-13 12:02:16 +03:00
}
2010-02-23 17:39:31 +02:00
}
2010-03-11 01:16:30 +02:00
bool CGameHandler : : castSpell ( const CGHeroInstance * h , int spellID , const int3 & pos )
{
2010-12-19 16:39:56 +02:00
const CSpell * s = VLC - > spellh - > spells [ spellID ] ;
2010-03-11 01:16:30 +02:00
int cost = h - > getSpellCost ( s ) ;
int schoolLevel = h - > getSpellSchoolLevel ( s ) ;
if ( ! h - > canCastThisSpell ( s ) )
COMPLAIN_RET ( " Hero cannot cast this spell! " ) ;
if ( h - > mana < cost )
COMPLAIN_RET ( " Hero doesn't have enough spell points to cast this spell! " ) ;
if ( s - > combatSpell )
COMPLAIN_RET ( " This function can be used only for adventure map spells! " ) ;
2010-05-16 16:42:19 +03:00
AdvmapSpellCast asc ;
asc . caster = h ;
asc . spellID = spellID ;
sendAndApply ( & asc ) ;
2010-03-21 00:17:19 +02:00
using namespace Spells ;
2010-03-11 01:16:30 +02:00
switch ( spellID )
{
2010-03-21 00:17:19 +02:00
case SUMMON_BOAT : //Summon Boat
2010-03-11 01:16:30 +02:00
{
//check if spell works at all
if ( rand ( ) % 100 > = s - > powers [ schoolLevel ] ) //power is % chance of success
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 336 ) ; //%s tried to summon a boat, but failed.
iw . text . addReplacement ( h - > name ) ;
sendAndApply ( & iw ) ;
return true ; //TODO? or should it be false? request was correct and realized, but spell failed...
}
//try to find unoccupied boat to summon
const CGBoat * nearest = NULL ;
double dist = 0 ;
int3 summonPos = h - > bestLocation ( ) ;
if ( summonPos . x < 0 )
COMPLAIN_RET ( " There is no water tile available! " ) ;
BOOST_FOREACH ( const CGObjectInstance * obj , gs - > map - > objects )
{
if ( obj & & obj - > ID = = 8 )
{
const CGBoat * b = static_cast < const CGBoat * > ( obj ) ;
if ( b - > hero ) continue ; //we're looking for unoccupied boat
double nDist = distance ( b - > pos , h - > getPosition ( ) ) ;
if ( ! nearest | | nDist < dist ) //it's first boat or closer than previous
{
nearest = b ;
dist = nDist ;
}
}
}
if ( nearest ) //we found boat to summon
{
ChangeObjPos cop ;
cop . objid = nearest - > id ;
cop . nPos = summonPos + int3 ( 1 , 0 , 0 ) ; ;
cop . flags = 1 ;
sendAndApply ( & cop ) ;
}
else if ( schoolLevel < 2 ) //none or basic level -> cannot create boat :(
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 335 ) ; //There are no boats to summon.
sendAndApply ( & iw ) ;
}
else //create boat
{
NewObject no ;
no . ID = 8 ;
no . subID = h - > getBoatType ( ) ;
no . pos = summonPos + int3 ( 1 , 0 , 0 ) ; ;
sendAndApply ( & no ) ;
}
break ;
}
2010-03-21 00:17:19 +02:00
case SCUTTLE_BOAT : //Scuttle Boat
{
//check if spell works at all
if ( rand ( ) % 100 > = s - > powers [ schoolLevel ] ) //power is % chance of success
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 337 ) ; //%s tried to scuttle the boat, but failed
iw . text . addReplacement ( h - > name ) ;
sendAndApply ( & iw ) ;
return true ; //TODO? or should it be false? request was correct and realized, but spell failed...
}
2010-06-01 00:14:15 +03:00
if ( ! gs - > map - > isInTheMap ( pos ) )
COMPLAIN_RET ( " Invalid dst tile for scuttle! " ) ;
2010-03-21 00:17:19 +02:00
//TODO: test range, visibility
const TerrainTile * t = & gs - > map - > getTile ( pos ) ;
if ( ! t - > visitableObjects . size ( ) | | t - > visitableObjects . back ( ) - > ID ! = 8 )
COMPLAIN_RET ( " There is no boat to scuttle! " ) ;
RemoveObject ro ;
ro . id = t - > visitableObjects . back ( ) - > id ;
sendAndApply ( & ro ) ;
break ;
}
case DIMENSION_DOOR : //Dimension Door
{
const TerrainTile * dest = getTile ( pos ) ;
const TerrainTile * curr = getTile ( h - > getSightCenter ( ) ) ;
if ( ! dest )
COMPLAIN_RET ( " Destination tile doesn't exist! " ) ;
if ( ! h - > movement )
COMPLAIN_RET ( " Hero needs movement points to cast Dimension Door! " ) ;
2011-01-20 21:57:12 +02:00
if ( h - > getBonusesCount ( Bonus : : SPELL_EFFECT , Spells : : DIMENSION_DOOR ) > = s - > powers [ schoolLevel ] ) //limit casts per turn
2010-03-21 00:17:19 +02:00
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 338 ) ; //%s is not skilled enough to cast this spell again today.
iw . text . addReplacement ( h - > name ) ;
sendAndApply ( & iw ) ;
break ;
}
GiveBonus gb ;
gb . id = h - > id ;
2011-01-20 21:57:12 +02:00
gb . bonus = Bonus ( Bonus : : ONE_DAY , Bonus : : NONE , Bonus : : SPELL_EFFECT , 0 , Spells : : DIMENSION_DOOR ) ;
2010-03-21 00:17:19 +02:00
sendAndApply ( & gb ) ;
if ( ! dest - > isClear ( curr ) ) //wrong dest tile
{
InfoWindow iw ;
iw . player = h - > tempOwner ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 70 ) ; //Dimension Door failed!
sendAndApply ( & iw ) ;
break ;
}
2010-06-01 00:14:15 +03:00
//we need obtain guard pos before moving hero, otherwise we get nothing, because tile will be "unguarded" by hero
int3 guardPos = gs - > guardingCreaturePosition ( pos ) ;
2010-03-21 00:17:19 +02:00
TryMoveHero tmh ;
tmh . id = h - > id ;
tmh . movePoints = std : : max < int > ( 0 , h - > movement - 300 ) ;
tmh . result = TryMoveHero : : TELEPORTATION ;
tmh . start = h - > pos ;
2010-06-01 00:14:15 +03:00
tmh . end = pos + h - > getVisitableOffset ( ) ;
2010-03-21 00:17:19 +02:00
getTilesInRange ( tmh . fowRevealed , pos , h - > getSightRadious ( ) , h - > tempOwner , 1 ) ;
sendAndApply ( & tmh ) ;
2010-06-01 00:14:15 +03:00
tryAttackingGuard ( guardPos , h ) ;
2010-03-21 00:17:19 +02:00
}
break ;
2010-05-15 18:00:19 +03:00
case FLY : //Fly
{
int subtype = schoolLevel > = 2 ? 1 : 2 ; //adv or expert
GiveBonus gb ;
gb . id = h - > id ;
2011-01-20 21:57:12 +02:00
gb . bonus = Bonus ( Bonus : : ONE_DAY , Bonus : : FLYING_MOVEMENT , Bonus : : SPELL_EFFECT , 0 , Spells : : FLY , subtype ) ;
2010-05-15 18:00:19 +03:00
sendAndApply ( & gb ) ;
}
break ;
case WATER_WALK : //Water Walk
{
int subtype = schoolLevel > = 2 ? 1 : 2 ; //adv or expert
GiveBonus gb ;
gb . id = h - > id ;
2011-01-20 21:57:12 +02:00
gb . bonus = Bonus ( Bonus : : ONE_DAY , Bonus : : WATER_WALKING , Bonus : : SPELL_EFFECT , 0 , Spells : : FLY , subtype ) ;
2010-05-15 18:00:19 +03:00
sendAndApply ( & gb ) ;
}
break ;
2010-08-04 16:41:01 +03:00
case TOWN_PORTAL : //Town Portal
{
2011-01-15 19:30:46 +02:00
if ( ! gs - > map - > isInTheMap ( pos ) )
COMPLAIN_RET ( " Destination tile not present! " )
TerrainTile tile = gs - > map - > getTile ( pos ) ;
2011-12-14 00:23:17 +03:00
if ( tile . visitableObjects . empty ( ) | | tile . visitableObjects . back ( ) - > ID ! = GameConstants : : TOWNI_TYPE )
2011-01-15 19:30:46 +02:00
COMPLAIN_RET ( " Town not found for Town Portal! " ) ;
CGTownInstance * town = static_cast < CGTownInstance * > ( tile . visitableObjects . back ( ) ) ;
if ( town - > tempOwner ! = h - > tempOwner )
COMPLAIN_RET ( " Can't teleport to another player! " ) ;
if ( town - > visitingHero )
COMPLAIN_RET ( " Can't teleport to occupied town! " ) ;
if ( h - > getSpellSchoolLevel ( s ) < 2 )
{
double dist = town - > pos . dist2d ( h - > pos ) ;
int nearest = town - > id ; //nearest town's ID
BOOST_FOREACH ( const CGTownInstance * currTown , gs - > getPlayer ( h - > tempOwner ) - > towns )
{
double curDist = currTown - > pos . dist2d ( h - > pos ) ;
if ( nearest = = - 1 | | curDist < dist )
{
nearest = town - > id ;
dist = curDist ;
}
}
if ( town - > id ! = nearest )
COMPLAIN_RET ( " This hero can only teleport to nearest town! " )
}
if ( h - > visitedTown )
2011-06-27 19:03:03 +03:00
stopHeroVisitCastle ( h - > visitedTown - > id , h - > id ) ;
2011-07-28 03:38:19 +03:00
if ( moveHero ( h - > id , town - > visitablePos ( ) + h - > getVisitableOffset ( ) , 1 ) )
2011-01-15 19:30:46 +02:00
heroVisitCastle ( town - > id , h - > id ) ;
2010-08-04 16:41:01 +03:00
}
break ;
2010-03-21 00:17:19 +02:00
case VISIONS : //Visions
case VIEW_EARTH : //View Earth
case DISGUISE : //Disguise
case VIEW_AIR : //View Air
2010-03-11 01:16:30 +02:00
default :
COMPLAIN_RET ( " This spell is not implemented yet! " ) ;
2010-08-04 16:41:01 +03:00
break ;
2010-03-11 01:16:30 +02:00
}
SetMana sm ;
sm . hid = h - > id ;
sm . val = h - > mana - cost ;
sendAndApply ( & sm ) ;
return true ;
2010-05-08 01:10:32 +03:00
}
2010-05-27 00:59:58 +03:00
void CGameHandler : : visitObjectOnTile ( const TerrainTile & t , const CGHeroInstance * h )
{
//to prevent self-visiting heroes on space press
if ( t . visitableObjects . back ( ) ! = h )
objectVisited ( t . visitableObjects . back ( ) , h ) ;
else if ( t . visitableObjects . size ( ) > 1 )
objectVisited ( * ( t . visitableObjects . end ( ) - 2 ) , h ) ;
2010-06-01 00:14:15 +03:00
}
bool CGameHandler : : tryAttackingGuard ( const int3 & guardPos , const CGHeroInstance * h )
{
if ( ! gs - > map - > isInTheMap ( guardPos ) )
return false ;
const TerrainTile & guardTile = gs - > map - > terrain [ guardPos . x ] [ guardPos . y ] [ guardPos . z ] ;
objectVisited ( guardTile . visitableObjects . back ( ) , h ) ;
visitObjectAfterVictory = true ;
2010-07-20 09:05:45 +03:00
return true ;
}
bool CGameHandler : : sacrificeCreatures ( const IMarket * market , const CGHeroInstance * hero , TSlot slot , ui32 count )
{
2010-11-27 03:46:19 +02:00
int oldCount = hero - > getStackCount ( slot ) ;
2010-07-20 09:05:45 +03:00
if ( oldCount < count )
COMPLAIN_RET ( " Not enough creatures to sacrifice! " )
else if ( oldCount = = count & & hero - > Slots ( ) . size ( ) = = 1 & & hero - > needsLastStack ( ) )
COMPLAIN_RET ( " Cannot sacrifice last creature! " ) ;
int crid = hero - > getStack ( slot ) . type - > idNumber ;
2010-11-27 22:17:28 +02:00
changeStackCount ( StackLocation ( hero , slot ) , - count ) ;
2010-07-20 09:05:45 +03:00
int dump , exp ;
2011-12-14 00:23:17 +03:00
market - > getOffer ( crid , 0 , dump , exp , EMarketMode : : CREATURE_EXP ) ;
2010-07-20 09:05:45 +03:00
exp * = count ;
2011-02-12 20:48:11 +02:00
changePrimSkill ( hero - > id , 4 , hero - > calculateXp ( exp ) ) ;
2010-07-20 09:05:45 +03:00
2010-07-23 15:02:15 +03:00
return true ;
}
2010-12-29 23:04:22 +02:00
bool CGameHandler : : sacrificeArtifact ( const IMarket * m , const CGHeroInstance * hero , int slot )
2010-07-23 15:02:15 +03:00
{
2010-12-29 23:04:22 +02:00
ArtifactLocation al ( hero , slot ) ;
const CArtifactInstance * a = al . getArt ( ) ;
if ( ! a )
2010-07-23 15:02:15 +03:00
COMPLAIN_RET ( " Cannot find artifact to sacrifice! " ) ;
2010-12-29 23:04:22 +02:00
2010-07-23 15:02:15 +03:00
int dmp , expToGive ;
2011-12-14 00:23:17 +03:00
m - > getOffer ( hero - > getArtTypeId ( slot ) , 0 , dmp , expToGive , EMarketMode : : ARTIFACT_EXP ) ;
2010-12-29 23:04:22 +02:00
removeArtifact ( al ) ;
2010-07-23 15:02:15 +03:00
changePrimSkill ( hero - > id , 4 , expToGive ) ;
2010-06-01 00:14:15 +03:00
return true ;
2010-09-04 17:47:39 +03:00
}
2010-11-27 03:46:19 +02:00
2010-12-04 21:15:20 +02:00
void CGameHandler : : makeStackDoNothing ( const CStack * next )
{
BattleAction doNothing ;
doNothing . actionType = 0 ;
doNothing . additionalInfo = 0 ;
doNothing . destinationTile = - 1 ;
doNothing . side = ! next - > attackerOwned ;
doNothing . stackNumber = next - > ID ;
2011-06-11 07:54:41 +03:00
StartAction start_action ( doNothing ) ;
sendAndApply ( & start_action ) ;
sendAndApply ( & end_action ) ;
2010-12-04 21:15:20 +02:00
}
2010-12-06 01:26:56 +02:00
2010-11-27 22:17:28 +02:00
bool CGameHandler : : insertNewStack ( const StackLocation & sl , const CCreature * c , TQuantity count )
2010-11-27 03:46:19 +02:00
{
2010-11-27 22:17:28 +02:00
if ( sl . army - > hasStackAtSlot ( sl . slot ) )
COMPLAIN_RET ( " Slot is already taken! " ) ;
2010-11-27 03:46:19 +02:00
InsertNewStack ins ;
ins . sl = sl ;
2010-12-06 01:10:02 +02:00
ins . stack = CStackBasicDescriptor ( c , count ) ;
2010-11-27 03:46:19 +02:00
sendAndApply ( & ins ) ;
2010-11-27 22:17:28 +02:00
return true ;
2010-11-27 03:46:19 +02:00
}
2010-12-12 01:11:26 +02:00
bool CGameHandler : : eraseStack ( const StackLocation & sl , bool forceRemoval /* = false*/ )
2010-11-27 03:46:19 +02:00
{
2010-11-27 22:17:28 +02:00
if ( ! sl . army - > hasStackAtSlot ( sl . slot ) )
COMPLAIN_RET ( " Cannot find a stack to erase " ) ;
if ( sl . army - > Slots ( ) . size ( ) = = 1 //from the last stack
2010-12-12 01:11:26 +02:00
& & sl . army - > needsLastStack ( ) //that must be left
& & ! forceRemoval ) //ignore above conditions if we are forcing removal
2010-11-27 22:17:28 +02:00
{
COMPLAIN_RET ( " Cannot erase the last stack! " ) ;
}
2010-11-27 03:46:19 +02:00
EraseStack es ;
es . sl = sl ;
sendAndApply ( & es ) ;
2010-11-27 22:17:28 +02:00
return true ;
}
bool CGameHandler : : changeStackCount ( const StackLocation & sl , TQuantity count , bool absoluteValue /*= false*/ )
{
TQuantity currentCount = sl . army - > getStackCount ( sl . slot ) ;
2011-05-30 22:20:14 +03:00
if ( ( absoluteValue & & count < 0 )
| | ( ! absoluteValue & & - count > currentCount ) )
2010-11-27 22:17:28 +02:00
{
COMPLAIN_RET ( " Cannot take more stacks than present! " ) ;
}
2011-05-30 22:20:14 +03:00
if ( ( currentCount = = - count & & ! absoluteValue )
| | ( ! count & & absoluteValue ) )
2010-11-27 22:17:28 +02:00
{
eraseStack ( sl ) ;
}
else
{
ChangeStackCount csc ;
csc . sl = sl ;
csc . count = count ;
csc . absoluteValue = absoluteValue ;
sendAndApply ( & csc ) ;
}
return true ;
}
bool CGameHandler : : addToSlot ( const StackLocation & sl , const CCreature * c , TQuantity count )
{
const CCreature * slotC = sl . army - > getCreature ( sl . slot ) ;
if ( ! slotC ) //slot is empty
insertNewStack ( sl , c , count ) ;
else if ( c = = slotC )
changeStackCount ( sl , count ) ;
else
{
2010-12-13 01:44:16 +02:00
COMPLAIN_RET ( " Cannot add " + c - > namePl + " to slot " + boost : : lexical_cast < std : : string > ( sl . slot ) + " ! " ) ;
2010-11-27 22:17:28 +02:00
}
return true ;
}
2010-12-13 01:44:16 +02:00
void CGameHandler : : tryJoiningArmy ( const CArmedInstance * src , const CArmedInstance * dst , bool removeObjWhenFinished , bool allowMerging )
2010-11-27 22:17:28 +02:00
{
2011-07-18 18:21:16 +03:00
if ( ! src - > canBeMergedWith ( * dst , allowMerging ) )
2010-11-27 22:17:28 +02:00
{
2011-03-08 15:27:32 +02:00
if ( allowMerging ) //do that, add all matching creatures.
{
bool cont = true ;
while ( cont )
{
for ( TSlots : : const_iterator i = src - > stacks . begin ( ) ; i ! = src - > stacks . end ( ) ; i + + ) //while there are unmoved creatures
{
TSlot pos = dst - > getSlotFor ( i - > second - > type ) ;
if ( pos > - 1 )
{
moveStack ( StackLocation ( src , i - > first ) , StackLocation ( dst , pos ) ) ;
cont = true ;
break ; //or iterator crashes
}
cont = false ;
}
}
}
2010-11-27 22:17:28 +02:00
boost : : function < void ( ) > removeOrNot = 0 ;
if ( removeObjWhenFinished )
removeOrNot = boost : : bind ( & IGameCallback : : removeObject , this , src - > id ) ;
showGarrisonDialog ( src - > id , dst - > id , true , removeOrNot ) ; //show garrison window and optionally remove ourselves from map when player ends
}
else //merge
{
2010-12-13 01:44:16 +02:00
moveArmy ( src , dst , allowMerging ) ;
2010-11-27 22:17:28 +02:00
if ( removeObjWhenFinished )
removeObject ( src - > id ) ;
}
2010-11-27 03:46:19 +02:00
}
2010-11-27 22:17:28 +02:00
bool CGameHandler : : moveStack ( const StackLocation & src , const StackLocation & dst , TQuantity count )
2010-11-27 03:46:19 +02:00
{
2010-11-27 22:17:28 +02:00
if ( ! src . army - > hasStackAtSlot ( src . slot ) )
COMPLAIN_RET ( " No stack to move! " ) ;
if ( dst . army - > hasStackAtSlot ( dst . slot ) & & dst . army - > getCreature ( dst . slot ) ! = src . army - > getCreature ( src . slot ) )
COMPLAIN_RET ( " Cannot move: stack of different type at destination pos! " ) ;
if ( count = = - 1 )
{
count = src . army - > getStackCount ( src . slot ) ;
}
if ( src . army ! = dst . army //moving away
& & count = = src . army - > getStackCount ( src . slot ) //all creatures
& & src . army - > Slots ( ) . size ( ) = = 1 //from the last stack
& & src . army - > needsLastStack ( ) ) //that must be left
{
COMPLAIN_RET ( " Cannot move away the alst creature! " ) ;
}
RebalanceStacks rs ;
rs . src = src ;
rs . dst = dst ;
rs . count = count ;
sendAndApply ( & rs ) ;
return true ;
}
bool CGameHandler : : swapStacks ( const StackLocation & sl1 , const StackLocation & sl2 )
{
if ( ! sl1 . army - > hasStackAtSlot ( sl1 . slot ) )
return moveStack ( sl2 , sl1 ) ;
else if ( ! sl2 . army - > hasStackAtSlot ( sl2 . slot ) )
return moveStack ( sl1 , sl2 ) ;
else
{
SwapStacks ss ;
ss . sl1 = sl1 ;
ss . sl2 = sl2 ;
sendAndApply ( & ss ) ;
return true ;
}
2010-12-12 01:11:26 +02:00
}
2010-12-22 22:14:40 +02:00
void CGameHandler : : runBattle ( )
{
assert ( gs - > curB ) ;
//TODO: pre-tactic stuff, call scripts etc.
//tactic round
{
2011-08-26 00:08:53 +03:00
while ( gs - > curB - > tacticDistance & & ! battleResult . get ( ) )
2011-02-12 18:12:48 +02:00
boost : : this_thread : : sleep ( boost : : posix_time : : milliseconds ( 50 ) ) ;
2010-12-22 22:14:40 +02:00
}
//spells opening battle
2011-02-22 13:52:36 +02:00
for ( int i = 0 ; i < ARRAY_COUNT ( gs - > curB - > heroes ) ; + + i )
2010-12-22 22:14:40 +02:00
{
2011-02-22 13:52:36 +02:00
if ( gs - > curB - > heroes [ i ] & & gs - > curB - > heroes [ i ] - > hasBonusOfType ( Bonus : : OPENING_BATTLE_SPELL ) )
2010-12-22 22:14:40 +02:00
{
2011-09-06 16:59:26 +03:00
TBonusListPtr bl = gs - > curB - > heroes [ i ] - > getBonuses ( Selector : : type ( Bonus : : OPENING_BATTLE_SPELL ) ) ;
2011-06-25 16:53:15 +03:00
BOOST_FOREACH ( Bonus * b , * bl )
2011-02-22 13:52:36 +02:00
{
2011-12-14 00:23:17 +03:00
handleSpellCasting ( b - > subtype , 3 , - 1 , 0 , gs - > curB - > heroes [ i ] - > tempOwner , NULL , gs - > curB - > heroes [ 1 - i ] , b - > val , ECastingMode : : HERO_CASTING , NULL ) ;
2011-02-22 13:52:36 +02:00
}
2010-12-22 22:14:40 +02:00
}
}
//main loop
while ( ! battleResult . get ( ) ) //till the end of the battle ;]
{
NEW_ROUND ;
std : : vector < CStack * > & stacks = ( gs - > curB - > stacks ) ;
const BattleInfo & curB = * gs - > curB ;
//stack loop
const CStack * next ;
while ( ! battleResult . get ( ) & & ( next = curB . getNextStack ( ) ) & & next - > willMove ( ) )
{
//check for bad morale => freeze
int nextStackMorale = next - > MoraleVal ( ) ;
if ( nextStackMorale < 0 & &
! ( NBonus : : hasOfType ( gs - > curB - > heroes [ 0 ] , Bonus : : BLOCK_MORALE ) | | NBonus : : hasOfType ( gs - > curB - > heroes [ 1 ] , Bonus : : BLOCK_MORALE ) ) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses)
)
{
if ( rand ( ) % 24 < - 2 * nextStackMorale )
{
//unit loses its turn - empty freeze action
BattleAction ba ;
ba . actionType = BattleAction : : BAD_MORALE ;
ba . additionalInfo = 1 ;
ba . side = ! next - > attackerOwned ;
ba . stackNumber = next - > ID ;
2011-06-11 07:54:41 +03:00
StartAction start_action ( ba ) ;
sendAndApply ( & start_action ) ;
sendAndApply ( & end_action ) ;
2010-12-22 22:14:40 +02:00
checkForBattleEnd ( stacks ) ; //check if this "action" ended the battle (not likely but who knows...)
continue ;
}
}
if ( next - > hasBonusOfType ( Bonus : : ATTACKS_NEAREST_CREATURE ) ) //while in berserk
{
std : : pair < const CStack * , int > attackInfo = curB . getNearestStack ( next , boost : : logic : : indeterminate ) ;
if ( attackInfo . first ! = NULL )
{
BattleAction attack ;
attack . actionType = BattleAction : : WALK_AND_ATTACK ;
attack . side = ! next - > attackerOwned ;
attack . stackNumber = next - > ID ;
attack . additionalInfo = attackInfo . first - > position ;
attack . destinationTile = attackInfo . second ;
makeBattleAction ( attack ) ;
checkForBattleEnd ( stacks ) ;
}
else
{
makeStackDoNothing ( next ) ;
}
continue ;
}
2011-01-07 12:48:31 +02:00
const CGHeroInstance * curOwner = gs - > curB - > battleGetOwner ( next ) ;
2010-12-22 22:14:40 +02:00
2011-09-01 05:48:29 +03:00
if ( ( next - > position < 0 | | next - > getCreature ( ) - > idNumber = = 146 ) //arrow turret or ballista
& & ( ! curOwner | | curOwner - > getSecSkillLevel ( CGHeroInstance : : ARTILLERY ) = = 0 ) ) //hero has no artillery
2010-12-22 22:14:40 +02:00
{
BattleAction attack ;
attack . actionType = BattleAction : : SHOOT ;
attack . side = ! next - > attackerOwned ;
attack . stackNumber = next - > ID ;
for ( int g = 0 ; g < gs - > curB - > stacks . size ( ) ; + + g )
{
2012-02-17 00:19:07 +03:00
if ( gs - > curB - > stacks [ g ] - > owner ! = next - > owner & & gs - > curB - > stacks [ g ] - > isValidTarget ( ) )
2010-12-22 22:14:40 +02:00
{
attack . destinationTile = gs - > curB - > stacks [ g ] - > position ;
break ;
}
}
makeBattleAction ( attack ) ;
checkForBattleEnd ( stacks ) ;
continue ;
}
2010-12-23 22:18:10 +02:00
if ( next - > getCreature ( ) - > idNumber = = 145 & & ( ! curOwner | | curOwner - > getSecSkillLevel ( CGHeroInstance : : BALLISTICS ) = = 0 ) ) //catapult, hero has no ballistics
2010-12-22 22:14:40 +02:00
{
BattleAction attack ;
static const int wallHexes [ ] = { 50 , 183 , 182 , 130 , 62 , 29 , 12 , 95 } ;
attack . destinationTile = wallHexes [ rand ( ) % ARRAY_COUNT ( wallHexes ) ] ;
attack . actionType = BattleAction : : CATAPULT ;
attack . additionalInfo = 0 ;
attack . side = ! next - > attackerOwned ;
attack . stackNumber = next - > ID ;
makeBattleAction ( attack ) ;
continue ;
}
2010-12-23 22:18:10 +02:00
if ( next - > getCreature ( ) - > idNumber = = 147 & & ( ! curOwner | | curOwner - > getSecSkillLevel ( CGHeroInstance : : FIRST_AID ) = = 0 ) ) //first aid tent, hero has no first aid
2010-12-22 22:14:40 +02:00
{
BattleAction heal ;
2011-01-18 20:54:53 +02:00
std : : vector < const CStack * > possibleStacks , secondPriority ;
2010-12-22 22:14:40 +02:00
for ( int v = 0 ; v < gs - > curB - > stacks . size ( ) ; + + v )
{
const CStack * cstack = gs - > curB - > stacks [ v ] ;
2012-02-17 00:19:07 +03:00
if ( cstack - > owner = = next - > owner & & cstack - > firstHPleft < cstack - > MaxHealth ( ) & & cstack - > isValidTarget ( ) ) //it's friendly and not fully healthy
2010-12-22 22:14:40 +02:00
{
2011-01-18 20:54:53 +02:00
if ( cstack - > hasBonusOfType ( Bonus : : SIEGE_WEAPON ) )
secondPriority . push_back ( cstack ) ;
else
possibleStacks . push_back ( cstack ) ;
2010-12-22 22:14:40 +02:00
}
}
2011-01-18 20:54:53 +02:00
if ( possibleStacks . size ( ) = = 0 & & secondPriority . size ( ) = = 0 )
2010-12-22 22:14:40 +02:00
{
//nothing to heal
makeStackDoNothing ( next ) ;
continue ;
}
else
{
//heal random creature
2011-01-18 20:54:53 +02:00
const CStack * toBeHealed = NULL ;
if ( possibleStacks . size ( ) > 0 )
toBeHealed = possibleStacks [ rand ( ) % possibleStacks . size ( ) ] ;
else
toBeHealed = secondPriority [ rand ( ) % secondPriority . size ( ) ] ;
2010-12-22 22:14:40 +02:00
heal . actionType = BattleAction : : STACK_HEAL ;
heal . additionalInfo = 0 ;
heal . destinationTile = toBeHealed - > position ;
heal . side = ! next - > attackerOwned ;
heal . stackNumber = next - > ID ;
makeBattleAction ( heal ) ;
}
continue ;
}
int numberOfAsks = 1 ;
bool breakOuter = false ;
do
{ //ask interface and wait for answer
if ( ! battleResult . get ( ) )
{
2011-10-08 16:02:58 +03:00
stackTurnTrigger ( next ) ; //various effects
2011-12-14 00:23:17 +03:00
if ( vstd : : contains ( next - > state , EBattleStackState : : FEAR ) )
2011-10-08 20:10:43 +03:00
{
makeStackDoNothing ( next ) ; //end immediately if stack was affected by fear
}
else
{
BattleSetActiveStack sas ;
sas . stack = next - > ID ;
sendAndApply ( & sas ) ;
boost : : unique_lock < boost : : mutex > lock ( battleMadeAction . mx ) ;
battleMadeAction . data = false ;
while ( next - > alive ( ) & &
( ! battleMadeAction . data & & ! battleResult . get ( ) ) ) //active stack hasn't made its action and battle is still going
battleMadeAction . cond . wait ( lock ) ;
}
2010-12-22 22:14:40 +02:00
}
if ( battleResult . get ( ) ) //don't touch it, battle could be finished while waiting got action
{
breakOuter = true ;
break ;
}
//we're after action, all results applied
checkForBattleEnd ( stacks ) ; //check if this action ended the battle
//check for good morale
nextStackMorale = next - > MoraleVal ( ) ;
2011-12-14 00:23:17 +03:00
if ( ! vstd : : contains ( next - > state , EBattleStackState : : HAD_MORALE ) //only one extra move per turn possible
& & ! vstd : : contains ( next - > state , EBattleStackState : : DEFENDING )
& & ! vstd : : contains ( next - > state , EBattleStackState : : WAITING )
& & ! vstd : : contains ( next - > state , EBattleStackState : : FEAR )
2010-12-22 22:14:40 +02:00
& & next - > alive ( )
& & nextStackMorale > 0
& & ! ( NBonus : : hasOfType ( gs - > curB - > heroes [ 0 ] , Bonus : : BLOCK_MORALE ) | | NBonus : : hasOfType ( gs - > curB - > heroes [ 1 ] , Bonus : : BLOCK_MORALE ) ) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses
)
{
if ( rand ( ) % 24 < nextStackMorale ) //this stack hasn't got morale this turn
2012-01-26 19:48:53 +03:00
{
BattleTriggerEffect bte ;
bte . stackID = next - > ID ;
bte . effect = Bonus : : MORALE ;
bte . val = 1 ;
bte . additionalInfo = 0 ;
sendAndApply ( & bte ) ; //play animation
+ + numberOfAsks ; //move this stack once more
}
2010-12-22 22:14:40 +02:00
}
- - numberOfAsks ;
} while ( numberOfAsks > 0 ) ;
if ( breakOuter )
{
break ;
}
}
}
endBattle ( gs - > curB - > tile , gs - > curB - > heroes [ 0 ] , gs - > curB - > heroes [ 1 ] ) ;
}
2010-12-26 16:34:11 +02:00
void CGameHandler : : giveHeroArtifact ( const CGHeroInstance * h , const CArtifactInstance * a , int pos )
{
assert ( a - > artType ) ;
ArtifactLocation al ;
al . hero = h ;
int slot = - 1 ;
if ( pos < 0 )
{
if ( pos = = - 2 )
slot = a - > firstAvailableSlot ( h ) ;
else
slot = a - > firstBackpackSlot ( h ) ;
}
else
{
slot = pos ;
}
al . slot = slot ;
if ( slot < 0 | | ! a - > canBePutAt ( al ) )
{
complain ( " Cannot put artifact in that slot! " ) ;
return ;
}
putArtifact ( al , a ) ;
}
void CGameHandler : : putArtifact ( const ArtifactLocation & al , const CArtifactInstance * a )
{
PutArtifact pa ;
pa . art = a ;
pa . al = al ;
sendAndApply ( & pa ) ;
}
void CGameHandler : : giveHeroNewArtifact ( const CGHeroInstance * h , const CArtifact * artType , int pos )
{
2011-01-18 20:56:14 +02:00
CArtifactInstance * a = NULL ;
2011-01-28 04:16:38 +02:00
if ( ! artType - > constituents )
2012-01-30 19:07:52 +03:00
{
if ( vstd : : contains ( VLC - > arth - > creatureArtifacts , artType - > id ) )
a = new CCreatureArtifactInstance ( ) ;
else
a = new CArtifactInstance ( ) ;
}
2011-01-18 20:56:14 +02:00
else
2012-01-30 19:07:52 +03:00
{
2011-01-18 20:56:14 +02:00
a = new CCombinedArtifactInstance ( ) ;
2012-01-30 19:07:52 +03:00
}
2011-01-15 04:17:56 +02:00
a - > artType = artType ; //*NOT* via settype -> all bonus-related stuff must be done by NewArtifact apply
2010-12-26 16:34:11 +02:00
NewArtifact na ;
na . art = a ;
2011-01-15 04:17:56 +02:00
sendAndApply ( & na ) ; // -> updates a!!!, will create a on other machines
2010-12-26 16:34:11 +02:00
giveHeroArtifact ( h , a , pos ) ;
}
2011-03-05 18:38:22 +02:00
void CGameHandler : : setBattleResult ( int resultType , int victoriusSide )
{
2011-08-26 00:08:53 +03:00
if ( battleResult . get ( ) )
{
complain ( " There is already set result? " ) ;
return ;
}
2011-03-05 18:38:22 +02:00
BattleResult * br = new BattleResult ;
br - > result = resultType ;
br - > winner = victoriusSide ; //surrendering side loses
gs - > curB - > calculateCasualties ( br - > casualties ) ;
battleResult . set ( br ) ;
}
2011-05-22 21:46:52 +03:00
void CGameHandler : : commitPackage ( CPackForClient * pack )
{
sendAndApply ( pack ) ;
}
2011-08-26 23:32:05 +03:00
void CGameHandler : : spawnWanderingMonsters ( int creatureID )
{
std : : vector < int3 > : : iterator tile ;
std : : vector < int3 > tiles ;
getFreeTiles ( tiles ) ;
2012-01-19 17:33:22 +03:00
ui32 amount = tiles . size ( ) / 200 ; //Chance is 0.5% for each tile
2011-08-26 23:32:05 +03:00
std : : random_shuffle ( tiles . begin ( ) , tiles . end ( ) , p_myrandom ) ;
tlog5 < < " Spawning wandering monsters. Found " < < tiles . size ( ) < < " free tiles. Creature type: " < < creatureID < < std : : endl ;
const CCreature * cre = VLC - > creh - > creatures [ creatureID ] ;
for ( int i = 0 ; i < amount ; + + i )
{
tile = tiles . begin ( ) ;
tlog5 < < " \t Spawning monster at " < < * tile < < std : : endl ;
putNewMonster ( creatureID , cre - > getRandomAmount ( std : : rand ) , * tile ) ;
tiles . erase ( tile ) ; //not use it again
}
}
2011-09-06 12:59:06 +03:00
2010-12-12 01:11:26 +02:00
CasualtiesAfterBattle : : CasualtiesAfterBattle ( const CArmedInstance * army , BattleInfo * bat )
{
int color = army - > tempOwner ;
if ( color = = 254 )
2011-12-14 00:23:17 +03:00
color = GameConstants : : NEUTRAL_PLAYER ;
2010-12-12 01:11:26 +02:00
BOOST_FOREACH ( CStack * st , bat - > stacks )
{
2011-12-14 00:23:17 +03:00
if ( vstd : : contains ( st - > state , EBattleStackState : : SUMMONED ) ) //don't take into account summoned stacks
2010-12-12 01:11:26 +02:00
continue ;
if ( st - > owner = = color & & ! army - > slotEmpty ( st - > slot ) & & st - > count < army - > getStackCount ( st - > slot ) )
{
StackLocation sl ( army , st - > slot ) ;
if ( st - > alive ( ) )
newStackCounts . push_back ( std : : pair < StackLocation , int > ( sl , st - > count ) ) ;
else
newStackCounts . push_back ( std : : pair < StackLocation , int > ( sl , 0 ) ) ;
}
}
}
void CasualtiesAfterBattle : : takeFromArmy ( CGameHandler * gh )
{
BOOST_FOREACH ( TStackAndItsNewCount & ncount , newStackCounts )
{
if ( ncount . second > 0 )
gh - > changeStackCount ( ncount . first , ncount . second , true ) ;
else
gh - > eraseStack ( ncount . first , true ) ;
}
2011-05-30 22:20:14 +03:00
}