2017-07-13 10:26:03 +02: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
*
*/
2014-03-17 22:51:07 +03:00
# include "StdInc.h"
# include "../lib/filesystem/Filesystem.h"
2016-01-26 15:51:38 +02:00
# include "../lib/filesystem/FileInfo.h"
2014-03-17 22:51:07 +03:00
# include "../lib/int3.h"
# include "../lib/mapping/CCampaignHandler.h"
# include "../lib/StartInfo.h"
# include "../lib/CModHandler.h"
# include "../lib/CArtHandler.h"
# include "../lib/CBuildingHandler.h"
# include "../lib/CHeroHandler.h"
2018-03-02 12:22:51 +02:00
# include "../lib/spells/AbilityCaster.h"
# include "../lib/spells/BonusCaster.h"
2015-02-02 10:25:26 +02:00
# include "../lib/spells/CSpellHandler.h"
2015-09-16 09:50:33 +02:00
# include "../lib/spells/ISpellMechanics.h"
2023-03-19 02:44:10 +02:00
# include "../lib/spells/ObstacleCasterProxy.h"
2017-07-20 06:08:49 +02:00
# include "../lib/spells/Problem.h"
2014-03-17 22:51:07 +03:00
# include "../lib/CGeneralTextHandler.h"
# include "../lib/CTownHandler.h"
# include "../lib/CCreatureHandler.h"
# include "../lib/CGameState.h"
2017-03-17 17:48:44 +02:00
# include "../lib/CStack.h"
2023-03-15 21:34:29 +02:00
# include "../lib/GameSettings.h"
2017-06-24 16:42:05 +02:00
# include "../lib/battle/BattleInfo.h"
2014-03-17 22:51:07 +03:00
# include "../lib/CondSh.h"
2023-02-12 09:23:39 +02:00
# include "ServerNetPackVisitors.h"
2014-03-17 22:51:07 +03:00
# include "../lib/VCMI_Lib.h"
# include "../lib/mapping/CMap.h"
2017-07-20 06:08:49 +02:00
# include "../lib/mapping/CMapService.h"
2016-09-10 02:32:40 +02:00
# include "../lib/rmg/CMapGenOptions.h"
2014-03-17 22:51:07 +03:00
# include "../lib/VCMIDirs.h"
# include "../lib/ScopeGuard.h"
2014-06-05 23:51:24 +03:00
# include "../lib/CSoundBase.h"
2023-01-09 01:17:37 +02:00
# include "../lib/TerrainHandler.h"
2014-03-17 22:51:07 +03:00
# include "CGameHandler.h"
2023-03-27 11:51:19 +02:00
# include "ServerSpellCastEnvironment.h"
2014-03-17 22:51:07 +03:00
# include "CVCMIServer.h"
# include "../lib/CCreatureSet.h"
# include "../lib/CThreadHelper.h"
# include "../lib/GameConstants.h"
# include "../lib/registerTypes/RegisterTypes.h"
2016-09-10 02:32:40 +02:00
# include "../lib/serializer/CTypeList.h"
# include "../lib/serializer/Connection.h"
2018-08-13 23:48:00 +02:00
# include "../lib/serializer/Cast.h"
2020-02-12 19:12:12 +02:00
# include "../lib/serializer/JsonSerializer.h"
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
# include "../lib/ScriptHandler.h"
2023-03-25 21:29:33 +02:00
# include "vstd/CLoggerBase.h"
# include <memory>
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
# include <vcmi/events/EventBus.h>
# include <vcmi/events/GenericEvents.h>
# include <vcmi/events/AdventureEvents.h>
2014-03-17 22:51:07 +03:00
# ifndef _MSC_VER
# include <boost/thread/xtime.hpp>
# endif
2016-10-12 17:16:26 +02:00
# define COMPLAIN_RET_IF(cond, txt) do {if (cond){complain(txt); return;}} while(0)
# define COMPLAIN_RET_FALSE_IF(cond, txt) do {if (cond){complain(txt); return false;}} while(0)
2014-03-17 22:51:07 +03:00
# define COMPLAIN_RET(txt) {complain(txt); return false;}
# define COMPLAIN_RETF(txt, FORMAT) {complain(boost::str(boost::format(txt) % FORMAT)); return false;}
2016-11-27 18:13:40 +02:00
CondSh < bool > battleMadeAction ( false ) ;
2014-03-17 22:51:07 +03:00
CondSh < BattleResult * > battleResult ( nullptr ) ;
template < typename T > class CApplyOnGH ;
class CBaseForGHApply
{
public :
2023-02-12 09:23:39 +02:00
virtual bool applyOnGH ( CGameHandler * gh , CGameState * gs , void * pack ) const = 0 ;
2014-03-17 22:51:07 +03:00
virtual ~ CBaseForGHApply ( ) { }
template < typename U > static CBaseForGHApply * getApplier ( const U * t = nullptr )
{
2017-07-16 11:58:05 +02:00
return new CApplyOnGH < U > ( ) ;
2014-03-17 22:51:07 +03:00
}
} ;
template < typename T > class CApplyOnGH : public CBaseForGHApply
{
public :
2023-02-12 09:23:39 +02:00
bool applyOnGH ( CGameHandler * gh , CGameState * gs , void * pack ) const override
2014-03-17 22:51:07 +03:00
{
T * ptr = static_cast < T * > ( pack ) ;
2018-01-25 19:21:34 +02:00
try
{
2023-02-12 09:23:39 +02:00
ApplyGhNetPackVisitor applier ( * gh , * gs ) ;
ptr - > visit ( applier ) ;
return applier . getResult ( ) ;
2018-01-25 19:21:34 +02:00
}
catch ( ExceptionNotAllowedAction & e )
{
2020-10-01 10:38:06 +02:00
( void ) e ;
2018-01-25 19:21:34 +02:00
return false ;
}
catch ( . . . )
{
throw ;
}
2014-03-17 22:51:07 +03:00
}
} ;
2015-10-28 22:53:44 +02:00
template < >
2014-03-17 22:51:07 +03:00
class CApplyOnGH < CPack > : public CBaseForGHApply
{
public :
2023-02-12 09:23:39 +02:00
bool applyOnGH ( CGameHandler * gh , CGameState * gs , void * pack ) const override
2014-03-17 22:51:07 +03:00
{
2016-08-30 00:11:54 +02:00
logGlobal - > error ( " Cannot apply on GH plain CPack! " ) ;
2014-03-17 22:51:07 +03:00
assert ( 0 ) ;
return false ;
}
} ;
static inline double distance ( int3 a , int3 b )
{
2016-10-12 17:16:26 +02:00
return std : : sqrt ( ( double ) ( a . x - b . x ) * ( a . x - b . x ) + ( a . y - b . y ) * ( a . y - b . y ) ) ;
2014-03-17 22:51:07 +03:00
}
static void giveExp ( BattleResult & r )
{
2016-10-12 17:16:26 +02:00
if ( r . winner > 1 )
2016-01-25 14:36:34 +02:00
{
// draw
return ;
}
2014-03-17 22:51:07 +03:00
r . exp [ 0 ] = 0 ;
r . exp [ 1 ] = 0 ;
2016-10-12 17:16:26 +02:00
for ( auto i = r . casualties [ ! r . winner ] . begin ( ) ; i ! = r . casualties [ ! r . winner ] . end ( ) ; i + + )
2014-03-17 22:51:07 +03:00
{
2023-05-01 00:20:01 +02:00
r . exp [ r . winner ] + = VLC - > creh - > objects . at ( i - > first ) - > valOfBonuses ( BonusType : : STACK_HEALTH ) * i - > second ;
2014-03-17 22:51:07 +03:00
}
}
2017-07-01 10:34:00 +02:00
static void summonGuardiansHelper ( std : : vector < BattleHex > & output , const BattleHex & targetPosition , ui8 side , bool targetIsTwoHex ) //return hexes for summoning two hex monsters in output, target = unit to guard
2017-01-26 22:24:01 +02:00
{
2017-01-29 12:50:37 +02:00
int x = targetPosition . getX ( ) ;
int y = targetPosition . getY ( ) ;
2017-07-01 10:34:00 +02:00
const bool targetIsAttacker = side = = BattleSide : : ATTACKER ;
2017-01-26 22:24:01 +02:00
if ( targetIsAttacker ) //handle front guardians, TODO: should we handle situation when units start battle near opposite side of the battlefield? Cannot happen in normal H3...
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : RIGHT , false ) . cloneInDirection ( BattleHex : : EDir : : RIGHT , false ) , output ) ;
2017-01-26 22:24:01 +02:00
else
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : LEFT , false ) . cloneInDirection ( BattleHex : : EDir : : LEFT , false ) , output ) ;
2017-01-26 22:24:01 +02:00
//guardian spawn locations for four default position cases for attacker and defender, non-default starting location for att and def is handled in first two if's
2017-01-29 12:50:37 +02:00
if ( targetIsAttacker & & ( ( y % 2 = = 0 ) | | ( x > 1 ) ) )
2017-01-26 22:24:01 +02:00
{
2017-01-29 12:50:37 +02:00
if ( targetIsTwoHex & & ( y % 2 = = 1 ) & & ( x = = 2 ) ) //handle exceptional case
2017-01-26 22:24:01 +02:00
{
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : TOP_RIGHT , false ) , output ) ;
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : BOTTOM_RIGHT , false ) , output ) ;
2017-01-26 22:24:01 +02:00
}
else
{ //add back-side guardians for two-hex target, side guardians for one-hex
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( targetIsTwoHex ? BattleHex : : EDir : : TOP_LEFT : BattleHex : : EDir : : TOP_RIGHT , false ) , output ) ;
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( targetIsTwoHex ? BattleHex : : EDir : : BOTTOM_LEFT : BattleHex : : EDir : : BOTTOM_RIGHT , false ) , output ) ;
2017-01-26 22:24:01 +02:00
2017-01-29 12:50:37 +02:00
if ( ! targetIsTwoHex & & x > 2 ) //back guard for one-hex
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : LEFT , false ) , output ) ;
2017-01-26 22:24:01 +02:00
else if ( targetIsTwoHex ) //front-side guardians for two-hex target
{
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : RIGHT , false ) . cloneInDirection ( BattleHex : : EDir : : TOP_RIGHT , false ) , output ) ;
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : RIGHT , false ) . cloneInDirection ( BattleHex : : EDir : : BOTTOM_RIGHT , false ) , output ) ;
2017-01-29 12:50:37 +02:00
if ( x > 3 ) //back guard for two-hex
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : LEFT , false ) . cloneInDirection ( BattleHex : : EDir : : LEFT , false ) , output ) ;
2017-01-26 22:24:01 +02:00
}
}
}
2017-01-29 12:50:37 +02:00
else if ( ! targetIsAttacker & & ( ( y % 2 = = 1 ) | | ( x < GameConstants : : BFIELD_WIDTH - 2 ) ) )
2017-01-26 22:24:01 +02:00
{
2017-01-29 12:50:37 +02:00
if ( targetIsTwoHex & & ( y % 2 = = 0 ) & & ( x = = GameConstants : : BFIELD_WIDTH - 3 ) ) //handle exceptional case... equivalent for above for defender side
2017-01-26 22:24:01 +02:00
{
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : TOP_LEFT , false ) , output ) ;
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : BOTTOM_LEFT , false ) , output ) ;
2017-01-26 22:24:01 +02:00
}
else
{
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( targetIsTwoHex ? BattleHex : : EDir : : TOP_RIGHT : BattleHex : : EDir : : TOP_LEFT , false ) , output ) ;
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( targetIsTwoHex ? BattleHex : : EDir : : BOTTOM_RIGHT : BattleHex : : EDir : : BOTTOM_LEFT , false ) , output ) ;
2017-01-26 22:24:01 +02:00
2017-01-29 12:50:37 +02:00
if ( ! targetIsTwoHex & & x < GameConstants : : BFIELD_WIDTH - 3 )
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : RIGHT , false ) , output ) ;
2017-01-26 22:24:01 +02:00
else if ( targetIsTwoHex )
{
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : LEFT , false ) . cloneInDirection ( BattleHex : : EDir : : TOP_LEFT , false ) , output ) ;
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : LEFT , false ) . cloneInDirection ( BattleHex : : EDir : : BOTTOM_LEFT , false ) , output ) ;
2017-01-29 12:50:37 +02:00
if ( x < GameConstants : : BFIELD_WIDTH - 4 )
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : RIGHT , false ) . cloneInDirection ( BattleHex : : EDir : : RIGHT , false ) , output ) ;
2017-01-26 22:24:01 +02:00
}
}
}
2017-01-29 12:50:37 +02:00
else if ( ! targetIsAttacker & & y % 2 = = 0 )
2017-01-26 22:24:01 +02:00
{
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : LEFT , false ) . cloneInDirection ( BattleHex : : EDir : : TOP_LEFT , false ) , output ) ;
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : LEFT , false ) . cloneInDirection ( BattleHex : : EDir : : BOTTOM_LEFT , false ) , output ) ;
2017-01-26 22:24:01 +02:00
}
2017-01-29 12:50:37 +02:00
else if ( targetIsAttacker & & y % 2 = = 1 )
2017-01-26 22:24:01 +02:00
{
2017-05-29 09:33:34 +02:00
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : RIGHT , false ) . cloneInDirection ( BattleHex : : EDir : : TOP_RIGHT , false ) , output ) ;
BattleHex : : checkAndPush ( targetPosition . cloneInDirection ( BattleHex : : EDir : : RIGHT , false ) . cloneInDirection ( BattleHex : : EDir : : BOTTOM_RIGHT , false ) , output ) ;
2017-01-26 22:24:01 +02:00
}
}
2014-03-17 22:51:07 +03:00
PlayerStatus PlayerStatuses : : operator [ ] ( PlayerColor player )
{
boost : : unique_lock < boost : : mutex > l ( mx ) ;
2016-10-12 17:16:26 +02:00
if ( players . find ( player ) ! = players . end ( ) )
2014-03-17 22:51:07 +03:00
{
return players . at ( player ) ;
}
else
{
throw std : : runtime_error ( " No such player! " ) ;
}
}
void PlayerStatuses : : addPlayer ( PlayerColor player )
{
boost : : unique_lock < boost : : mutex > l ( mx ) ;
players [ player ] ;
}
bool PlayerStatuses : : checkFlag ( PlayerColor player , bool PlayerStatus : : * flag )
{
boost : : unique_lock < boost : : mutex > l ( mx ) ;
2016-10-12 17:16:26 +02:00
if ( players . find ( player ) ! = players . end ( ) )
2014-03-17 22:51:07 +03:00
{
return players [ player ] . * flag ;
}
else
{
throw std : : runtime_error ( " No such player! " ) ;
}
}
void PlayerStatuses : : setFlag ( PlayerColor player , bool PlayerStatus : : * flag , bool val )
{
boost : : unique_lock < boost : : mutex > l ( mx ) ;
2016-10-12 17:16:26 +02:00
if ( players . find ( player ) ! = players . end ( ) )
2014-03-17 22:51:07 +03:00
{
players [ player ] . * flag = val ;
}
else
{
throw std : : runtime_error ( " No such player! " ) ;
}
cv . notify_all ( ) ;
}
template < typename T >
void callWith ( std : : vector < T > args , std : : function < void ( T ) > fun , ui32 which )
{
fun ( args [ which ] ) ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const Services * CGameHandler : : services ( ) const
{
return VLC ;
}
const CGameHandler : : BattleCb * CGameHandler : : battle ( ) const
{
return this ;
}
const CGameHandler : : GameCb * CGameHandler : : game ( ) const
{
return this ;
}
vstd : : CLoggerBase * CGameHandler : : logger ( ) const
{
return logGlobal ;
}
events : : EventBus * CGameHandler : : eventBus ( ) const
{
return serverEventBus . get ( ) ;
}
2014-03-17 22:51:07 +03:00
void CGameHandler : : levelUpHero ( const CGHeroInstance * hero , SecondarySkill skill )
{
changeSecSkill ( hero , skill , 1 , 0 ) ;
expGiven ( hero ) ;
}
void CGameHandler : : levelUpHero ( const CGHeroInstance * hero )
{
// required exp for at least 1 lvl-up hasn't been reached
2016-10-12 17:16:26 +02:00
if ( ! hero - > gainsLevel ( ) )
2014-03-17 22:51:07 +03:00
{
return ;
}
2014-04-10 20:11:09 +03:00
// give primary skill
2023-01-02 13:27:03 +02:00
logGlobal - > trace ( " %s got level %d " , hero - > getNameTranslated ( ) , hero - > level ) ;
2016-08-23 18:12:10 +02:00
auto primarySkill = hero - > nextPrimarySkill ( getRandomGenerator ( ) ) ;
2014-03-17 22:51:07 +03:00
SetPrimSkill sps ;
sps . id = hero - > id ;
2014-04-10 20:11:09 +03:00
sps . which = primarySkill ;
2014-03-17 22:51:07 +03:00
sps . abs = false ;
sps . val = 1 ;
sendAndApply ( & sps ) ;
2016-09-07 23:24:05 +02:00
PrepareHeroLevelUp pre ;
2018-03-10 21:19:55 +02:00
pre . heroId = hero - > id ;
2016-09-07 23:24:05 +02:00
sendAndApply ( & pre ) ;
2014-03-17 22:51:07 +03:00
HeroLevelUp hlu ;
2018-03-10 21:19:55 +02:00
hlu . player = hero - > tempOwner ;
hlu . heroId = hero - > id ;
2014-04-10 20:11:09 +03:00
hlu . primskill = primarySkill ;
2016-09-07 23:24:05 +02:00
hlu . skills = pre . skills ;
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( hlu . skills . size ( ) = = 0 )
2014-03-17 22:51:07 +03:00
{
sendAndApply ( & hlu ) ;
levelUpHero ( hero ) ;
}
2016-10-12 17:16:26 +02:00
else if ( hlu . skills . size ( ) = = 1 )
2014-03-17 22:51:07 +03:00
{
sendAndApply ( & hlu ) ;
2016-09-07 23:24:05 +02:00
levelUpHero ( hero , pre . skills . front ( ) ) ;
2014-03-17 22:51:07 +03:00
}
2016-10-12 17:16:26 +02:00
else if ( hlu . skills . size ( ) > 1 )
2014-03-17 22:51:07 +03:00
{
2018-03-10 21:19:55 +02:00
auto levelUpQuery = std : : make_shared < CHeroLevelUpDialogQuery > ( this , hlu , hero ) ;
2014-03-17 22:51:07 +03:00
hlu . queryID = levelUpQuery - > queryID ;
queries . addQuery ( levelUpQuery ) ;
sendAndApply ( & hlu ) ;
//level up will be called on query reply
}
}
void CGameHandler : : levelUpCommander ( const CCommanderInstance * c , int skill )
{
SetCommanderProperty scp ;
auto hero = dynamic_cast < const CGHeroInstance * > ( c - > armyObj ) ;
if ( hero )
scp . heroid = hero - > id ;
else
{
complain ( " Commander is not led by hero! " ) ;
return ;
}
scp . accumulatedBonus . subtype = 0 ;
scp . accumulatedBonus . additionalInfo = 0 ;
2023-05-01 00:20:01 +02:00
scp . accumulatedBonus . duration = BonusDuration : : PERMANENT ;
2014-03-17 22:51:07 +03:00
scp . accumulatedBonus . turnsRemain = 0 ;
2023-05-01 00:20:01 +02:00
scp . accumulatedBonus . source = BonusSource : : COMMANDER ;
scp . accumulatedBonus . valType = BonusValueType : : BASE_NUMBER ;
2014-03-17 22:51:07 +03:00
if ( skill < = ECommander : : SPELL_POWER )
{
scp . which = SetCommanderProperty : : BONUS ;
auto difference = [ ] ( std : : vector < std : : vector < ui8 > > skillLevels , std : : vector < ui8 > secondarySkills , int skill ) - > int
{
int s = std : : min ( skill , ( int ) ECommander : : SPELL_POWER ) ; //spell power level controls also casts and resistance
return skillLevels . at ( skill ) . at ( secondarySkills . at ( s ) ) - ( secondarySkills . at ( s ) ? skillLevels . at ( skill ) . at ( secondarySkills . at ( s ) - 1 ) : 0 ) ;
} ;
switch ( skill )
{
case ECommander : : ATTACK :
2023-05-01 00:20:01 +02:00
scp . accumulatedBonus . type = BonusType : : PRIMARY_SKILL ;
2014-03-17 22:51:07 +03:00
scp . accumulatedBonus . subtype = PrimarySkill : : ATTACK ;
break ;
case ECommander : : DEFENSE :
2023-05-01 00:20:01 +02:00
scp . accumulatedBonus . type = BonusType : : PRIMARY_SKILL ;
2014-03-17 22:51:07 +03:00
scp . accumulatedBonus . subtype = PrimarySkill : : DEFENSE ;
break ;
case ECommander : : HEALTH :
2023-05-01 00:20:01 +02:00
scp . accumulatedBonus . type = BonusType : : STACK_HEALTH ;
scp . accumulatedBonus . valType = BonusValueType : : PERCENT_TO_BASE ;
2014-03-17 22:51:07 +03:00
break ;
case ECommander : : DAMAGE :
2023-05-01 00:20:01 +02:00
scp . accumulatedBonus . type = BonusType : : CREATURE_DAMAGE ;
2014-03-17 22:51:07 +03:00
scp . accumulatedBonus . subtype = 0 ;
2023-05-01 00:20:01 +02:00
scp . accumulatedBonus . valType = BonusValueType : : PERCENT_TO_BASE ;
2014-03-17 22:51:07 +03:00
break ;
case ECommander : : SPEED :
2023-05-01 00:20:01 +02:00
scp . accumulatedBonus . type = BonusType : : STACKS_SPEED ;
2014-03-17 22:51:07 +03:00
break ;
case ECommander : : SPELL_POWER :
2023-05-01 00:20:01 +02:00
scp . accumulatedBonus . type = BonusType : : MAGIC_RESISTANCE ;
2014-03-17 22:51:07 +03:00
scp . accumulatedBonus . val = difference ( VLC - > creh - > skillLevels , c - > secondarySkills , ECommander : : RESISTANCE ) ;
sendAndApply ( & scp ) ; //additional pack
2023-05-01 00:20:01 +02:00
scp . accumulatedBonus . type = BonusType : : CREATURE_SPELL_POWER ;
2014-03-17 22:51:07 +03:00
scp . accumulatedBonus . val = difference ( VLC - > creh - > skillLevels , c - > secondarySkills , ECommander : : SPELL_POWER ) * 100 ; //like hero with spellpower = ability level
sendAndApply ( & scp ) ; //additional pack
2023-05-01 00:20:01 +02:00
scp . accumulatedBonus . type = BonusType : : CASTS ;
2014-03-17 22:51:07 +03:00
scp . accumulatedBonus . val = difference ( VLC - > creh - > skillLevels , c - > secondarySkills , ECommander : : CASTS ) ;
sendAndApply ( & scp ) ; //additional pack
2023-05-01 00:20:01 +02:00
scp . accumulatedBonus . type = BonusType : : CREATURE_ENCHANT_POWER ; //send normally
2014-03-17 22:51:07 +03:00
break ;
}
scp . accumulatedBonus . val = difference ( VLC - > creh - > skillLevels , c - > secondarySkills , skill ) ;
sendAndApply ( & scp ) ;
scp . which = SetCommanderProperty : : SECONDARY_SKILL ;
scp . additionalInfo = skill ;
scp . amount = c - > secondarySkills . at ( skill ) + 1 ;
sendAndApply ( & scp ) ;
}
else if ( skill > = 100 )
{
scp . which = SetCommanderProperty : : SPECIAL_SKILL ;
scp . accumulatedBonus = * VLC - > creh - > skillRequirements . at ( skill - 100 ) . first ;
scp . additionalInfo = skill ; //unnormalized
sendAndApply ( & scp ) ;
}
expGiven ( hero ) ;
}
void CGameHandler : : levelUpCommander ( const CCommanderInstance * c )
{
if ( ! c - > gainsLevel ( ) )
{
return ;
}
CommanderLevelUp clu ;
auto hero = dynamic_cast < const CGHeroInstance * > ( c - > armyObj ) ;
2018-03-10 21:19:55 +02:00
if ( hero )
{
clu . heroId = hero - > id ;
clu . player = hero - > tempOwner ;
}
2014-03-17 22:51:07 +03:00
else
{
complain ( " Commander is not led by hero! " ) ;
return ;
}
//picking sec. skills for choice
for ( int i = 0 ; i < = ECommander : : SPELL_POWER ; + + i )
{
if ( c - > secondarySkills . at ( i ) < ECommander : : MAX_SKILL_LEVEL )
clu . skills . push_back ( i ) ;
}
int i = 100 ;
for ( auto specialSkill : VLC - > creh - > skillRequirements )
{
if ( c - > secondarySkills . at ( specialSkill . second . first ) = = ECommander : : MAX_SKILL_LEVEL
& & c - > secondarySkills . at ( specialSkill . second . second ) = = ECommander : : MAX_SKILL_LEVEL
& & ! vstd : : contains ( c - > specialSKills , i ) )
clu . skills . push_back ( i ) ;
+ + i ;
}
2020-10-01 10:38:06 +02:00
int skillAmount = static_cast < int > ( clu . skills . size ( ) ) ;
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( ! skillAmount )
2014-03-17 22:51:07 +03:00
{
sendAndApply ( & clu ) ;
levelUpCommander ( c ) ;
}
2016-10-12 17:16:26 +02:00
else if ( skillAmount = = 1 | | hero - > tempOwner = = PlayerColor : : NEUTRAL ) //choose skill automatically
2014-03-17 22:51:07 +03:00
{
sendAndApply ( & clu ) ;
2016-09-07 23:54:26 +02:00
levelUpCommander ( c , * RandomGeneratorUtil : : nextItem ( clu . skills , getRandomGenerator ( ) ) ) ;
2014-03-17 22:51:07 +03:00
}
2016-10-12 17:16:26 +02:00
else if ( skillAmount > 1 ) //apply and ask for secondary skill
2014-03-17 22:51:07 +03:00
{
2018-03-10 21:19:55 +02:00
auto commanderLevelUp = std : : make_shared < CCommanderLevelUpDialogQuery > ( this , clu , hero ) ;
2014-03-17 22:51:07 +03:00
clu . queryID = commanderLevelUp - > queryID ;
queries . addQuery ( commanderLevelUp ) ;
sendAndApply ( & clu ) ;
}
}
void CGameHandler : : expGiven ( const CGHeroInstance * hero )
{
2016-10-12 17:16:26 +02:00
if ( hero - > gainsLevel ( ) )
2014-03-17 22:51:07 +03:00
levelUpHero ( hero ) ;
2016-10-12 17:16:26 +02:00
else if ( hero - > commander & & hero - > commander - > gainsLevel ( ) )
2014-03-17 22:51:07 +03:00
levelUpCommander ( hero - > commander ) ;
2016-10-12 17:16:26 +02:00
//if (hero->commander && hero->level > hero->commander->level && hero->commander->gainsLevel())
2014-03-17 22:51:07 +03:00
// levelUpCommander(hero->commander);
// else
// levelUpHero(hero);
}
void CGameHandler : : changePrimSkill ( const CGHeroInstance * hero , PrimarySkill : : PrimarySkill which , si64 val , bool abs )
{
if ( which = = PrimarySkill : : EXPERIENCE ) // Check if scenario limit reached
{
if ( gs - > map - > levelLimit ! = 0 )
{
TExpType expLimit = VLC - > heroh - > reqExp ( gs - > map - > levelLimit ) ;
TExpType resultingExp = abs ? val : hero - > exp + val ;
if ( resultingExp > expLimit )
{
// set given experience to max possible, but don't decrease if hero already over top
abs = true ;
val = std : : max ( expLimit , hero - > exp ) ;
InfoWindow iw ;
iw . player = hero - > tempOwner ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 1 ) ; //can gain no more XP
2023-02-24 13:40:06 +02:00
iw . text . addReplacement ( hero - > getNameTranslated ( ) ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & iw ) ;
}
}
}
SetPrimSkill sps ;
sps . id = hero - > id ;
sps . which = which ;
sps . abs = abs ;
sps . val = val ;
sendAndApply ( & sps ) ;
//only for exp - hero may level up
if ( which = = PrimarySkill : : EXPERIENCE )
{
2016-10-12 17:16:26 +02:00
if ( hero - > commander & & hero - > commander - > alive )
2014-03-17 22:51:07 +03:00
{
//FIXME: trim experience according to map limit?
SetCommanderProperty scp ;
scp . heroid = hero - > id ;
scp . which = SetCommanderProperty : : EXPERIENCE ;
scp . amount = val ;
sendAndApply ( & scp ) ;
CBonusSystemNode : : treeHasChanged ( ) ;
}
expGiven ( hero ) ;
}
}
2017-07-15 13:08:20 +02:00
void CGameHandler : : changeSecSkill ( const CGHeroInstance * hero , SecondarySkill which , int val , bool abs )
2014-03-17 22:51:07 +03:00
{
2016-11-25 20:17:24 +02:00
if ( ! hero )
{
logGlobal - > error ( " changeSecSkill provided no hero " ) ;
return ;
}
2014-03-17 22:51:07 +03:00
SetSecSkill sss ;
sss . id = hero - > id ;
sss . which = which ;
sss . val = val ;
sss . abs = abs ;
sendAndApply ( & sss ) ;
2023-02-27 12:57:21 +02:00
if ( hero - > visitedTown )
giveSpells ( hero - > visitedTown , hero ) ;
2014-03-17 22:51:07 +03:00
}
2021-03-23 16:47:07 +02:00
void CGameHandler : : endBattle ( int3 tile , const CGHeroInstance * heroAttacker , const CGHeroInstance * heroDefender )
2014-03-17 22:51:07 +03:00
{
LOG_TRACE ( logGlobal ) ;
//Fill BattleResult structure with exp info
giveExp ( * battleResult . data ) ;
if ( battleResult . get ( ) - > result = = BattleResult : : NORMAL ) // give 500 exp for defeating hero, unless he escaped
{
2021-03-23 16:47:07 +02:00
if ( heroAttacker )
2014-03-17 22:51:07 +03:00
battleResult . data - > exp [ 1 ] + = 500 ;
2021-03-23 16:47:07 +02:00
if ( heroDefender )
2014-03-17 22:51:07 +03:00
battleResult . data - > exp [ 0 ] + = 500 ;
}
2021-03-23 16:47:07 +02:00
if ( heroAttacker )
battleResult . data - > exp [ 0 ] = heroAttacker - > calculateXp ( battleResult . data - > exp [ 0 ] ) ; //scholar skill
if ( heroDefender )
battleResult . data - > exp [ 1 ] = heroDefender - > calculateXp ( battleResult . data - > exp [ 1 ] ) ;
2014-03-17 22:51:07 +03:00
2022-11-05 04:34:38 +02:00
auto battleQuery = std : : dynamic_pointer_cast < CBattleQuery > ( queries . topQuery ( gs - > curB - > sides [ 0 ] . color ) ) ;
2016-10-12 17:16:26 +02:00
if ( ! battleQuery )
2014-03-17 22:51:07 +03:00
{
2016-08-30 00:11:54 +02:00
logGlobal - > error ( " Cannot find battle query! " ) ;
2022-11-05 04:34:38 +02:00
complain ( " Player " + boost : : lexical_cast < std : : string > ( gs - > curB - > sides [ 0 ] . color ) + " has no battle query at the top! " ) ;
return ;
2014-03-17 22:51:07 +03:00
}
2023-04-16 19:42:56 +02:00
battleQuery - > result = std : : make_optional ( * battleResult . data ) ;
2014-03-17 22:51:07 +03:00
//Check how many battle queries were created (number of players blocked by battle)
2020-10-01 10:38:06 +02:00
const int queriedPlayers = battleQuery ? ( int ) boost : : count ( queries . allQueries ( ) , battleQuery ) : 0 ;
2022-12-07 23:36:20 +02:00
finishingBattle = std : : make_unique < FinishingBattleHelper > ( battleQuery , queriedPlayers ) ;
2022-11-05 01:23:31 +02:00
auto battleDialogQuery = std : : make_shared < CBattleDialogQuery > ( this , gs - > curB ) ;
battleResult . data - > queryID = battleDialogQuery - > queryID ;
queries . addQuery ( battleDialogQuery ) ;
2023-04-06 20:49:38 +02:00
//set same battle result for all queries
for ( auto q : queries . allQueries ( ) )
{
auto otherBattleQuery = std : : dynamic_pointer_cast < CBattleQuery > ( q ) ;
if ( otherBattleQuery )
otherBattleQuery - > result = battleQuery - > result ;
}
2022-11-05 01:23:31 +02:00
sendAndApply ( battleResult . data ) ; //after this point casualties objects are destroyed
}
2014-03-17 22:51:07 +03:00
2022-11-05 01:23:31 +02:00
void CGameHandler : : endBattleConfirm ( const BattleInfo * battleInfo )
{
2022-11-05 04:34:38 +02:00
auto battleQuery = std : : dynamic_pointer_cast < CBattleQuery > ( queries . topQuery ( battleInfo - > sides . at ( 0 ) . color ) ) ;
if ( ! battleQuery )
2022-11-05 01:23:31 +02:00
{
2022-11-25 01:02:34 +02:00
logGlobal - > trace ( " No battle query, battle end was confirmed by another player " ) ;
2022-11-05 04:34:38 +02:00
return ;
2022-11-05 01:23:31 +02:00
}
const CArmedInstance * bEndArmy1 = battleInfo - > sides . at ( 0 ) . armyObject ;
const CArmedInstance * bEndArmy2 = battleInfo - > sides . at ( 1 ) . armyObject ;
const BattleResult : : EResult result = battleResult . get ( ) - > result ;
CasualtiesAfterBattle cab1 ( bEndArmy1 , battleInfo ) , cab2 ( bEndArmy2 , battleInfo ) ; //calculate casualties before deleting battle
2014-03-17 22:51:07 +03:00
ChangeSpells cs ; //for Eagle Eye
2022-11-16 00:50:47 +02:00
if ( ! finishingBattle - > isDraw ( ) & & finishingBattle - > winnerHero )
2014-03-17 22:51:07 +03:00
{
2023-05-01 00:20:01 +02:00
if ( int eagleEyeLevel = finishingBattle - > winnerHero - > valOfBonuses ( BonusType : : LEARN_BATTLE_SPELL_LEVEL_LIMIT , - 1 ) )
2014-03-17 22:51:07 +03:00
{
2023-05-01 00:20:01 +02:00
double eagleEyeChance = finishingBattle - > winnerHero - > valOfBonuses ( BonusType : : LEARN_BATTLE_SPELL_CHANCE , 0 ) ;
2022-11-05 01:23:31 +02:00
for ( auto & spellId : battleInfo - > sides . at ( ! battleResult . data - > winner ) . usedSpellsHistory )
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
{
auto spell = spellId . toSpell ( VLC - > spells ( ) ) ;
if ( spell & & spell - > getLevel ( ) < = eagleEyeLevel & & ! finishingBattle - > winnerHero - > spellbookContainsSpell ( spell - > getId ( ) ) & & getRandomGenerator ( ) . nextInt ( 99 ) < eagleEyeChance )
cs . spells . insert ( spell - > getId ( ) ) ;
}
2014-03-17 22:51:07 +03:00
}
}
2016-01-21 19:23:45 +02:00
std : : vector < const CArtifactInstance * > arts ; //display them in window
2014-03-17 22:51:07 +03:00
2022-11-16 00:50:47 +02:00
if ( result = = BattleResult : : NORMAL & & ! finishingBattle - > isDraw ( ) & & finishingBattle - > winnerHero )
2014-03-17 22:51:07 +03:00
{
2016-01-21 19:23:45 +02:00
auto sendMoveArtifact = [ & ] ( const CArtifactInstance * art , MoveArtifact * ma )
2014-03-17 22:51:07 +03:00
{
2023-03-21 20:02:58 +02:00
const auto slot = ArtifactUtils : : getArtAnyPosition ( finishingBattle - > winnerHero , art - > getTypeId ( ) ) ;
2023-03-21 12:13:53 +02:00
if ( slot ! = ArtifactPosition : : PRE_FIRST )
{
arts . push_back ( art ) ;
ma - > dst = ArtifactLocation ( finishingBattle - > winnerHero , slot ) ;
if ( ArtifactUtils : : isSlotBackpack ( slot ) )
ma - > askAssemble = false ;
sendAndApply ( ma ) ;
}
2016-01-21 19:23:45 +02:00
} ;
2021-03-23 16:47:07 +02:00
2016-10-12 17:16:26 +02:00
if ( finishingBattle - > loserHero )
2016-01-21 19:23:45 +02:00
{
2023-04-15 03:33:00 +02:00
//TODO: wrap it into a function, somehow (std::variant -_-)
2016-01-21 19:23:45 +02:00
auto artifactsWorn = finishingBattle - > loserHero - > artifactsWorn ;
2014-03-17 22:51:07 +03:00
for ( auto artSlot : artifactsWorn )
{
MoveArtifact ma ;
2016-01-21 19:23:45 +02:00
ma . src = ArtifactLocation ( finishingBattle - > loserHero , artSlot . first ) ;
2014-03-17 22:51:07 +03:00
const CArtifactInstance * art = ma . src . getArt ( ) ;
2016-10-12 17:16:26 +02:00
if ( art & & ! art - > artType - > isBig ( ) & &
2023-01-02 15:58:56 +02:00
art - > artType - > getId ( ) ! = ArtifactID : : SPELLBOOK )
2016-01-21 19:23:45 +02:00
// don't move war machines or locked arts (spellbook)
2014-03-17 22:51:07 +03:00
{
2016-01-21 19:23:45 +02:00
sendMoveArtifact ( art , & ma ) ;
2014-03-17 22:51:07 +03:00
}
}
2023-04-23 10:38:33 +02:00
for ( int slotNumber = finishingBattle - > loserHero - > artifactsInBackpack . size ( ) - 1 ; slotNumber > = 0 ; slotNumber - - )
2014-03-17 22:51:07 +03:00
{
//we assume that no big artifacts can be found
MoveArtifact ma ;
2016-01-21 19:23:45 +02:00
ma . src = ArtifactLocation ( finishingBattle - > loserHero ,
2023-04-23 10:38:33 +02:00
ArtifactPosition ( GameConstants : : BACKPACK_START + slotNumber ) ) ; //backpack automatically shifts arts to beginning
2014-03-17 22:51:07 +03:00
const CArtifactInstance * art = ma . src . getArt ( ) ;
2023-01-02 15:58:56 +02:00
if ( art - > artType - > getId ( ) ! = ArtifactID : : GRAIL ) //grail may not be won
2016-01-21 19:23:45 +02:00
{
sendMoveArtifact ( art , & ma ) ;
}
2014-03-17 22:51:07 +03:00
}
2016-10-12 17:16:26 +02:00
if ( finishingBattle - > loserHero - > commander ) //TODO: what if commanders belong to no hero?
2014-03-17 22:51:07 +03:00
{
artifactsWorn = finishingBattle - > loserHero - > commander - > artifactsWorn ;
2016-10-12 17:16:26 +02:00
for ( auto artSlot : artifactsWorn )
2014-03-17 22:51:07 +03:00
{
MoveArtifact ma ;
2016-01-21 19:23:45 +02:00
ma . src = ArtifactLocation ( finishingBattle - > loserHero - > commander . get ( ) , artSlot . first ) ;
2014-03-17 22:51:07 +03:00
const CArtifactInstance * art = ma . src . getArt ( ) ;
if ( art & & ! art - > artType - > isBig ( ) )
{
2016-01-21 19:23:45 +02:00
sendMoveArtifact ( art , & ma ) ;
2014-03-17 22:51:07 +03:00
}
}
}
}
2022-11-05 01:23:31 +02:00
for ( auto armySlot : battleInfo - > sides . at ( ! battleResult . data - > winner ) . armyObject - > stacks )
2014-03-17 22:51:07 +03:00
{
auto artifactsWorn = armySlot . second - > artifactsWorn ;
for ( auto artSlot : artifactsWorn )
{
MoveArtifact ma ;
2016-01-21 19:23:45 +02:00
ma . src = ArtifactLocation ( armySlot . second , artSlot . first ) ;
2014-03-17 22:51:07 +03:00
const CArtifactInstance * art = ma . src . getArt ( ) ;
if ( art & & ! art - > artType - > isBig ( ) )
{
2016-01-21 19:23:45 +02:00
sendMoveArtifact ( art , & ma ) ;
2014-03-17 22:51:07 +03:00
}
}
}
}
2022-11-03 19:38:49 +02:00
2016-10-12 17:16:26 +02:00
if ( arts . size ( ) ) //display loot
2014-03-17 22:51:07 +03:00
{
InfoWindow iw ;
iw . player = finishingBattle - > winnerHero - > tempOwner ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 30 ) ; //You have captured enemy artifact
2016-10-12 17:16:26 +02:00
for ( auto art : arts ) //TODO; separate function to display loot for various ojects?
2014-03-17 22:51:07 +03:00
{
2023-03-10 14:54:12 +02:00
iw . components . emplace_back (
Component : : EComponentType : : ARTIFACT , art - > artType - > getId ( ) ,
2023-04-13 19:40:36 +02:00
art - > artType - > getId ( ) = = ArtifactID : : SPELL_SCROLL ? art - > getScrollSpellID ( ) : 0 , 0 ) ;
2016-10-12 17:16:26 +02:00
if ( iw . components . size ( ) > = 14 )
2014-03-17 22:51:07 +03:00
{
sendAndApply ( & iw ) ;
iw . components . clear ( ) ;
}
}
2016-10-12 17:16:26 +02:00
if ( iw . components . size ( ) )
2014-03-17 22:51:07 +03:00
{
sendAndApply ( & iw ) ;
}
}
//Eagle Eye secondary skill handling
2016-10-12 17:16:26 +02:00
if ( ! cs . spells . empty ( ) )
2014-03-17 22:51:07 +03:00
{
cs . learn = 1 ;
cs . hid = finishingBattle - > winnerHero - > id ;
InfoWindow iw ;
iw . player = finishingBattle - > winnerHero - > tempOwner ;
iw . text . addTxt ( MetaString : : GENERAL_TXT , 221 ) ; //Through eagle-eyed observation, %s is able to learn %s
2023-02-24 13:40:06 +02:00
iw . text . addReplacement ( finishingBattle - > winnerHero - > getNameTranslated ( ) ) ;
2014-03-17 22:51:07 +03:00
std : : ostringstream names ;
2016-10-12 17:16:26 +02:00
for ( int i = 0 ; i < cs . spells . size ( ) ; i + + )
2014-03-17 22:51:07 +03:00
{
names < < " %s " ;
2016-10-12 17:16:26 +02:00
if ( i < cs . spells . size ( ) - 2 )
2014-03-17 22:51:07 +03:00
names < < " , " ;
2016-10-12 17:16:26 +02:00
else if ( i < cs . spells . size ( ) - 1 )
2014-03-17 22:51:07 +03:00
names < < " %s " ;
}
names < < " . " ;
iw . text . addReplacement ( names . str ( ) ) ;
auto it = cs . spells . begin ( ) ;
2016-10-12 17:16:26 +02:00
for ( int i = 0 ; i < cs . spells . size ( ) ; i + + , it + + )
2014-03-17 22:51:07 +03:00
{
iw . text . addReplacement ( MetaString : : SPELL_NAME , it - > toEnum ( ) ) ;
2016-10-12 17:16:26 +02:00
if ( i = = cs . spells . size ( ) - 2 ) //we just added pre-last name
2014-03-17 22:51:07 +03:00
iw . text . addReplacement ( MetaString : : GENERAL_TXT , 141 ) ; // " and "
2023-03-10 14:54:12 +02:00
iw . components . emplace_back ( Component : : EComponentType : : SPELL , * it , 0 , 0 ) ;
2014-03-17 22:51:07 +03:00
}
sendAndApply ( & iw ) ;
sendAndApply ( & cs ) ;
}
2015-11-07 10:42:06 +02:00
cab1 . updateArmy ( this ) ;
cab2 . updateArmy ( this ) ; //take casualties after battle is deleted
2022-11-05 01:23:31 +02:00
if ( finishingBattle - > loserHero ) //remove beaten hero
2014-03-17 22:51:07 +03:00
{
2022-11-05 01:23:31 +02:00
RemoveObject ro ( finishingBattle - > loserHero - > id ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & ro ) ;
}
2022-11-16 00:50:47 +02:00
if ( finishingBattle - > isDraw ( ) & & finishingBattle - > winnerHero ) //for draw case both heroes should be removed
2014-03-17 22:51:07 +03:00
{
2022-11-16 00:50:47 +02:00
RemoveObject ro ( finishingBattle - > winnerHero - > id ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & ro ) ;
}
2022-11-05 01:23:31 +02:00
if ( battleResult . data - > winner = = BattleSide : : DEFENDER
& & finishingBattle - > winnerHero
& & finishingBattle - > winnerHero - > visitedTown
& & ! finishingBattle - > winnerHero - > inTownGarrison
& & finishingBattle - > winnerHero - > visitedTown - > garrisonHero = = finishingBattle - > winnerHero )
2021-03-23 16:47:07 +02:00
{
2022-11-05 01:23:31 +02:00
swapGarrisonOnSiege ( finishingBattle - > winnerHero - > visitedTown - > id ) ; //return defending visitor from garrison to its rightful place
2021-03-23 16:47:07 +02:00
}
2014-03-17 22:51:07 +03:00
//give exp
2022-11-16 00:50:47 +02:00
if ( ! finishingBattle - > isDraw ( ) & & battleResult . data - > exp [ finishingBattle - > winnerSide ] & & finishingBattle - > winnerHero )
2022-11-05 01:23:31 +02:00
changePrimSkill ( finishingBattle - > winnerHero , PrimarySkill : : EXPERIENCE , battleResult . data - > exp [ finishingBattle - > winnerSide ] ) ;
2022-11-05 04:34:38 +02:00
BattleResultAccepted raccepted ;
raccepted . army1 = const_cast < CArmedInstance * > ( bEndArmy1 ) ;
raccepted . army2 = const_cast < CArmedInstance * > ( bEndArmy2 ) ;
raccepted . hero1 = const_cast < CGHeroInstance * > ( battleInfo - > sides . at ( 0 ) . hero ) ;
raccepted . hero2 = const_cast < CGHeroInstance * > ( battleInfo - > sides . at ( 1 ) . hero ) ;
raccepted . exp [ 0 ] = battleResult . data - > exp [ 0 ] ;
raccepted . exp [ 1 ] = battleResult . data - > exp [ 1 ] ;
sendAndApply ( & raccepted ) ;
2014-03-17 22:51:07 +03:00
queries . popIfTop ( battleQuery ) ;
2022-11-25 01:02:34 +02:00
//--> continuation (battleAfterLevelUp) occurs after level-up queries are handled or on removing query
2014-03-17 22:51:07 +03:00
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : battleAfterLevelUp ( const BattleResult & result )
2014-03-17 22:51:07 +03:00
{
LOG_TRACE ( logGlobal ) ;
2023-04-06 17:34:07 +02:00
if ( ! finishingBattle )
return ;
2014-03-17 22:51:07 +03:00
finishingBattle - > remainingBattleQueriesCount - - ;
2016-08-30 00:11:54 +02:00
logGlobal - > trace ( " Decremented queries count to %d " , finishingBattle - > remainingBattleQueriesCount ) ;
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( finishingBattle - > remainingBattleQueriesCount > 0 )
2014-03-17 22:51:07 +03:00
//Battle results will be handled when all battle queries are closed
return ;
//TODO consider if we really want it to work like above. ATM each player as unblocked as soon as possible
// but the battle consequences are applied after final player is unblocked. Hard to abuse...
// Still, it looks like a hole.
// Necromancy if applicable.
const CStackBasicDescriptor raisedStack = finishingBattle - > winnerHero ? finishingBattle - > winnerHero - > calculateNecromancy ( * battleResult . data ) : CStackBasicDescriptor ( ) ;
// Give raised units to winner and show dialog, if any were raised,
// units will be given after casualties are taken
const SlotID necroSlot = raisedStack . type ? finishingBattle - > winnerHero - > getSlotFor ( raisedStack . type ) : SlotID ( ) ;
if ( necroSlot ! = SlotID ( ) )
{
2016-09-09 19:30:36 +02:00
finishingBattle - > winnerHero - > showNecromancyDialog ( raisedStack , getRandomGenerator ( ) ) ;
2014-03-17 22:51:07 +03:00
addToSlot ( StackLocation ( finishingBattle - > winnerHero , necroSlot ) , raisedStack . type , raisedStack . count ) ;
}
BattleResultsApplied resultsApplied ;
resultsApplied . player1 = finishingBattle - > victor ;
resultsApplied . player2 = finishingBattle - > loser ;
sendAndApply ( & resultsApplied ) ;
setBattle ( nullptr ) ;
2016-10-12 17:16:26 +02:00
if ( visitObjectAfterVictory & & result . winner = = 0 & & ! finishingBattle - > winnerHero - > stacks . empty ( ) )
2014-03-17 22:51:07 +03:00
{
2016-08-30 00:11:54 +02:00
logGlobal - > trace ( " post-victory visit " ) ;
2022-12-07 21:50:45 +02:00
visitObjectOnTile ( * getTile ( finishingBattle - > winnerHero - > visitablePos ( ) ) , finishingBattle - > winnerHero ) ;
2014-03-17 22:51:07 +03:00
}
visitObjectAfterVictory = false ;
//handle victory/loss of engaged players
2014-10-02 18:43:46 +03:00
std : : set < PlayerColor > playerColors = { finishingBattle - > loser , finishingBattle - > victor } ;
2014-03-17 22:51:07 +03:00
checkVictoryLossConditions ( playerColors ) ;
2016-10-12 17:16:26 +02:00
if ( result . result = = BattleResult : : SURRENDER | | result . result = = BattleResult : : ESCAPE ) //loser has escaped or surrendered
2014-03-17 22:51:07 +03:00
{
SetAvailableHeroes sah ;
sah . player = finishingBattle - > loser ;
sah . hid [ 0 ] = finishingBattle - > loserHero - > subID ;
2016-10-12 17:16:26 +02:00
if ( result . result = = BattleResult : : ESCAPE ) //retreat
2014-03-17 22:51:07 +03:00
{
sah . army [ 0 ] . clear ( ) ;
sah . army [ 0 ] . setCreature ( SlotID ( 0 ) , finishingBattle - > loserHero - > type - > initialArmy . at ( 0 ) . creature , 1 ) ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( const CGHeroInstance * another = getPlayerState ( finishingBattle - > loser ) - > availableHeroes . at ( 0 ) )
2014-03-17 22:51:07 +03:00
sah . hid [ 1 ] = another - > subID ;
else
sah . hid [ 1 ] = - 1 ;
sendAndApply ( & sah ) ;
}
2022-04-23 15:45:38 +02:00
if ( result . winner ! = 2 & & finishingBattle - > winnerHero & & finishingBattle - > winnerHero - > stacks . empty ( )
& & ( ! finishingBattle - > winnerHero - > commander | | ! finishingBattle - > winnerHero - > commander - > alive ) )
2016-01-22 21:29:53 +02:00
{
RemoveObject ro ( finishingBattle - > winnerHero - > id ) ;
sendAndApply ( & ro ) ;
2023-03-15 23:47:26 +02:00
if ( VLC - > settings ( ) - > getBoolean ( EGameSettings : : HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS ) )
2016-01-27 10:38:35 +02:00
{
SetAvailableHeroes sah ;
sah . player = finishingBattle - > victor ;
sah . hid [ 0 ] = finishingBattle - > winnerHero - > subID ;
sah . army [ 0 ] . clear ( ) ;
sah . army [ 0 ] . setCreature ( SlotID ( 0 ) , finishingBattle - > winnerHero - > type - > initialArmy . at ( 0 ) . creature , 1 ) ;
2016-01-22 21:29:53 +02:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( const CGHeroInstance * another = getPlayerState ( finishingBattle - > victor ) - > availableHeroes . at ( 0 ) )
2016-01-27 10:38:35 +02:00
sah . hid [ 1 ] = another - > subID ;
else
sah . hid [ 1 ] = - 1 ;
2014-03-17 22:51:07 +03:00
2016-01-27 10:38:35 +02:00
sendAndApply ( & sah ) ;
}
2014-03-17 22:51:07 +03:00
}
2023-04-06 17:34:07 +02:00
finishingBattle . reset ( ) ;
2014-03-17 22:51:07 +03:00
}
2017-07-20 06:08:49 +02:00
void CGameHandler : : makeAttack ( const CStack * attacker , const CStack * defender , int distance , BattleHex targetHex , bool first , bool ranged , bool counter )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
if ( first & & ! counter )
handleAttackBeforeCasting ( ranged , attacker , defender ) ;
FireShieldInfo fireShield ;
BattleAttack bat ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
BattleLogMessage blm ;
2017-07-20 06:08:49 +02:00
bat . stackAttacking = attacker - > unitId ( ) ;
2022-12-11 12:29:11 +02:00
bat . tile = targetHex ;
2017-07-20 06:08:49 +02:00
std : : shared_ptr < battle : : CUnitState > attackerState = attacker - > acquireState ( ) ;
if ( ranged )
bat . flags | = BattleAttack : : SHOT ;
if ( counter )
bat . flags | = BattleAttack : : COUNTER ;
2023-05-02 00:05:59 +02:00
const int attackerLuck = attacker - > luckVal ( ) ;
2014-03-17 22:51:07 +03:00
2023-03-15 23:47:26 +02:00
if ( attackerLuck > 0 )
2014-03-17 22:51:07 +03:00
{
2023-03-15 23:47:26 +02:00
auto diceSize = VLC - > settings ( ) - > getVector ( EGameSettings : : COMBAT_GOOD_LUCK_DICE ) ;
size_t diceIndex = std : : min < size_t > ( diceSize . size ( ) - 1 , attackerLuck ) ;
if ( diceSize . size ( ) > 0 & & getRandomGenerator ( ) . nextInt ( 1 , diceSize [ diceIndex ] ) = = 1 )
bat . flags | = BattleAttack : : LUCKY ;
2023-03-09 11:08:32 +02:00
}
2023-03-15 21:34:29 +02:00
2023-03-15 23:47:26 +02:00
if ( attackerLuck < 0 )
2023-03-09 11:08:32 +02:00
{
2023-03-15 23:47:26 +02:00
auto diceSize = VLC - > settings ( ) - > getVector ( EGameSettings : : COMBAT_BAD_LUCK_DICE ) ;
size_t diceIndex = std : : min < size_t > ( diceSize . size ( ) - 1 , - attackerLuck ) ;
if ( diceSize . size ( ) > 0 & & getRandomGenerator ( ) . nextInt ( 1 , diceSize [ diceIndex ] ) = = 1 )
2023-03-09 11:08:32 +02:00
bat . flags | = BattleAttack : : UNLUCKY ;
2014-03-17 22:51:07 +03:00
}
2023-05-01 00:20:01 +02:00
if ( getRandomGenerator ( ) . nextInt ( 99 ) < attacker - > valOfBonuses ( BonusType : : DOUBLE_DAMAGE_CHANCE ) )
2014-03-17 22:51:07 +03:00
{
bat . flags | = BattleAttack : : DEATH_BLOW ;
}
2023-04-27 19:43:20 +02:00
const auto * owner = gs - > curB - > getHero ( attacker - > unitOwner ( ) ) ;
2023-02-26 00:19:39 +02:00
if ( owner )
2014-03-17 22:51:07 +03:00
{
2023-05-01 00:20:01 +02:00
int chance = owner - > valOfBonuses ( BonusType : : BONUS_DAMAGE_CHANCE , attacker - > creatureIndex ( ) ) ;
2016-10-12 17:16:26 +02:00
if ( chance > getRandomGenerator ( ) . nextInt ( 99 ) )
2014-03-17 22:51:07 +03:00
bat . flags | = BattleAttack : : BALLISTA_DOUBLE_DMG ;
}
2022-09-19 16:45:48 +02:00
int64_t drainedLife = 0 ;
2014-03-17 22:51:07 +03:00
// only primary target
2017-07-20 06:08:49 +02:00
if ( defender - > alive ( ) )
2022-09-19 16:45:48 +02:00
drainedLife + = applyBattleEffects ( bat , attackerState , fireShield , defender , distance , false ) ;
2014-03-17 22:51:07 +03:00
2017-09-04 23:32:24 +02:00
//multiple-hex normal attack
2017-07-20 06:08:49 +02:00
std : : set < const CStack * > attackedCreatures = gs - > curB - > getAttackedCreatures ( attacker , targetHex , bat . shot ( ) ) ; //creatures other than primary target
for ( const CStack * stack : attackedCreatures )
2017-09-04 23:32:24 +02:00
{
2017-07-20 06:08:49 +02:00
if ( stack ! = defender & & stack - > alive ( ) ) //do not hit same stack twice
2022-09-19 16:45:48 +02:00
drainedLife + = applyBattleEffects ( bat , attackerState , fireShield , stack , distance , true ) ;
2014-03-17 22:51:07 +03:00
}
2023-05-01 00:20:01 +02:00
std : : shared_ptr < const Bonus > bonus = attacker - > getBonusLocalFirst ( Selector : : type ( ) ( BonusType : : SPELL_LIKE_ATTACK ) ) ;
2017-07-20 06:08:49 +02:00
if ( bonus & & ranged ) //TODO: make it work in melee?
2015-10-28 22:53:44 +02:00
{
2014-11-27 23:36:14 +02:00
//this is need for displaying hit animation
bat . flags | = BattleAttack : : SPELL_LIKE ;
bat . spellID = SpellID ( bonus - > subtype ) ;
2015-10-28 22:53:44 +02:00
2014-11-27 23:36:14 +02:00
//TODO: should spell override creature`s projectile?
2015-10-28 22:53:44 +02:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
auto spell = bat . spellID . toSpell ( ) ;
2017-07-20 06:08:49 +02:00
battle : : Target target ;
2023-04-01 00:06:26 +02:00
target . emplace_back ( defender , targetHex ) ;
2017-07-20 06:08:49 +02:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
spells : : BattleCast event ( gs - > curB , attacker , spells : : Mode : : SPELL_LIKE_ATTACK , spell ) ;
event . setSpellLevel ( bonus - > val ) ;
auto attackedCreatures = spell - > battleMechanics ( & event ) - > getAffectedStacks ( target ) ;
2015-10-28 22:53:44 +02:00
2014-03-17 22:51:07 +03:00
//TODO: get exact attacked hex for defender
2017-07-20 06:08:49 +02:00
for ( const CStack * stack : attackedCreatures )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
if ( stack ! = defender & & stack - > alive ( ) ) //do not hit same stack twice
2014-03-17 22:51:07 +03:00
{
2022-09-19 16:45:48 +02:00
drainedLife + = applyBattleEffects ( bat , attackerState , fireShield , stack , distance , true ) ;
2014-03-17 22:51:07 +03:00
}
}
2015-10-28 22:53:44 +02:00
2014-11-27 23:36:14 +02:00
//now add effect info for all attacked stacks
2016-10-12 17:16:26 +02:00
for ( BattleStackAttacked & bsa : bat . bsa )
2014-11-27 23:36:14 +02:00
{
2023-04-27 19:43:20 +02:00
if ( bsa . attackerID = = attacker - > unitId ( ) ) //this is our attack and not f.e. fire shield
2014-11-27 23:36:14 +02:00
{
//this is need for displaying affect animation
bsa . flags | = BattleStackAttacked : : SPELL_EFFECT ;
bsa . spellID = SpellID ( bonus - > subtype ) ;
}
}
2017-07-20 06:08:49 +02:00
}
attackerState - > afterAttack ( ranged , counter ) ;
2015-10-28 22:53:44 +02:00
2017-07-20 06:08:49 +02:00
{
UnitChanges info ( attackerState - > unitId ( ) , UnitChanges : : EOperation : : RESET_STATE ) ;
attackerState - > save ( info . data ) ;
bat . attackerChanges . changedStacks . push_back ( info ) ;
2014-03-17 22:51:07 +03:00
}
2017-07-20 06:08:49 +02:00
2022-12-11 12:29:11 +02:00
if ( drainedLife > 0 )
bat . flags | = BattleAttack : : LIFE_DRAIN ;
2017-07-20 06:08:49 +02:00
sendAndApply ( & bat ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
{
const bool multipleTargets = bat . bsa . size ( ) > 1 ;
int64_t totalDamage = 0 ;
int32_t totalKills = 0 ;
for ( const BattleStackAttacked & bsa : bat . bsa )
{
totalDamage + = bsa . damageAmount ;
totalKills + = bsa . killedAmount ;
}
{
MetaString text ;
attacker - > addText ( text , MetaString : : GENERAL_TXT , 376 ) ;
attacker - > addNameReplacement ( text ) ;
text . addReplacement ( totalDamage ) ;
blm . lines . push_back ( text ) ;
}
addGenericKilledLog ( blm , defender , totalKills , multipleTargets ) ;
}
2022-09-19 16:45:48 +02:00
// drain life effect (as well as log entry) must be applied after the attack
if ( drainedLife > 0 )
{
MetaString text ;
attackerState - > addText ( text , MetaString : : GENERAL_TXT , 361 ) ;
attackerState - > addNameReplacement ( text , false ) ;
text . addReplacement ( drainedLife ) ;
defender - > addNameReplacement ( text , true ) ;
blm . lines . push_back ( std : : move ( text ) ) ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
2017-07-20 06:08:49 +02:00
if ( ! fireShield . empty ( ) )
{
//todo: this should be "virtual" spell instead, we only need fire spell school bonus here
const CSpell * fireShieldSpell = SpellID ( SpellID : : FIRE_SHIELD ) . toSpell ( ) ;
int64_t totalDamage = 0 ;
for ( const auto & item : fireShield )
{
const CStack * actor = item . first ;
int64_t rawDamage = item . second ;
2023-04-27 19:43:20 +02:00
const CGHeroInstance * actorOwner = gs - > curB - > getHero ( actor - > unitOwner ( ) ) ;
2017-07-20 06:08:49 +02:00
if ( actorOwner )
{
rawDamage = fireShieldSpell - > adjustRawDamage ( actorOwner , attacker , rawDamage ) ;
}
else
{
rawDamage = fireShieldSpell - > adjustRawDamage ( actor , attacker , rawDamage ) ;
}
totalDamage + = rawDamage ;
//FIXME: add custom effect on actor
}
2023-01-04 17:55:19 +02:00
if ( totalDamage > 0 )
{
BattleStackAttacked bsa ;
2017-07-20 06:08:49 +02:00
2023-01-04 17:55:19 +02:00
bsa . flags | = BattleStackAttacked : : FIRE_SHIELD ;
2023-04-27 19:43:20 +02:00
bsa . stackAttacked = attacker - > unitId ( ) ; //invert
bsa . attackerID = defender - > unitId ( ) ;
2023-01-04 17:55:19 +02:00
bsa . damageAmount = totalDamage ;
attacker - > prepareAttacked ( bsa , getRandomGenerator ( ) ) ;
2017-07-20 06:08:49 +02:00
2023-01-04 17:55:19 +02:00
StacksInjured pack ;
pack . stacks . push_back ( bsa ) ;
sendAndApply ( & pack ) ;
2022-09-21 09:42:42 +02:00
2023-01-04 17:55:19 +02:00
// TODO: this is already implemented in Damage::describeEffect()
{
MetaString text ;
text . addTxt ( MetaString : : GENERAL_TXT , 376 ) ;
text . addReplacement ( MetaString : : SPELL_NAME , SpellID : : FIRE_SHIELD ) ;
text . addReplacement ( totalDamage ) ;
blm . lines . push_back ( std : : move ( text ) ) ;
}
addGenericKilledLog ( blm , attacker , bsa . killedAmount , false ) ;
2022-09-21 09:42:42 +02:00
}
2017-07-20 06:08:49 +02:00
}
2022-09-21 09:42:42 +02:00
sendAndApply ( & blm ) ;
2017-07-20 06:08:49 +02:00
handleAfterAttackCasting ( ranged , attacker , defender ) ;
2014-03-17 22:51:07 +03:00
}
2022-09-19 16:45:48 +02:00
int64_t CGameHandler : : applyBattleEffects ( BattleAttack & bat , std : : shared_ptr < battle : : CUnitState > attackerState , FireShieldInfo & fireShield , const CStack * def , int distance , bool secondary )
2014-03-17 22:51:07 +03:00
{
BattleStackAttacked bsa ;
2017-07-20 06:08:49 +02:00
if ( secondary )
2014-03-17 22:51:07 +03:00
bsa . flags | = BattleStackAttacked : : SECONDARY ; //all other targets do not suffer from spells & spell-like abilities
2022-12-09 13:10:35 +02:00
2017-07-20 06:08:49 +02:00
bsa . attackerID = attackerState - > unitId ( ) ;
bsa . stackAttacked = def - > unitId ( ) ;
{
2023-01-14 19:03:18 +02:00
BattleAttackInfo bai ( attackerState . get ( ) , def , distance , bat . shot ( ) ) ;
2014-03-17 22:51:07 +03:00
2023-01-14 19:03:18 +02:00
bai . deathBlow = bat . deathBlow ( ) ;
bai . doubleDamage = bat . ballistaDoubleDmg ( ) ;
bai . luckyStrike = bat . lucky ( ) ;
bai . unluckyStrike = bat . unlucky ( ) ;
2017-07-20 06:08:49 +02:00
auto range = gs - > curB - > calculateDmgRange ( bai ) ;
2023-03-24 17:17:17 +02:00
bsa . damageAmount = gs - > curB - > getActualDamage ( range . damage , attackerState - > getCount ( ) , getRandomGenerator ( ) ) ;
2017-07-20 06:08:49 +02:00
CStack : : prepareAttacked ( bsa , getRandomGenerator ( ) , bai . defender - > acquireState ( ) ) ; //calculate casualties
}
2022-09-19 16:45:48 +02:00
int64_t drainedLife = 0 ;
2017-07-04 13:24:46 +02:00
2017-07-20 06:08:49 +02:00
//life drain handling
2023-05-01 00:20:01 +02:00
if ( attackerState - > hasBonusOfType ( BonusType : : LIFE_DRAIN ) & & def - > isLiving ( ) )
2017-07-20 06:08:49 +02:00
{
2023-05-01 00:20:01 +02:00
int64_t toHeal = bsa . damageAmount * attackerState - > valOfBonuses ( BonusType : : LIFE_DRAIN ) / 100 ;
2022-09-19 16:45:48 +02:00
attackerState - > heal ( toHeal , EHealLevel : : RESURRECT , EHealPower : : PERMANENT ) ;
drainedLife + = toHeal ;
2014-03-17 22:51:07 +03:00
}
2017-01-20 16:48:45 +02:00
//soul steal handling
2023-05-01 00:20:01 +02:00
if ( attackerState - > hasBonusOfType ( BonusType : : SOUL_STEAL ) & & def - > isLiving ( ) )
2017-01-20 16:48:45 +02:00
{
2017-07-20 06:08:49 +02:00
//we can have two bonuses - one with subtype 0 and another with subtype 1
//try to use permanent first, use only one of two
for ( si32 subtype = 1 ; subtype > = 0 ; subtype - - )
2017-01-29 15:45:36 +02:00
{
2023-05-01 00:20:01 +02:00
if ( attackerState - > hasBonusOfType ( BonusType : : SOUL_STEAL , subtype ) )
2017-01-29 15:45:36 +02:00
{
2023-05-01 00:20:01 +02:00
int64_t toHeal = bsa . killedAmount * attackerState - > valOfBonuses ( BonusType : : SOUL_STEAL , subtype ) * attackerState - > getMaxHealth ( ) ;
2022-09-19 16:45:48 +02:00
attackerState - > heal ( toHeal , EHealLevel : : OVERHEAL , ( ( subtype = = 0 ) ? EHealPower : : ONE_BATTLE : EHealPower : : PERMANENT ) ) ;
drainedLife + = toHeal ;
2017-07-20 06:08:49 +02:00
break ;
2017-01-29 15:45:36 +02:00
}
2017-01-20 16:48:45 +02:00
}
}
2017-03-18 12:25:12 +02:00
bat . bsa . push_back ( bsa ) ; //add this stack to the list of victims after drain life has been calculated
2014-03-17 22:51:07 +03:00
//fire shield handling
2022-12-21 18:04:54 +02:00
if ( ! bat . shot ( ) & &
! def - > isClone ( ) & &
2023-05-01 00:20:01 +02:00
def - > hasBonusOfType ( BonusType : : FIRE_SHIELD ) & &
! attackerState - > hasBonusOfType ( BonusType : : FIRE_IMMUNITY ) & &
2022-12-21 18:04:54 +02:00
CStack : : isMeleeAttackPossible ( attackerState . get ( ) , def ) // attacked needs to be adjacent to defender for fire shield to trigger (e.g. Dragon Breath attack)
)
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
//TODO: use damage with bonus but without penalties
2023-05-01 00:20:01 +02:00
auto fireShieldDamage = ( std : : min < int64_t > ( def - > getAvailableHealth ( ) , bsa . damageAmount ) * def - > valOfBonuses ( BonusType : : FIRE_SHIELD ) ) / 100 ;
2017-07-20 06:08:49 +02:00
fireShield . push_back ( std : : make_pair ( def , fireShieldDamage ) ) ;
2014-03-17 22:51:07 +03:00
}
2022-09-19 16:45:48 +02:00
return drainedLife ;
2014-03-17 22:51:07 +03:00
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
void CGameHandler : : sendGenericKilledLog ( const CStack * defender , int32_t killed , bool multiple )
{
if ( killed > 0 )
{
BattleLogMessage blm ;
addGenericKilledLog ( blm , defender , killed , multiple ) ;
sendAndApply ( & blm ) ;
}
}
void CGameHandler : : addGenericKilledLog ( BattleLogMessage & blm , const CStack * defender , int32_t killed , bool multiple )
{
if ( killed > 0 )
{
const int32_t txtIndex = ( killed > 1 ) ? 379 : 378 ;
2022-12-27 23:17:41 +02:00
std : : string formatString = VLC - > generaltexth - > allTexts [ txtIndex ] ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
// these default h3 texts have unnecessary new lines, so get rid of them before displaying (and trim just in case, trimming newlines does not works for some reason)
formatString . erase ( std : : remove ( formatString . begin ( ) , formatString . end ( ) , ' \n ' ) , formatString . end ( ) ) ;
formatString . erase ( std : : remove ( formatString . begin ( ) , formatString . end ( ) , ' \r ' ) , formatString . end ( ) ) ;
boost : : algorithm : : trim ( formatString ) ;
boost : : format txt ( formatString ) ;
if ( killed > 1 )
{
2023-04-12 00:52:12 +02:00
txt % killed % ( multiple ? VLC - > generaltexth - > allTexts [ 43 ] : defender - > unitType ( ) - > getNamePluralTranslated ( ) ) ; // creatures perish
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
}
else //killed == 1
{
2023-04-12 00:52:12 +02:00
txt % ( multiple ? VLC - > generaltexth - > allTexts [ 42 ] : defender - > unitType ( ) - > getNameSingularTranslated ( ) ) ; // creature perishes
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
}
MetaString line ;
2022-09-21 09:38:42 +02:00
line < < txt . str ( ) ;
blm . lines . push_back ( std : : move ( line ) ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
}
}
2018-01-05 19:21:07 +02:00
void CGameHandler : : handleClientDisconnection ( std : : shared_ptr < CConnection > c )
{
2022-10-04 22:51:34 +02:00
if ( lobby - > state = = EServerState : : SHUTDOWN | | ! gs | | ! gs - > scenarioOps )
return ;
for ( auto & playerConnections : connections )
2017-05-31 08:45:26 +02:00
{
2022-10-04 22:51:34 +02:00
PlayerColor playerId = playerConnections . first ;
2022-10-05 17:04:51 +02:00
auto * playerSettings = gs - > scenarioOps - > getPlayersSettings ( playerId . getNum ( ) ) ;
if ( ! playerSettings )
continue ;
2022-10-08 17:52:29 +02:00
auto playerConnection = vstd : : find ( playerConnections . second , c ) ;
if ( playerConnection ! = playerConnections . second . end ( ) )
2017-05-31 08:45:26 +02:00
{
2022-10-08 17:52:29 +02:00
std : : string messageText = boost : : str ( boost : : format ( " %s (cid %d) was disconnected " ) % playerSettings - > name % c - > connectionID ) ;
playerMessage ( playerId , messageText , ObjectInstanceID { } ) ;
2017-05-31 08:45:26 +02:00
}
2022-10-04 22:51:34 +02:00
}
2018-01-05 19:21:07 +02:00
}
2016-08-30 00:11:54 +02:00
2018-01-05 19:21:07 +02:00
void CGameHandler : : handleReceivedPack ( CPackForServer * pack )
{
//prepare struct informing that action was applied
auto sendPackageResponse = [ & ] ( bool succesfullyApplied )
{
PackageApplied applied ;
applied . player = pack - > player ;
applied . result = succesfullyApplied ;
applied . packType = typeList . getTypeID ( pack ) ;
applied . requestID = pack - > requestID ;
pack - > c - > sendPack ( & applied ) ;
} ;
2014-03-17 22:51:07 +03:00
2018-01-05 19:21:07 +02:00
CBaseForGHApply * apply = applier - > getApplier ( typeList . getTypeID ( pack ) ) ; //and appropriate applier object
if ( isBlockedByQueries ( pack , pack - > player ) )
2014-03-17 22:51:07 +03:00
{
2018-01-05 19:21:07 +02:00
sendPackageResponse ( false ) ;
2017-05-31 08:45:26 +02:00
}
2018-01-05 19:21:07 +02:00
else if ( apply )
2017-05-31 08:45:26 +02:00
{
2023-02-12 09:23:39 +02:00
const bool result = apply - > applyOnGH ( this , this - > gs , pack ) ;
2018-01-05 19:21:07 +02:00
if ( result )
logGlobal - > trace ( " Message %s successfully applied! " , typeid ( * pack ) . name ( ) ) ;
else
complain ( ( boost : : format ( " Got false in applying %s... that request must have been fishy! " )
% typeid ( * pack ) . name ( ) ) . str ( ) ) ;
sendPackageResponse ( true ) ;
2014-03-17 22:51:07 +03:00
}
2018-01-05 19:21:07 +02:00
else
2015-02-14 21:42:47 +02:00
{
2018-01-05 19:21:07 +02:00
logGlobal - > error ( " Message cannot be applied, cannot find applier (unregistered type)! " ) ;
sendPackageResponse ( false ) ;
2015-02-14 21:42:47 +02:00
}
2014-03-17 22:51:07 +03:00
2018-01-05 19:21:07 +02:00
vstd : : clear_pointer ( pack ) ;
2014-03-17 22:51:07 +03:00
}
int CGameHandler : : moveStack ( int stack , BattleHex dest )
{
int ret = 0 ;
const CStack * curStack = gs - > curB - > battleGetStackByID ( stack ) ,
* stackAtEnd = gs - > curB - > battleGetStackByPos ( dest ) ;
assert ( curStack ) ;
assert ( dest < GameConstants : : BFIELD_SIZE ) ;
if ( gs - > curB - > tacticDistance )
{
assert ( gs - > curB - > isInTacticRange ( dest ) ) ;
}
2017-07-20 06:08:49 +02:00
auto start = curStack - > getPosition ( ) ;
2016-10-12 17:16:26 +02:00
if ( start = = dest )
2014-03-17 22:51:07 +03:00
return 0 ;
//initing necessary tables
auto accessibility = getAccesibility ( curStack ) ;
2023-03-27 15:11:17 +02:00
std : : set < BattleHex > passed ;
//Ignore obstacles on starting position
passed . insert ( curStack - > getPosition ( ) ) ;
if ( curStack - > doubleWide ( ) )
passed . insert ( curStack - > occupiedHex ( ) ) ;
2014-03-17 22:51:07 +03:00
//shifting destination (if we have double wide stack and we can occupy dest but not be exactly there)
2017-07-01 10:34:00 +02:00
if ( ! stackAtEnd & & curStack - > doubleWide ( ) & & ! accessibility . accessible ( dest , curStack ) )
2014-03-17 22:51:07 +03:00
{
2017-07-03 11:59:33 +02:00
BattleHex shifted = dest . cloneInDirection ( curStack - > destShiftDir ( ) , false ) ;
2017-07-01 10:34:00 +02:00
if ( accessibility . accessible ( shifted , curStack ) )
dest = shifted ;
2014-03-17 22:51:07 +03:00
}
2017-07-01 10:34:00 +02:00
if ( ( stackAtEnd & & stackAtEnd ! = curStack & & stackAtEnd - > alive ( ) ) | | ! accessibility . accessible ( dest , curStack ) )
2014-03-17 22:51:07 +03:00
{
complain ( " Given destination is not accessible! " ) ;
return 0 ;
}
2016-02-13 16:40:31 +02:00
bool canUseGate = false ;
auto dbState = gs - > curB - > si . gateState ;
2023-04-27 19:43:20 +02:00
if ( battleGetSiegeLevel ( ) > 0 & & curStack - > unitSide ( ) = = BattleSide : : DEFENDER & &
2016-02-13 16:40:31 +02:00
dbState ! = EGateState : : DESTROYED & &
dbState ! = EGateState : : BLOCKED )
2016-02-09 10:30:09 +02:00
{
2016-02-13 16:40:31 +02:00
canUseGate = true ;
2016-02-09 10:30:09 +02:00
}
2016-02-09 09:45:59 +02:00
std : : pair < std : : vector < BattleHex > , int > path = gs - > curB - > getPath ( start , dest , curStack ) ;
2014-03-17 22:51:07 +03:00
ret = path . second ;
2023-05-02 00:05:59 +02:00
int creSpeed = curStack - > speed ( 0 , true ) ;
2023-03-30 11:20:13 +02:00
if ( gs - > curB - > tacticDistance > 0 & & creSpeed > 0 )
creSpeed = GameConstants : : BFIELD_SIZE ;
2014-03-17 22:51:07 +03:00
2023-03-23 00:16:11 +02:00
bool hasWideMoat = vstd : : contains_if ( battleGetAllObstaclesOnPos ( BattleHex ( ESiegeHex : : GATE_BRIDGE ) , false ) , [ ] ( const std : : shared_ptr < const CObstacleInstance > & obst )
{
return obst - > obstacleType = = CObstacleInstance : : MOAT ;
} ) ;
2014-03-17 22:51:07 +03:00
2016-02-09 10:59:20 +02:00
auto isGateDrawbridgeHex = [ & ] ( BattleHex hex ) - > bool
{
2023-03-23 00:16:11 +02:00
if ( hasWideMoat & & hex = = ESiegeHex : : GATE_BRIDGE )
2016-02-09 10:59:20 +02:00
return true ;
2016-10-12 17:16:26 +02:00
if ( hex = = ESiegeHex : : GATE_OUTER )
2016-02-09 10:59:20 +02:00
return true ;
2016-10-12 17:16:26 +02:00
if ( hex = = ESiegeHex : : GATE_INNER )
2016-02-09 10:59:20 +02:00
return true ;
return false ;
} ;
auto occupyGateDrawbridgeHex = [ & ] ( BattleHex hex ) - > bool
{
2016-10-12 17:16:26 +02:00
if ( isGateDrawbridgeHex ( hex ) )
2016-02-09 10:59:20 +02:00
return true ;
2016-10-12 17:16:26 +02:00
if ( curStack - > doubleWide ( ) )
2016-02-09 10:59:20 +02:00
{
BattleHex otherHex = curStack - > occupiedHex ( hex ) ;
2016-10-12 17:16:26 +02:00
if ( otherHex . isValid ( ) & & isGateDrawbridgeHex ( otherHex ) )
2016-02-09 10:59:20 +02:00
return true ;
}
return false ;
} ;
2023-05-01 00:20:01 +02:00
if ( curStack - > hasBonusOfType ( BonusType : : FLYING ) )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( path . second < = creSpeed & & path . first . size ( ) > 0 )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( canUseGate & & dbState ! = EGateState : : OPENED & &
2016-02-09 10:59:20 +02:00
occupyGateDrawbridgeHex ( dest ) )
2016-02-09 10:30:09 +02:00
{
2016-02-13 16:40:31 +02:00
BattleUpdateGateState db ;
db . state = EGateState : : OPENED ;
2016-02-09 10:30:09 +02:00
sendAndApply ( & db ) ;
}
2014-03-17 22:51:07 +03:00
//inform clients about move
BattleStackMoved sm ;
2023-04-27 19:43:20 +02:00
sm . stack = curStack - > unitId ( ) ;
2014-03-17 22:51:07 +03:00
std : : vector < BattleHex > tiles ;
tiles . push_back ( path . first [ 0 ] ) ;
sm . tilesToMove = tiles ;
sm . distance = path . second ;
sm . teleporting = false ;
sendAndApply ( & sm ) ;
}
}
else //for non-flying creatures
{
std : : vector < BattleHex > tiles ;
2015-04-12 08:26:50 +02:00
const int tilesToMove = std : : max ( ( int ) ( path . first . size ( ) - creSpeed ) , 0 ) ;
2020-10-01 10:38:06 +02:00
int v = ( int ) path . first . size ( ) - 1 ;
2016-02-09 09:45:59 +02:00
path . first . push_back ( start ) ;
2014-03-17 22:51:07 +03:00
2016-02-08 11:15:07 +02:00
// check if gate need to be open or closed at some point
BattleHex openGateAtHex , gateMayCloseAtHex ;
2016-10-12 17:16:26 +02:00
if ( canUseGate )
2016-02-08 11:15:07 +02:00
{
2020-10-01 10:38:06 +02:00
for ( int i = ( int ) path . first . size ( ) - 1 ; i > = 0 ; i - - )
2016-02-08 11:15:07 +02:00
{
2016-02-09 14:41:37 +02:00
auto needOpenGates = [ & ] ( BattleHex hex ) - > bool
2016-02-08 11:15:07 +02:00
{
2023-03-23 00:16:11 +02:00
if ( hasWideMoat & & hex = = ESiegeHex : : GATE_BRIDGE )
2016-02-09 14:41:37 +02:00
return true ;
2016-10-12 17:16:26 +02:00
if ( hex = = ESiegeHex : : GATE_BRIDGE & & i - 1 > = 0 & & path . first [ i - 1 ] = = ESiegeHex : : GATE_OUTER )
2016-02-09 14:41:37 +02:00
return true ;
2016-10-12 17:16:26 +02:00
else if ( hex = = ESiegeHex : : GATE_OUTER | | hex = = ESiegeHex : : GATE_INNER )
2016-02-09 14:41:37 +02:00
return true ;
return false ;
} ;
auto hex = path . first [ i ] ;
2016-10-12 17:16:26 +02:00
if ( ! openGateAtHex . isValid ( ) & & dbState ! = EGateState : : OPENED )
2016-02-09 14:41:37 +02:00
{
2016-10-12 17:16:26 +02:00
if ( needOpenGates ( hex ) )
2016-02-08 11:15:07 +02:00
openGateAtHex = path . first [ i + 1 ] ;
2016-02-09 14:41:37 +02:00
//TODO we need find batter way to handle double-wide stacks
//currently if only second occupied stack part is standing on gate / bridge hex then stack will start to wait for bridge to lower before it's needed. Though this is just a visual bug.
2016-10-12 17:16:26 +02:00
if ( curStack - > doubleWide ( ) )
2016-02-09 14:41:37 +02:00
{
BattleHex otherHex = curStack - > occupiedHex ( hex ) ;
2016-10-12 17:16:26 +02:00
if ( otherHex . isValid ( ) & & needOpenGates ( otherHex ) )
2016-02-09 14:41:37 +02:00
openGateAtHex = path . first [ i + 2 ] ;
2016-02-08 11:15:07 +02:00
}
2016-02-09 09:45:59 +02:00
//gate may be opened and then closed during stack movement, but not other way around
2016-10-12 17:16:26 +02:00
if ( openGateAtHex . isValid ( ) )
2016-02-13 16:40:31 +02:00
dbState = EGateState : : OPENED ;
2016-02-08 11:15:07 +02:00
}
2016-10-12 17:16:26 +02:00
if ( ! gateMayCloseAtHex . isValid ( ) & & dbState ! = EGateState : : CLOSED )
2016-02-08 11:15:07 +02:00
{
2016-10-12 17:16:26 +02:00
if ( hex = = ESiegeHex : : GATE_INNER & & i - 1 > = 0 & & path . first [ i - 1 ] ! = ESiegeHex : : GATE_OUTER )
2016-02-08 11:15:07 +02:00
{
gateMayCloseAtHex = path . first [ i - 1 ] ;
}
2023-03-23 00:16:11 +02:00
if ( hasWideMoat )
2016-02-08 11:15:07 +02:00
{
2016-10-12 17:16:26 +02:00
if ( hex = = ESiegeHex : : GATE_BRIDGE & & i - 1 > = 0 & & path . first [ i - 1 ] ! = ESiegeHex : : GATE_OUTER )
2016-02-08 11:15:07 +02:00
{
gateMayCloseAtHex = path . first [ i - 1 ] ;
}
2016-10-12 17:16:26 +02:00
else if ( hex = = ESiegeHex : : GATE_OUTER & & i - 1 > = 0 & &
2016-02-09 16:38:59 +02:00
path . first [ i - 1 ] ! = ESiegeHex : : GATE_INNER & &
path . first [ i - 1 ] ! = ESiegeHex : : GATE_BRIDGE )
2016-02-08 11:15:07 +02:00
{
gateMayCloseAtHex = path . first [ i - 1 ] ;
}
}
2016-10-12 17:16:26 +02:00
else if ( hex = = ESiegeHex : : GATE_OUTER & & i - 1 > = 0 & & path . first [ i - 1 ] ! = ESiegeHex : : GATE_INNER )
2016-02-08 11:15:07 +02:00
{
gateMayCloseAtHex = path . first [ i - 1 ] ;
}
}
}
}
2014-03-17 22:51:07 +03:00
2015-04-12 07:12:07 +02:00
bool stackIsMoving = true ;
2015-10-28 22:53:44 +02:00
2015-04-12 07:12:07 +02:00
while ( stackIsMoving )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( v < tilesToMove )
2014-03-17 22:51:07 +03:00
{
2015-04-12 07:12:07 +02:00
logGlobal - > error ( " Movement terminated abnormally " ) ;
2014-03-17 22:51:07 +03:00
break ;
}
2015-04-12 08:26:50 +02:00
2016-02-08 11:15:07 +02:00
bool gateStateChanging = false ;
2016-02-09 09:45:59 +02:00
//special handling for opening gate on from starting hex
2016-10-12 17:16:26 +02:00
if ( openGateAtHex . isValid ( ) & & openGateAtHex = = start )
2016-02-09 09:45:59 +02:00
gateStateChanging = true ;
else
2015-04-12 07:12:07 +02:00
{
2016-10-12 17:16:26 +02:00
for ( bool obstacleHit = false ; ( ! obstacleHit ) & & ( ! gateStateChanging ) & & ( v > = tilesToMove ) ; - - v )
2015-04-12 07:12:07 +02:00
{
2016-02-09 09:45:59 +02:00
BattleHex hex = path . first [ v ] ;
tiles . push_back ( hex ) ;
2015-04-12 08:26:50 +02:00
2016-10-12 17:16:26 +02:00
if ( ( openGateAtHex . isValid ( ) & & openGateAtHex = = hex ) | |
2016-02-09 09:45:59 +02:00
( gateMayCloseAtHex . isValid ( ) & & gateMayCloseAtHex = = hex ) )
{
gateStateChanging = true ;
}
2015-04-12 08:26:50 +02:00
2016-02-09 09:45:59 +02:00
//if we walked onto something, finalize this portion of stack movement check into obstacle
2017-07-09 10:18:46 +02:00
if ( ! battleGetAllObstaclesOnPos ( hex , false ) . empty ( ) )
2015-04-12 08:26:50 +02:00
obstacleHit = true ;
2016-02-09 09:45:59 +02:00
2016-10-12 17:16:26 +02:00
if ( curStack - > doubleWide ( ) )
2016-02-09 09:45:59 +02:00
{
BattleHex otherHex = curStack - > occupiedHex ( hex ) ;
//two hex creature hit obstacle by backside
2017-07-09 10:18:46 +02:00
auto obstacle2 = battleGetAllObstaclesOnPos ( otherHex , false ) ;
2017-07-01 17:59:53 +02:00
if ( otherHex . isValid ( ) & & ! obstacle2 . empty ( ) )
2016-02-09 09:45:59 +02:00
obstacleHit = true ;
}
2023-03-27 15:11:17 +02:00
if ( ! obstacleHit )
passed . insert ( hex ) ;
2015-04-12 07:12:07 +02:00
}
}
2023-03-27 15:11:17 +02:00
if ( ! tiles . empty ( ) )
2014-03-17 22:51:07 +03:00
{
2015-04-12 07:12:07 +02:00
//commit movement
BattleStackMoved sm ;
2023-04-27 19:43:20 +02:00
sm . stack = curStack - > unitId ( ) ;
2015-04-12 07:12:07 +02:00
sm . distance = path . second ;
sm . teleporting = false ;
sm . tilesToMove = tiles ;
sendAndApply ( & sm ) ;
2014-03-17 22:51:07 +03:00
tiles . clear ( ) ;
}
2015-04-12 07:12:07 +02:00
//we don't handle obstacle at the destination tile -> it's handled separately in the if at the end
2017-07-20 06:08:49 +02:00
if ( curStack - > getPosition ( ) ! = dest )
2015-04-12 07:12:07 +02:00
{
2017-07-20 06:08:49 +02:00
if ( stackIsMoving & & start ! = curStack - > getPosition ( ) )
2023-03-27 15:11:17 +02:00
{
2023-04-12 15:20:40 +02:00
stackIsMoving = handleObstacleTriggersForUnit ( * spellEnv , * curStack , passed ) ;
2023-03-27 15:11:17 +02:00
passed . insert ( curStack - > getPosition ( ) ) ;
if ( curStack - > doubleWide ( ) )
passed . insert ( curStack - > occupiedHex ( ) ) ;
}
2016-10-12 17:16:26 +02:00
if ( gateStateChanging )
2016-02-08 11:15:07 +02:00
{
2017-07-20 06:08:49 +02:00
if ( curStack - > getPosition ( ) = = openGateAtHex )
2016-02-08 11:15:07 +02:00
{
2016-02-09 09:45:59 +02:00
openGateAtHex = BattleHex ( ) ;
//only open gate if stack is still alive
2016-10-12 17:16:26 +02:00
if ( curStack - > alive ( ) )
2016-02-09 09:45:59 +02:00
{
2016-02-13 16:40:31 +02:00
BattleUpdateGateState db ;
db . state = EGateState : : OPENED ;
2016-02-09 09:45:59 +02:00
sendAndApply ( & db ) ;
}
2016-02-08 11:15:07 +02:00
}
2017-07-20 06:08:49 +02:00
else if ( curStack - > getPosition ( ) = = gateMayCloseAtHex )
2016-02-09 09:45:59 +02:00
{
2016-02-09 14:41:37 +02:00
gateMayCloseAtHex = BattleHex ( ) ;
2016-02-13 16:40:31 +02:00
updateGateState ( ) ;
2016-02-09 09:45:59 +02:00
}
2016-02-08 11:15:07 +02:00
}
2015-04-12 07:12:07 +02:00
}
else
2015-04-12 08:26:50 +02:00
//movement finished normally: we reached destination
2015-04-12 07:12:07 +02:00
stackIsMoving = false ;
2014-03-17 22:51:07 +03:00
}
}
2023-03-27 01:29:20 +02:00
//handle last hex separately for deviation
if ( VLC - > settings ( ) - > getBoolean ( EGameSettings : : COMBAT_ONE_HEX_TRIGGERS_OBSTACLES ) )
{
2023-04-27 19:43:20 +02:00
if ( dest = = battle : : Unit : : occupiedHex ( start , curStack - > doubleWide ( ) , curStack - > unitSide ( ) )
| | start = = battle : : Unit : : occupiedHex ( dest , curStack - > doubleWide ( ) , curStack - > unitSide ( ) ) )
2023-03-27 01:29:20 +02:00
passed . clear ( ) ; //Just empty passed, obstacles will handled automatically
}
2014-03-17 22:51:07 +03:00
//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
2023-04-12 15:20:40 +02:00
handleObstacleTriggersForUnit ( * spellEnv , * curStack , passed ) ;
2015-04-12 07:12:07 +02:00
2014-03-17 22:51:07 +03:00
return ret ;
}
2018-01-05 19:21:07 +02:00
CGameHandler : : CGameHandler ( CVCMIServer * lobby )
: lobby ( lobby )
2021-11-28 14:57:38 +02:00
, complainNoCreatures ( " No creatures to split " )
, complainNotEnoughCreatures ( " Cannot split that stack, not enough creatures! " )
, complainInvalidSlot ( " Invalid slot accessed! " )
2014-03-17 22:51:07 +03:00
{
QID = 1 ;
IObjectInterface : : cb = this ;
2018-01-05 19:21:07 +02:00
applier = std : : make_shared < CApplier < CBaseForGHApply > > ( ) ;
2014-03-17 22:51:07 +03:00
registerTypesServerPacks ( * applier ) ;
visitObjectAfterVictory = false ;
2015-10-28 22:53:44 +02:00
2014-11-25 21:00:04 +02:00
spellEnv = new ServerSpellCastEnvironment ( this ) ;
2014-03-17 22:51:07 +03:00
}
2018-01-13 10:43:26 +02:00
CGameHandler : : ~ CGameHandler ( )
2014-03-17 22:51:07 +03:00
{
2023-03-25 21:29:33 +02:00
if ( battleThread )
{
//Setting battleMadeAction is needed because battleThread waits for the action to continue the main loop
battleMadeAction . setn ( true ) ;
battleThread - > join ( ) ;
}
2014-11-25 21:00:04 +02:00
delete spellEnv ;
2014-03-17 22:51:07 +03:00
delete gs ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
void CGameHandler : : reinitScripting ( )
{
2022-12-07 23:36:20 +02:00
serverEventBus = std : : make_unique < events : : EventBus > ( ) ;
2022-09-21 18:31:14 +02:00
# if SCRIPTING_ENABLED
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
serverScripts . reset ( new scripting : : PoolImpl ( this , spellEnv ) ) ;
2022-09-21 18:31:14 +02:00
# endif
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
}
2014-03-17 22:51:07 +03:00
void CGameHandler : : init ( StartInfo * si )
{
2016-10-12 17:16:26 +02:00
if ( si - > seedToBeUsed = = 0 )
2014-03-17 22:51:07 +03:00
{
2020-10-01 10:38:06 +02:00
si - > seedToBeUsed = static_cast < ui32 > ( std : : time ( nullptr ) ) ;
2014-03-17 22:51:07 +03:00
}
2017-07-20 06:08:49 +02:00
CMapService mapService ;
2014-03-17 22:51:07 +03:00
gs = new CGameState ( ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
gs - > preInit ( VLC ) ;
2016-08-30 00:11:54 +02:00
logGlobal - > info ( " Gamestate created! " ) ;
2017-07-20 06:08:49 +02:00
gs - > init ( & mapService , si ) ;
2016-08-30 00:11:54 +02:00
logGlobal - > info ( " Gamestate initialized! " ) ;
2014-03-17 22:51:07 +03:00
2014-04-10 20:11:09 +03:00
// reset seed, so that clients can't predict any following random values
2016-09-07 23:54:26 +02:00
getRandomGenerator ( ) . resetSeed ( ) ;
2014-04-10 20:11:09 +03:00
2016-10-12 17:16:26 +02:00
for ( auto & elem : gs - > players )
2014-03-17 22:51:07 +03:00
{
states . addPlayer ( elem . first ) ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
reinitScripting ( ) ;
2014-03-17 22:51:07 +03:00
}
static bool evntCmp ( const CMapEvent & a , const CMapEvent & b )
{
2016-03-12 03:41:27 +02:00
return a . earlierThan ( b ) ;
2014-03-17 22:51:07 +03:00
}
void CGameHandler : : setPortalDwelling ( const CGTownInstance * town , bool forced = false , bool clear = false )
{ // bool forced = true - if creature should be replaced, if false - only if no creature was set
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const PlayerState * p = getPlayerState ( town - > tempOwner ) ;
2016-10-12 17:16:26 +02:00
if ( ! p )
2014-03-17 22:51:07 +03:00
{
2023-01-04 15:17:50 +02:00
logGlobal - > warn ( " There is no player owner of town %s at %s " , town - > getNameTranslated ( ) , town - > pos . toString ( ) ) ;
2014-03-17 22:51:07 +03:00
return ;
}
if ( forced | | town - > creatures . at ( GameConstants : : CREATURES_PER_TOWN ) . second . empty ( ) ) //we need to change creature
{
SetAvailableCreatures ssi ;
ssi . tid = town - > id ;
ssi . creatures = town - > creatures ;
ssi . creatures [ GameConstants : : CREATURES_PER_TOWN ] . second . clear ( ) ; //remove old one
const std : : vector < ConstTransitivePtr < CGDwelling > > & dwellings = p - > dwellings ;
if ( dwellings . empty ( ) ) //no dwellings - just remove
{
sendAndApply ( & ssi ) ;
return ;
}
2016-09-07 23:54:26 +02:00
auto dwelling = * RandomGeneratorUtil : : nextItem ( dwellings , getRandomGenerator ( ) ) ;
2014-04-10 20:11:09 +03:00
// for multi-creature dwellings like Golem Factory
2016-09-07 23:54:26 +02:00
auto creatureId = RandomGeneratorUtil : : nextItem ( dwelling - > creatures , getRandomGenerator ( ) ) - > second [ 0 ] ;
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( clear )
2014-04-10 20:11:09 +03:00
{
2023-04-05 02:26:29 +02:00
ssi . creatures [ GameConstants : : CREATURES_PER_TOWN ] . first = std : : max ( 1 , ( VLC - > creh - > objects . at ( creatureId ) - > getGrowth ( ) ) / 2 ) ;
2014-04-10 20:11:09 +03:00
}
2014-03-17 22:51:07 +03:00
else
2014-04-10 20:11:09 +03:00
{
2023-04-05 02:26:29 +02:00
ssi . creatures [ GameConstants : : CREATURES_PER_TOWN ] . first = VLC - > creh - > objects . at ( creatureId ) - > getGrowth ( ) ;
2014-04-10 20:11:09 +03:00
}
ssi . creatures [ GameConstants : : CREATURES_PER_TOWN ] . second . push_back ( creatureId ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & ssi ) ;
}
}
void CGameHandler : : newTurn ( )
{
2016-08-30 00:11:54 +02:00
logGlobal - > trace ( " Turn %d " , gs - > day + 1 ) ;
2014-03-17 22:51:07 +03:00
NewTurn n ;
n . specialWeek = NewTurn : : NO_ACTION ;
n . creatureid = CreatureID : : NONE ;
n . day = gs - > day + 1 ;
bool firstTurn = ! getDate ( Date : : DAY ) ;
bool newWeek = getDate ( Date : : DAY_OF_WEEK ) = = 7 ; //day numbers are confusing, as day was not yet switched
bool newMonth = getDate ( Date : : DAY_OF_MONTH ) = = 28 ;
std : : map < PlayerColor , si32 > hadGold ; //starting gold - for buildings like dwarven treasury
if ( firstTurn )
{
for ( auto obj : gs - > map - > objects )
{
if ( obj & & obj - > ID = = Obj : : PRISON ) //give imprisoned hero 0 exp to level him up. easiest to do at this point
{
changePrimSkill ( getHero ( obj - > id ) , PrimarySkill : : EXPERIENCE , 0 ) ;
}
}
}
if ( newWeek & & ! firstTurn )
{
n . specialWeek = NewTurn : : NORMAL ;
bool deityOfFireBuilt = false ;
2016-10-12 17:16:26 +02:00
for ( const CGTownInstance * t : gs - > map - > towns )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( t - > hasBuilt ( BuildingID : : GRAIL , ETownType : : INFERNO ) )
2014-03-17 22:51:07 +03:00
{
deityOfFireBuilt = true ;
break ;
}
}
2016-10-12 17:16:26 +02:00
if ( deityOfFireBuilt )
2014-03-17 22:51:07 +03:00
{
n . specialWeek = NewTurn : : DEITYOFFIRE ;
n . creatureid = CreatureID : : IMP ;
}
2023-04-28 15:54:00 +02:00
else if ( VLC - > settings ( ) - > getBoolean ( EGameSettings : : CREATURES_ALLOW_RANDOM_SPECIAL_WEEKS ) )
2014-03-17 22:51:07 +03:00
{
2016-09-07 23:54:26 +02:00
int monthType = getRandomGenerator ( ) . nextInt ( 99 ) ;
2016-10-12 17:16:26 +02:00
if ( newMonth ) //new month
2014-03-17 22:51:07 +03:00
{
if ( monthType < 40 ) //double growth
{
n . specialWeek = NewTurn : : DOUBLE_GROWTH ;
2023-03-15 23:47:26 +02:00
if ( VLC - > settings ( ) - > getBoolean ( EGameSettings : : CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH ) )
2014-03-17 22:51:07 +03:00
{
2023-03-15 23:47:26 +02:00
n . creatureid = VLC - > creh - > pickRandomMonster ( getRandomGenerator ( ) ) ;
2014-03-17 22:51:07 +03:00
}
2016-10-12 17:16:26 +02:00
else if ( VLC - > creh - > doubledCreatures . size ( ) )
2014-03-17 22:51:07 +03:00
{
2023-03-15 23:47:26 +02:00
n . creatureid = * RandomGeneratorUtil : : nextItem ( VLC - > creh - > doubledCreatures , getRandomGenerator ( ) ) ;
2014-03-17 22:51:07 +03:00
}
else
{
complain ( " Cannot find creature that can be spawned! " ) ;
n . specialWeek = NewTurn : : NORMAL ;
}
}
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
2019-05-06 15:12:14 +02:00
std : : pair < int , CreatureID > newMonster ( 54 , CreatureID ( ) ) ;
do
{
newMonster . second = VLC - > creh - > pickRandomMonster ( getRandomGenerator ( ) ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
} while ( VLC - > creh - > objects [ newMonster . second ] & &
2023-04-09 17:26:32 +02:00
( * VLC - > townh ) [ VLC - > creatures ( ) - > getById ( newMonster . second ) - > getFaction ( ) ] - > town = = nullptr ) ; // find first non neutral creature
2014-03-17 22:51:07 +03:00
n . creatureid = newMonster . second ;
}
}
}
}
std : : map < ui32 , ConstTransitivePtr < CGHeroInstance > > pool = gs - > hpool . heroesPool ;
2016-10-12 17:16:26 +02:00
for ( auto & hp : pool )
2016-01-28 21:48:51 +02:00
{
auto hero = hp . second ;
2016-10-12 17:16:26 +02:00
if ( hero - > isInitialized ( ) & & hero - > stacks . size ( ) )
2016-01-28 21:48:51 +02:00
{
// reset retreated or surrendered heroes
auto maxmove = hero - > maxMovePoints ( true ) ;
// if movement is greater than maxmove, we should decrease it
2016-10-12 17:16:26 +02:00
if ( hero - > movement ! = maxmove | | hero - > mana < hero - > manaLimit ( ) )
2016-01-28 21:48:51 +02:00
{
NewTurn : : Hero hth ;
hth . id = hero - > id ;
hth . move = maxmove ;
2016-01-30 09:20:49 +02:00
hth . mana = hero - > getManaNewTurn ( ) ;
2016-01-28 21:48:51 +02:00
n . heroes . insert ( hth ) ;
}
}
}
2014-03-17 22:51:07 +03:00
for ( auto & elem : gs - > players )
{
2016-10-12 17:16:26 +02:00
if ( elem . first = = PlayerColor : : NEUTRAL )
2014-03-17 22:51:07 +03:00
continue ;
2016-10-12 17:16:26 +02:00
else if ( elem . first > = PlayerColor : : PLAYER_LIMIT )
2014-03-17 22:51:07 +03:00
assert ( 0 ) ; //illegal player number!
2023-04-05 02:26:29 +02:00
std : : pair < PlayerColor , si32 > playerGold ( elem . first , elem . second . resources [ EGameResID : : GOLD ] ) ;
2014-03-17 22:51:07 +03:00
hadGold . insert ( playerGold ) ;
2016-10-12 17:16:26 +02:00
if ( newWeek ) //new heroes in tavern
2014-03-17 22:51:07 +03:00
{
SetAvailableHeroes sah ;
sah . player = elem . first ;
//pick heroes and their armies
CHeroClass * banned = nullptr ;
for ( int j = 0 ; j < GameConstants : : AVAILABLE_HEROES_PER_PLAYER ; j + + )
{
//first hero - native if possible, second hero -> any other class
2016-10-12 17:16:26 +02:00
if ( CGHeroInstance * h = gs - > hpool . pickHeroFor ( j = = 0 , elem . first , getNativeTown ( elem . first ) , pool , getRandomGenerator ( ) , banned ) )
2014-03-17 22:51:07 +03:00
{
sah . hid [ j ] = h - > subID ;
2016-08-23 07:13:52 +02:00
h - > initArmy ( getRandomGenerator ( ) , & sah . army [ j ] ) ;
2014-03-17 22:51:07 +03:00
banned = h - > type - > heroClass ;
}
else
2016-01-28 21:48:51 +02:00
{
2014-03-17 22:51:07 +03:00
sah . hid [ j ] = - 1 ;
2016-01-28 21:48:51 +02:00
}
2014-03-17 22:51:07 +03:00
}
sendAndApply ( & sah ) ;
}
n . res [ elem . first ] = elem . second . resources ;
2019-03-17 20:48:47 +02:00
if ( ! firstTurn & & newWeek ) //weekly crystal generation if 1 or more crystal dragons in any hero army or town garrison
{
bool hasCrystalGenCreature = false ;
for ( CGHeroInstance * hero : elem . second . heroes )
{
for ( auto stack : hero - > stacks )
{
2023-05-01 00:20:01 +02:00
if ( stack . second - > hasBonusOfType ( BonusType : : SPECIAL_CRYSTAL_GENERATION ) )
2019-03-17 20:48:47 +02:00
{
hasCrystalGenCreature = true ;
break ;
}
}
}
if ( ! hasCrystalGenCreature ) //not found in armies, check towns
{
for ( CGTownInstance * town : elem . second . towns )
{
for ( auto stack : town - > stacks )
{
2023-05-01 00:20:01 +02:00
if ( stack . second - > hasBonusOfType ( BonusType : : SPECIAL_CRYSTAL_GENERATION ) )
2019-03-17 20:48:47 +02:00
{
hasCrystalGenCreature = true ;
break ;
}
}
}
}
if ( hasCrystalGenCreature )
2023-04-05 02:26:29 +02:00
n . res [ elem . first ] [ EGameResID : : CRYSTAL ] + = 3 ;
2019-03-17 20:48:47 +02:00
}
2016-10-12 17:16:26 +02:00
for ( CGHeroInstance * h : ( elem ) . second . heroes )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( h - > visitedTown )
2014-03-17 22:51:07 +03:00
giveSpells ( h - > visitedTown , h ) ;
NewTurn : : Hero hth ;
hth . id = h - > id ;
2022-12-07 23:36:20 +02:00
auto ti = std : : make_unique < TurnInfo > ( h , 1 ) ;
2015-12-25 10:09:06 +02:00
// TODO: this code executed when bonuses of previous day not yet updated (this happen in NewTurn::applyGs). See issue 2356
2022-12-07 22:10:08 +02:00
hth . move = h - > maxMovePointsCached ( gs - > map - > getTile ( h - > visitablePos ( ) ) . terType - > isLand ( ) , ti . get ( ) ) ;
2016-01-30 09:20:49 +02:00
hth . mana = h - > getManaNewTurn ( ) ;
2014-03-17 22:51:07 +03:00
n . heroes . insert ( hth ) ;
2016-10-12 17:16:26 +02:00
if ( ! firstTurn ) //not first day
2014-03-17 22:51:07 +03:00
{
for ( int k = 0 ; k < GameConstants : : RESOURCE_QUANTITY ; k + + )
{
2023-05-01 00:20:01 +02:00
n . res [ elem . first ] [ k ] + = h - > valOfBonuses ( BonusType : : GENERATE_RESOURCE , k ) ;
2014-03-17 22:51:07 +03:00
}
}
}
}
2016-10-12 17:16:26 +02:00
for ( CGTownInstance * t : gs - > map - > towns )
2014-03-17 22:51:07 +03:00
{
PlayerColor player = t - > tempOwner ;
handleTownEvents ( t , n ) ;
2016-10-12 17:16:26 +02:00
if ( newWeek ) //first day of week
2014-03-17 22:51:07 +03:00
{
2020-10-15 14:03:01 +02:00
if ( t - > hasBuilt ( BuildingSubID : : PORTAL_OF_SUMMONING ) )
2014-03-17 22:51:07 +03:00
setPortalDwelling ( t , true , ( n . specialWeek = = NewTurn : : PLAGUE ? true : false ) ) ; //set creatures for Portal of Summoning
2016-10-12 17:16:26 +02:00
if ( ! firstTurn )
2020-10-19 21:39:57 +02:00
if ( t - > hasBuilt ( BuildingSubID : : TREASURY ) & & player < PlayerColor : : PLAYER_LIMIT )
2023-04-05 02:26:29 +02:00
n . res [ player ] [ EGameResID : : GOLD ] + = hadGold . at ( player ) / 10 ; //give 10% of starting gold
2014-03-17 22:51:07 +03:00
if ( ! vstd : : contains ( n . cres , t - > id ) )
{
n . cres [ t - > id ] . tid = t - > id ;
n . cres [ t - > id ] . creatures = t - > creatures ;
}
auto & sac = n . cres . at ( t - > id ) ;
for ( int k = 0 ; k < GameConstants : : CREATURES_PER_TOWN ; k + + ) //creature growths
{
if ( ! t - > creatures . at ( k ) . second . empty ( ) ) // there are creatures at this level
{
ui32 & availableCount = sac . creatures . at ( k ) . first ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const CCreature * cre = VLC - > creh - > objects . at ( t - > creatures . at ( k ) . second . back ( ) ) ;
2014-03-17 22:51:07 +03:00
if ( n . specialWeek = = NewTurn : : PLAGUE )
availableCount = t - > creatures . at ( k ) . first / 2 ; //halve their number, no growth
else
{
2016-10-12 17:16:26 +02:00
if ( firstTurn ) //first day of game: use only basic growths
2023-04-05 02:26:29 +02:00
availableCount = cre - > getGrowth ( ) ;
2014-03-17 22:51:07 +03:00
else
availableCount + = t - > creatureGrowth ( k ) ;
//Deity of fire week - upgrade both imps and upgrades
if ( n . specialWeek = = NewTurn : : DEITYOFFIRE & & vstd : : contains ( t - > creatures . at ( k ) . second , n . creatureid ) )
availableCount + = 15 ;
2023-04-05 02:26:29 +02:00
if ( cre - > getId ( ) = = n . creatureid ) //bonus week, effect applies only to identical creatures
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( n . specialWeek = = NewTurn : : DOUBLE_GROWTH )
2014-03-17 22:51:07 +03:00
availableCount * = 2 ;
2016-10-12 17:16:26 +02:00
else if ( n . specialWeek = = NewTurn : : BONUS_GROWTH )
2014-03-17 22:51:07 +03:00
availableCount + = 5 ;
}
}
}
}
}
2016-10-12 17:16:26 +02:00
if ( ! firstTurn & & player < PlayerColor : : PLAYER_LIMIT ) //not the first day and town not neutral
2014-03-17 22:51:07 +03:00
{
2014-04-26 17:23:35 +03:00
n . res [ player ] = n . res [ player ] + t - > dailyIncome ( ) ;
2014-03-17 22:51:07 +03:00
}
2021-10-29 10:45:10 +02:00
if ( t - > hasBuilt ( BuildingID : : GRAIL )
& & t - > town - > buildings . at ( BuildingID : : GRAIL ) - > height = = CBuilding : : HEIGHT_SKYSHIP )
2014-03-17 22:51:07 +03:00
{
// Skyship, probably easier to handle same as Veil of darkness
//do it every new day after veils apply
if ( player ! = PlayerColor : : NEUTRAL ) //do not reveal fow for neutral player
{
FoWChange fw ;
fw . mode = 1 ;
fw . player = player ;
// find all hidden tiles
2022-09-18 16:39:10 +02:00
const auto fow = getPlayerTeam ( player ) - > fogOfWarMap ;
auto shape = fow - > shape ( ) ;
for ( size_t z = 0 ; z < shape [ 0 ] ; z + + )
for ( size_t x = 0 ; x < shape [ 1 ] ; x + + )
for ( size_t y = 0 ; y < shape [ 2 ] ; y + + )
if ( ! ( * fow ) [ z ] [ x ] [ y ] )
fw . tiles . insert ( int3 ( x , y , z ) ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & fw ) ;
}
}
2023-05-01 00:20:01 +02:00
if ( t - > hasBonusOfType ( BonusType : : DARKNESS ) )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
for ( auto & player : gs - > players )
2014-06-24 14:50:27 +03:00
{
if ( getPlayerStatus ( player . first ) = = EPlayerStatus : : INGAME & &
getPlayerRelations ( player . first , t - > tempOwner ) = = PlayerRelations : : ENEMIES )
2023-05-01 00:20:01 +02:00
changeFogOfWar ( t - > visitablePos ( ) , t - > getBonusLocalFirst ( Selector : : type ( ) ( BonusType : : DARKNESS ) ) - > val , player . first , true ) ;
2014-06-24 14:50:27 +03:00
}
2014-03-17 22:51:07 +03:00
}
}
2016-10-12 17:16:26 +02:00
if ( newMonth )
2014-03-17 22:51:07 +03:00
{
SetAvailableArtifacts saa ;
saa . id = - 1 ;
2016-09-07 23:54:26 +02:00
pickAllowedArtsSet ( saa . arts , getRandomGenerator ( ) ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & saa ) ;
}
sendAndApply ( & n ) ;
2016-10-12 17:16:26 +02:00
if ( newWeek )
2014-03-17 22:51:07 +03:00
{
//spawn wandering monsters
if ( newMonth & & ( n . specialWeek = = NewTurn : : DOUBLE_GROWTH | | n . specialWeek = = NewTurn : : DEITYOFFIRE ) )
{
spawnWanderingMonsters ( n . creatureid ) ;
}
//new week info popup
2016-10-12 17:16:26 +02:00
if ( ! firstTurn )
2014-03-17 22:51:07 +03:00
{
InfoWindow iw ;
switch ( n . specialWeek )
{
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 ;
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 ;
default :
if ( newMonth )
{
iw . text . addTxt ( MetaString : : ARRAY_TXT , ( 130 ) ) ;
2016-09-07 23:54:26 +02:00
iw . text . addReplacement ( MetaString : : ARRAY_TXT , getRandomGenerator ( ) . nextInt ( 32 , 41 ) ) ;
2014-03-17 22:51:07 +03:00
}
else
{
iw . text . addTxt ( MetaString : : ARRAY_TXT , ( 133 ) ) ;
2016-09-07 23:54:26 +02:00
iw . text . addReplacement ( MetaString : : ARRAY_TXT , getRandomGenerator ( ) . nextInt ( 43 , 57 ) ) ;
2014-03-17 22:51:07 +03:00
}
}
for ( auto & elem : gs - > players )
{
iw . player = elem . first ;
sendAndApply ( & iw ) ;
}
}
}
2016-08-30 00:11:54 +02:00
logGlobal - > trace ( " Info about turn %d has been sent! " , n . day ) ;
2014-03-17 22:51:07 +03:00
handleTimeEvents ( ) ;
//call objects
2016-10-12 17:16:26 +02:00
for ( auto & elem : gs - > map - > objects )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( elem )
2016-09-09 19:30:36 +02:00
elem - > newTurn ( getRandomGenerator ( ) ) ;
2014-03-17 22:51:07 +03:00
}
synchronizeArtifactHandlerLists ( ) ; //new day events may have changed them. TODO better of managing that
}
void CGameHandler : : run ( bool resume )
{
LOG_TRACE_PARAMS ( logGlobal , " resume=%d " , resume ) ;
using namespace boost : : posix_time ;
2018-01-05 19:21:07 +02:00
for ( auto cc : lobby - > connections )
2014-03-17 22:51:07 +03:00
{
2018-01-05 19:21:07 +02:00
auto players = lobby - > getAllClientPlayers ( cc - > connectionID ) ;
2016-03-12 03:41:27 +02:00
std : : stringstream sbuffer ;
sbuffer < < " Connection " < < cc - > connectionID < < " will handle " < < players . size ( ) < < " player: " ;
2016-10-12 17:16:26 +02:00
for ( PlayerColor color : players )
2014-03-17 22:51:07 +03:00
{
2016-03-12 03:41:27 +02:00
sbuffer < < color < < " " ;
2014-03-17 22:51:07 +03:00
{
boost : : unique_lock < boost : : recursive_mutex > lock ( gsm ) ;
2018-01-05 19:21:07 +02:00
connections [ color ] . insert ( cc ) ;
2014-03-17 22:51:07 +03:00
}
}
2016-08-30 00:11:54 +02:00
logGlobal - > info ( sbuffer . str ( ) ) ;
2014-03-17 22:51:07 +03:00
}
2022-09-21 18:31:14 +02:00
# if SCRIPTING_ENABLED
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
services ( ) - > scripts ( ) - > run ( serverScripts ) ;
2022-09-21 18:31:14 +02:00
# endif
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( resume )
events : : GameResumed : : defaultExecute ( serverEventBus . get ( ) ) ;
2014-03-17 22:51:07 +03:00
auto playerTurnOrder = generatePlayerTurnOrder ( ) ;
2018-01-05 19:21:07 +02:00
while ( lobby - > state = = EServerState : : GAMEPLAY )
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( ! resume )
{
newTurn ( ) ;
events : : TurnStarted : : defaultExecute ( serverEventBus . get ( ) ) ;
}
2014-03-17 22:51:07 +03:00
std : : list < PlayerColor > : : iterator it ;
2016-10-12 17:16:26 +02:00
if ( resume )
2014-03-17 22:51:07 +03:00
{
it = std : : find ( playerTurnOrder . begin ( ) , playerTurnOrder . end ( ) , gs - > currentPlayer ) ;
}
else
{
it = playerTurnOrder . begin ( ) ;
}
resume = false ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
for ( ; ( it ! = playerTurnOrder . end ( ) ) & & ( lobby - > state = = EServerState : : GAMEPLAY ) ; it + + )
2014-03-17 22:51:07 +03:00
{
auto playerColor = * it ;
2015-10-24 16:21:30 +02:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
auto onGetTurn = [ & ] ( events : : PlayerGotTurn & event )
2014-03-17 22:51:07 +03:00
{
2015-10-24 16:21:30 +02:00
//if player runs out of time, he shouldn't get the turn (especially AI)
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
//pre-trigger may change anything, should check before each player
//TODO: is it enough to check only one player?
2015-10-24 16:21:30 +02:00
checkVictoryLossConditionsForAll ( ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
auto player = event . getPlayer ( ) ;
const PlayerState * playerState = & gs - > players [ player ] ;
if ( playerState - > status ! = EPlayerStatus : : INGAME )
{
event . setPlayer ( PlayerColor : : CANNOT_DETERMINE ) ;
2015-10-24 16:21:30 +02:00
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
else
2015-10-24 17:02:00 +02:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
states . setFlag ( player , & PlayerStatus : : makingTurn , true ) ;
2014-03-17 22:51:07 +03:00
2015-10-24 17:02:00 +02:00
YourTurn yt ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
yt . player = player ;
2015-10-24 17:02:00 +02:00
//Change local daysWithoutCastle counter for local interface message //TODO: needed?
yt . daysWithoutCastle = playerState - > daysWithoutCastle ;
applyAndSend ( & yt ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
}
} ;
2014-03-17 22:51:07 +03:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
events : : PlayerGotTurn : : defaultExecute ( serverEventBus . get ( ) , onGetTurn , playerColor ) ;
if ( playerColor ! = PlayerColor : : CANNOT_DETERMINE )
{
//wait till turn is done
boost : : unique_lock < boost : : mutex > lock ( states . mx ) ;
while ( states . players . at ( playerColor ) . makingTurn & & lobby - > state = = EServerState : : GAMEPLAY )
{
static time_duration p = milliseconds ( 100 ) ;
states . cv . timed_wait ( lock , p ) ;
2014-03-17 22:51:07 +03:00
}
}
}
2015-08-22 15:22:10 +02:00
//additional check that game is not finished
bool activePlayer = false ;
2016-10-12 17:16:26 +02:00
for ( auto player : playerTurnOrder )
2015-08-22 15:22:10 +02:00
{
2016-10-12 17:16:26 +02:00
if ( gs - > players [ player ] . status = = EPlayerStatus : : INGAME )
2015-08-22 15:22:10 +02:00
activePlayer = true ;
}
2018-01-05 19:21:07 +02:00
if ( ! activePlayer )
lobby - > state = EServerState : : GAMEPLAY_ENDED ;
2014-03-17 22:51:07 +03:00
}
}
std : : list < PlayerColor > CGameHandler : : generatePlayerTurnOrder ( ) const
{
// Generate player turn order
std : : list < PlayerColor > playerTurnOrder ;
2016-10-12 17:16:26 +02:00
for ( const auto & player : gs - > players ) // add human players first
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( player . second . human )
2014-03-17 22:51:07 +03:00
playerTurnOrder . push_back ( player . first ) ;
}
2016-10-12 17:16:26 +02:00
for ( const auto & player : gs - > players ) // then add non-human players
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ! player . second . human )
2014-03-17 22:51:07 +03:00
playerTurnOrder . push_back ( player . first ) ;
}
2015-12-04 01:06:02 +02:00
return playerTurnOrder ;
2014-03-17 22:51:07 +03:00
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : setupBattle ( int3 tile , const CArmedInstance * armies [ 2 ] , const CGHeroInstance * heroes [ 2 ] , bool creatureBank , const CGTownInstance * town )
2014-03-17 22:51:07 +03:00
{
battleResult . set ( nullptr ) ;
2022-09-25 09:33:56 +02:00
const auto & t = * getTile ( tile ) ;
2023-01-01 17:10:47 +02:00
TerrainId terrain = t . terType - > getId ( ) ;
2016-10-12 17:16:26 +02:00
if ( gs - > map - > isCoastalTile ( tile ) ) //coastal tile is always ground
2022-12-20 18:35:40 +02:00
terrain = ETerrainId : : SAND ;
2016-08-29 03:46:01 +02:00
2022-06-22 10:41:02 +02:00
BattleField terType = gs - > battleGetBattlefieldType ( tile , getRandomGenerator ( ) ) ;
2016-08-29 03:46:01 +02:00
if ( heroes [ 0 ] & & heroes [ 0 ] - > boat & & heroes [ 1 ] & & heroes [ 1 ] - > boat )
2022-06-28 10:05:30 +02:00
terType = BattleField : : fromString ( " ship_to_ship " ) ;
2016-08-29 03:46:01 +02:00
2014-03-17 22:51:07 +03:00
//send info about battles
BattleStart bs ;
2016-08-29 03:46:01 +02:00
bs . info = BattleInfo : : setupBattle ( tile , terrain , terType , armies , heroes , creatureBank , town ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & bs ) ;
}
2016-02-09 09:59:33 +02:00
void CGameHandler : : checkBattleStateChanges ( )
2014-03-17 22:51:07 +03:00
{
2016-02-09 09:59:33 +02:00
//check if drawbridge state need to be changes
2016-10-12 17:16:26 +02:00
if ( battleGetSiegeLevel ( ) > 0 )
2016-02-13 16:40:31 +02:00
updateGateState ( ) ;
2016-02-09 09:59:33 +02:00
//check if battle ended
2016-10-12 17:16:26 +02:00
if ( auto result = battleIsFinished ( ) )
2014-03-17 22:51:07 +03:00
{
setBattleResult ( BattleResult : : NORMAL , * result ) ;
}
}
2016-03-13 12:24:17 +02:00
void CGameHandler : : giveSpells ( const CGTownInstance * t , const CGHeroInstance * h )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ! h - > hasSpellbook ( ) )
2014-03-17 22:51:07 +03:00
return ; //hero hasn't spellbook
ChangeSpells cs ;
cs . hid = h - > id ;
cs . learn = true ;
2016-10-12 17:16:26 +02:00
if ( t - > hasBuilt ( BuildingID : : GRAIL , ETownType : : CONFLUX ) & & t - > hasBuilt ( BuildingID : : MAGES_GUILD_1 ) )
2014-03-17 22:51:07 +03:00
{
2016-03-13 12:24:17 +02:00
// Aurora Borealis give spells of all levels even if only level 1 mages guild built
2017-08-26 10:49:29 +02:00
for ( int i = 0 ; i < h - > maxSpellLevel ( ) ; i + + )
2014-03-17 22:51:07 +03:00
{
std : : vector < SpellID > spells ;
2016-03-13 12:24:17 +02:00
getAllowedSpells ( spells , i + 1 ) ;
2016-10-12 17:16:26 +02:00
for ( auto & spell : spells )
2014-03-17 22:51:07 +03:00
cs . spells . insert ( spell ) ;
}
2016-03-13 12:24:17 +02:00
}
else
{
2017-08-26 10:49:29 +02:00
for ( int i = 0 ; i < std : : min ( t - > mageGuildLevel ( ) , h - > maxSpellLevel ( ) ) ; i + + )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
for ( int j = 0 ; j < t - > spellsAtLevel ( i + 1 , true ) & & j < t - > spells . at ( i ) . size ( ) ; j + + )
2014-03-17 22:51:07 +03:00
{
2018-12-20 23:42:31 +02:00
if ( ! h - > spellbookContainsSpell ( t - > spells . at ( i ) . at ( j ) ) )
2014-03-17 22:51:07 +03:00
cs . spells . insert ( t - > spells . at ( i ) . at ( j ) ) ;
}
}
}
2016-10-12 17:16:26 +02:00
if ( ! cs . spells . empty ( ) )
2014-03-17 22:51:07 +03:00
sendAndApply ( & cs ) ;
}
2016-10-12 17:16:26 +02:00
bool CGameHandler : : removeObject ( const CGObjectInstance * obj )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ! obj | | ! getObj ( obj - > id ) )
2014-03-17 22:51:07 +03:00
{
2016-08-30 00:11:54 +02:00
logGlobal - > error ( " Something wrong, that object already has been removed or hasn't existed! " ) ;
2014-03-17 22:51:07 +03:00
return false ;
}
RemoveObject ro ;
ro . id = obj - > id ;
sendAndApply ( & ro ) ;
checkVictoryLossConditionsForAll ( ) ; //eg if monster escaped (removing objs after battle is done dircetly by endBattle, not this function)
return true ;
}
2017-07-15 13:08:20 +02:00
bool CGameHandler : : moveHero ( ObjectInstanceID hid , int3 dst , ui8 teleporting , bool transit , PlayerColor asker )
2014-03-17 22:51:07 +03:00
{
const CGHeroInstance * h = getHero ( hid ) ;
2016-10-12 17:16:26 +02:00
// not turn of that hero or player can't simply teleport hero (at least not with this function)
if ( ! h | | ( asker ! = PlayerColor : : NEUTRAL & & ( teleporting | | h - > getOwner ( ) ! = gs - > currentPlayer ) ) )
2014-03-17 22:51:07 +03:00
{
2016-08-30 00:11:54 +02:00
logGlobal - > error ( " Illegal call to move hero! " ) ;
2014-03-17 22:51:07 +03:00
return false ;
}
2017-08-11 19:03:05 +02:00
logGlobal - > trace ( " Player %d (%s) wants to move hero %d from %s to %s " , asker , asker . getStr ( ) , hid . getNum ( ) , h - > pos . toString ( ) , dst . toString ( ) ) ;
2022-12-09 14:42:47 +02:00
const int3 hmpos = h - > convertToVisitablePos ( dst ) ;
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( ! gs - > map - > isInTheMap ( hmpos ) )
2014-03-17 22:51:07 +03:00
{
2016-08-30 00:11:54 +02:00
logGlobal - > error ( " Destination tile is outside the map! " ) ;
2014-03-17 22:51:07 +03:00
return false ;
}
2016-09-18 10:53:51 +02:00
const TerrainTile t = * getTile ( hmpos ) ;
2014-03-17 22:51:07 +03:00
const int3 guardPos = gs - > guardingCreaturePosition ( hmpos ) ;
const bool embarking = ! h - > boat & & ! t . visitableObjects . empty ( ) & & t . visitableObjects . back ( ) - > ID = = Obj : : BOAT ;
2023-04-18 17:18:44 +02:00
const bool disembarking = h - > boat
& & t . terType - > isLand ( )
2023-04-18 23:11:51 +02:00
& & ( dst = = h - > pos
| | ( h - > boat - > layer = = EPathfindingLayer : : SAIL & & ! t . blocked ) ) ;
2014-03-17 22:51:07 +03:00
//result structure for start - movement failed, no move points used
TryMoveHero tmh ;
tmh . id = hid ;
tmh . start = h - > pos ;
tmh . end = dst ;
tmh . result = TryMoveHero : : FAILED ;
tmh . movePoints = h - > movement ;
//check if destination tile is available
2022-12-07 23:36:20 +02:00
auto pathfinderHelper = std : : make_unique < CPathfinderHelper > ( gs , h , PathfinderOptions ( ) ) ;
2018-08-01 20:46:06 +02:00
auto ti = pathfinderHelper - > getTurnInfo ( ) ;
2023-05-01 00:20:01 +02:00
const bool canFly = pathfinderHelper - > hasBonusOfType ( BonusType : : FLYING_MOVEMENT ) | | ( h - > boat & & h - > boat - > layer = = EPathfindingLayer : : AIR ) ;
const bool canWalkOnSea = pathfinderHelper - > hasBonusOfType ( BonusType : : WATER_WALKING ) | | ( h - > boat & & h - > boat - > layer = = EPathfindingLayer : : WATER ) ;
2022-12-07 21:50:45 +02:00
const int cost = pathfinderHelper - > getMovementCost ( h - > visitablePos ( ) , hmpos , nullptr , nullptr , h - > movement ) ;
2014-03-17 22:51:07 +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)
2022-09-21 11:34:23 +02:00
if ( ( ( ! t . terType - > isPassable ( ) | | ( t . blocked & & ! t . visitable & & ! canFly ) )
2014-03-17 22:51:07 +03:00
& & complain ( " Cannot move hero, destination tile is blocked! " ) )
2022-09-21 11:34:23 +02:00
| | ( ( ! h - > boat & & ! canWalkOnSea & & ! canFly & & t . terType - > isWater ( ) & & ( t . visitableObjects . size ( ) < 1 | | ( t . visitableObjects . back ( ) - > ID ! = Obj : : BOAT & & t . visitableObjects . back ( ) - > ID ! = Obj : : HERO ) ) ) //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)
2014-03-17 22:51:07 +03:00
& & complain ( " Cannot move hero, destination tile is on water! " ) )
2023-04-18 17:18:44 +02:00
| | ( ( h - > boat & & h - > boat - > layer = = EPathfindingLayer : : SAIL & & t . terType - > isLand ( ) & & t . blocked )
2014-03-17 22:51:07 +03:00
& & complain ( " Cannot disembark hero, tile is blocked! " ) )
2016-10-12 17:16:26 +02:00
| | ( ( distance ( h - > pos , dst ) > = 1.5 & & ! teleporting )
2014-03-17 22:51:07 +03:00
& & complain ( " Tiles are not neighboring! " ) )
2016-10-12 17:16:26 +02:00
| | ( ( h - > inTownGarrison )
2014-03-17 22:51:07 +03:00
& & complain ( " Can not move garrisoned hero! " ) )
2020-10-01 10:38:06 +02:00
| | ( ( ( int ) h - > movement < cost & & dst ! = h - > pos & & ! teleporting )
2014-03-17 22:51:07 +03:00
& & complain ( " Hero doesn't have any movement points left! " ) )
2015-11-09 18:57:26 +02:00
| | ( ( transit & & ! canFly & & ! CGTeleport : : isTeleport ( t . topVisitableObj ( ) ) )
2015-11-05 09:50:47 +02:00
& & complain ( " Hero cannot transit over this tile! " ) )
2014-03-17 22:51:07 +03:00
/*|| (states.checkFlag(h->tempOwner, &PlayerStatus::engagedIntoBattle)
& & complain ( " Cannot move hero during the battle " ) ) */ )
{
//send info about movement failure
sendAndApply ( & tmh ) ;
return false ;
}
//several generic blocks of code
// should be called if hero changes tile but before applying TryMoveHero package
auto leaveTile = [ & ] ( )
{
2016-10-12 17:16:26 +02:00
for ( CGObjectInstance * obj : gs - > map - > getTile ( int3 ( h - > pos . x - 1 , h - > pos . y , h - > pos . z ) ) . visitableObjects )
2014-03-17 22:51:07 +03:00
{
obj - > onHeroLeave ( h ) ;
}
2016-01-31 17:01:58 +02:00
this - > getTilesInRange ( tmh . fowRevealed , h - > getSightCenter ( ) + ( tmh . end - tmh . start ) , h - > getSightRadius ( ) , h - > tempOwner , 1 ) ;
2014-03-17 22:51:07 +03:00
} ;
auto doMove = [ & ] ( TryMoveHero : : EResult result , EGuardLook lookForGuards ,
EVisitDest visitDest , ELEaveTile leavingTile ) - > bool
{
2023-01-02 13:27:03 +02:00
LOG_TRACE_PARAMS ( logGlobal , " Hero %s starts movement from %s to %s " , h - > getNameTranslated ( ) % tmh . start . toString ( ) % tmh . end . toString ( ) ) ;
2014-03-17 22:51:07 +03:00
2017-06-06 06:53:51 +02:00
auto moveQuery = std : : make_shared < CHeroMovementQuery > ( this , tmh , h ) ;
2014-03-17 22:51:07 +03:00
queries . addQuery ( moveQuery ) ;
2016-10-12 17:16:26 +02:00
if ( leavingTile = = LEAVING_TILE )
2014-03-17 22:51:07 +03:00
leaveTile ( ) ;
2023-03-31 21:42:32 +02:00
if ( isInTheMap ( guardPos ) )
2023-04-16 19:42:56 +02:00
tmh . attackedFrom = std : : make_optional ( guardPos ) ;
2023-03-31 21:42:32 +02:00
2014-03-17 22:51:07 +03:00
tmh . result = result ;
sendAndApply ( & tmh ) ;
2015-02-22 19:12:49 +02:00
if ( visitDest = = VISIT_DEST & & t . topVisitableObj ( ) & & t . topVisitableObj ( ) - > id = = h - > id )
{ // Hero should be always able to visit any object he staying on even if there guards around
visitObjectOnTile ( t , h ) ;
}
2023-03-31 21:42:32 +02:00
else if ( lookForGuards = = CHECK_FOR_GUARDS & & isInTheMap ( guardPos ) )
2014-03-17 22:51:07 +03:00
{
const TerrainTile & guardTile = * gs - > getTile ( guardPos ) ;
objectVisited ( guardTile . visitableObjects . back ( ) , h ) ;
moveQuery - > visitDestAfterVictory = visitDest = = VISIT_DEST ;
}
2016-10-12 17:16:26 +02:00
else if ( visitDest = = VISIT_DEST )
2014-03-17 22:51:07 +03:00
{
2015-11-05 09:02:13 +02:00
visitObjectOnTile ( t , h ) ;
2014-03-17 22:51:07 +03:00
}
queries . popIfTop ( moveQuery ) ;
2023-01-02 13:27:03 +02:00
logGlobal - > trace ( " Hero %s ends movement " , h - > getNameTranslated ( ) ) ;
2014-03-17 22:51:07 +03:00
return result ! = TryMoveHero : : FAILED ;
} ;
//interaction with blocking object (like resources)
auto blockingVisit = [ & ] ( ) - > bool
{
2016-10-12 17:16:26 +02:00
for ( CGObjectInstance * obj : t . visitableObjects )
2014-03-17 22:51:07 +03:00
{
2023-04-19 00:11:24 +02:00
if ( h - > boat & & ! obj - > blockVisit & & ! h - > boat - > onboardVisitAllowed )
return doMove ( TryMoveHero : : SUCCESS , this - > IGNORE_GUARDS , DONT_VISIT_DEST , REMAINING_ON_TILE ) ;
if ( obj ! = h & & obj - > blockVisit & & ! obj - > passableFor ( h - > tempOwner ) )
2014-03-17 22:51:07 +03:00
{
2023-04-19 00:11:24 +02:00
EVisitDest visitDest = VISIT_DEST ;
if ( h - > boat & & ! h - > boat - > onboardVisitAllowed )
visitDest = DONT_VISIT_DEST ;
return doMove ( TryMoveHero : : BLOCKING_VISIT , this - > IGNORE_GUARDS , visitDest , REMAINING_ON_TILE ) ;
2014-03-17 22:51:07 +03:00
}
}
return false ;
} ;
2016-10-12 17:16:26 +02:00
if ( ! transit & & embarking )
2014-03-17 22:51:07 +03:00
{
2018-08-01 20:46:06 +02:00
tmh . movePoints = h - > movementPointsAfterEmbark ( h - > movement , cost , false , ti ) ;
2014-03-17 22:51:07 +03:00
return doMove ( TryMoveHero : : EMBARK , IGNORE_GUARDS , DONT_VISIT_DEST , LEAVING_TILE ) ;
2015-12-15 18:13:55 +02:00
// In H3 embark ignore guards
2014-03-17 22:51:07 +03:00
}
2016-10-12 17:16:26 +02:00
if ( disembarking )
2014-03-17 22:51:07 +03:00
{
2018-08-01 20:46:06 +02:00
tmh . movePoints = h - > movementPointsAfterEmbark ( h - > movement , cost , true , ti ) ;
2014-03-17 22:51:07 +03:00
return doMove ( TryMoveHero : : DISEMBARK , CHECK_FOR_GUARDS , VISIT_DEST , LEAVING_TILE ) ;
}
2016-10-12 17:16:26 +02:00
if ( teleporting )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( blockingVisit ( ) ) // e.g. hero on the other side of teleporter
2014-03-17 22:51:07 +03:00
return true ;
doMove ( TryMoveHero : : TELEPORTATION , IGNORE_GUARDS , DONT_VISIT_DEST , LEAVING_TILE ) ;
// visit town for town portal \ castle gates
// do not use generic visitObjectOnTile to avoid double-teleporting
// if this moveHero call was triggered by teleporter
if ( ! t . visitableObjects . empty ( ) )
{
if ( CGTownInstance * town = dynamic_cast < CGTownInstance * > ( t . visitableObjects . back ( ) ) )
town - > onHeroVisit ( h ) ;
}
return true ;
}
2023-04-19 00:11:24 +02:00
2014-03-17 22:51:07 +03:00
//still here? it is standard movement!
{
2020-10-01 10:38:06 +02:00
tmh . movePoints = ( int ) h - > movement > = cost
2014-03-17 22:51:07 +03:00
? h - > movement - cost
: 0 ;
2015-11-05 09:02:13 +02:00
EGuardLook lookForGuards = CHECK_FOR_GUARDS ;
EVisitDest visitDest = VISIT_DEST ;
2016-10-12 17:16:26 +02:00
if ( transit )
2015-11-05 09:02:13 +02:00
{
2016-10-12 17:16:26 +02:00
if ( CGTeleport : : isTeleport ( t . topVisitableObj ( ) ) )
2015-11-05 09:02:13 +02:00
visitDest = DONT_VISIT_DEST ;
2016-10-12 17:16:26 +02:00
if ( canFly )
2015-11-05 09:02:13 +02:00
{
lookForGuards = IGNORE_GUARDS ;
visitDest = DONT_VISIT_DEST ;
}
}
2016-10-12 17:16:26 +02:00
else if ( blockingVisit ( ) )
2014-03-17 22:51:07 +03:00
return true ;
2023-04-19 00:11:24 +02:00
if ( h - > boat & & ! h - > boat - > onboardAssaultAllowed )
lookForGuards = IGNORE_GUARDS ;
2014-03-17 22:51:07 +03:00
2015-11-05 09:02:13 +02:00
doMove ( TryMoveHero : : SUCCESS , lookForGuards , visitDest , LEAVING_TILE ) ;
2014-03-17 22:51:07 +03:00
return true ;
}
}
2017-07-15 13:08:20 +02:00
bool CGameHandler : : teleportHero ( ObjectInstanceID hid , ObjectInstanceID dstid , ui8 source , PlayerColor asker )
2014-03-17 22:51:07 +03:00
{
const CGHeroInstance * h = getHero ( hid ) ;
const CGTownInstance * t = getTown ( dstid ) ;
2016-10-12 17:16:26 +02:00
if ( ! h | | ! t | | h - > getOwner ( ) ! = gs - > currentPlayer )
2016-11-27 21:50:37 +02:00
COMPLAIN_RET ( " Invalid call to teleportHero! " ) ;
2014-03-17 22:51:07 +03:00
const CGTownInstance * from = h - > visitedTown ;
2016-10-12 17:16:26 +02:00
if ( ( ( h - > getOwner ( ) ! = t - > getOwner ( ) )
2014-03-17 22:51:07 +03:00
& & complain ( " Cannot teleport hero to another player " ) )
2020-10-02 23:55:46 +02:00
2023-01-04 15:17:50 +02:00
| | ( from - > town - > faction - > getId ( ) ! = t - > town - > faction - > getId ( )
2020-10-02 23:55:46 +02:00
& & complain ( " Source town and destination town should belong to the same faction " ) )
| | ( ( ! from | | ! from - > hasBuilt ( BuildingSubID : : CASTLE_GATE ) )
2014-03-17 22:51:07 +03:00
& & complain ( " Hero must be in town with Castle gate for teleporting " ) )
2020-10-02 23:55:46 +02:00
| | ( ! t - > hasBuilt ( BuildingSubID : : CASTLE_GATE )
2014-03-17 22:51:07 +03:00
& & complain ( " Cannot teleport hero to town without Castle gate in it " ) ) )
return false ;
2022-12-09 14:42:47 +02:00
int3 pos = h - > convertFromVisitablePos ( t - > visitablePos ( ) ) ;
2014-03-17 22:51:07 +03:00
moveHero ( hid , pos , 1 ) ;
return true ;
}
2021-03-23 16:47:07 +02:00
void CGameHandler : : setOwner ( const CGObjectInstance * obj , const PlayerColor owner )
2014-03-17 22:51:07 +03:00
{
PlayerColor oldOwner = getOwner ( obj - > id ) ;
2016-09-09 16:42:16 +02:00
SetObjectProperty sop ( obj - > id , ObjProperty : : OWNER , owner . getNum ( ) ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & sop ) ;
2014-10-02 18:43:46 +03:00
std : : set < PlayerColor > playerColors = { owner , oldOwner } ;
2014-03-17 22:51:07 +03:00
checkVictoryLossConditions ( playerColors ) ;
2016-11-27 21:50:37 +02:00
const CGTownInstance * town = dynamic_cast < const CGTownInstance * > ( obj ) ;
if ( town ) //town captured
2014-03-17 22:51:07 +03:00
{
2015-10-24 18:03:00 +02:00
if ( owner < PlayerColor : : PLAYER_LIMIT ) //new owner is real player
{
2020-10-15 14:03:01 +02:00
if ( town - > hasBuilt ( BuildingSubID : : PORTAL_OF_SUMMONING ) )
2015-10-24 18:03:00 +02:00
setPortalDwelling ( town , true , false ) ;
}
2014-03-17 22:51:07 +03:00
2015-10-24 18:03:00 +02:00
if ( oldOwner < PlayerColor : : PLAYER_LIMIT ) //old owner is real player
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( getPlayerState ( oldOwner ) - > towns . empty ( ) & & getPlayerState ( oldOwner ) - > status ! = EPlayerStatus : : LOSER ) //previous player lost last last town
2015-10-24 18:03:00 +02:00
{
InfoWindow iw ;
iw . player = oldOwner ;
2015-11-08 23:54:59 +02:00
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 , oldOwner . getNum ( ) ) ;
2015-10-24 18:03:00 +02:00
sendAndApply ( & iw ) ;
}
2014-03-17 22:51:07 +03:00
}
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const PlayerState * p = getPlayerState ( owner ) ;
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( ( obj - > ID = = Obj : : CREATURE_GENERATOR1 | | obj - > ID = = Obj : : CREATURE_GENERATOR4 ) & & p & & p - > dwellings . size ( ) = = 1 ) //first dwelling captured
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
for ( const CGTownInstance * t : getPlayerState ( owner ) - > towns )
2014-03-17 22:51:07 +03:00
{
2020-10-15 14:03:01 +02:00
if ( t - > hasBuilt ( BuildingSubID : : PORTAL_OF_SUMMONING ) )
2014-03-17 22:51:07 +03:00
setPortalDwelling ( t ) ; //set initial creatures for all portals of summoning
}
}
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : showBlockingDialog ( BlockingDialog * iw )
2014-03-17 22:51:07 +03:00
{
2017-06-06 06:53:51 +02:00
auto dialogQuery = std : : make_shared < CBlockingDialogQuery > ( this , * iw ) ;
2014-03-17 22:51:07 +03:00
queries . addQuery ( dialogQuery ) ;
iw - > queryID = dialogQuery - > queryID ;
sendToAllClients ( iw ) ;
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : showTeleportDialog ( TeleportDialog * iw )
2015-03-08 15:52:50 +02:00
{
2017-06-06 06:53:51 +02:00
auto dialogQuery = std : : make_shared < CTeleportDialogQuery > ( this , * iw ) ;
2015-03-08 15:52:50 +02:00
queries . addQuery ( dialogQuery ) ;
iw - > queryID = dialogQuery - > queryID ;
2014-03-17 22:51:07 +03:00
sendToAllClients ( iw ) ;
}
2023-04-05 02:26:29 +02:00
void CGameHandler : : giveResource ( PlayerColor player , GameResID which , int val ) //TODO: cap according to Bersy's suggestion
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ! val ) return ; //don't waste time on empty call
2016-11-26 14:14:43 +02:00
TResources resources ;
2023-03-31 13:12:38 +02:00
resources [ which ] = val ;
2016-11-26 14:14:43 +02:00
giveResources ( player , resources ) ;
2014-03-17 22:51:07 +03:00
}
void CGameHandler : : giveResources ( PlayerColor player , TResources resources )
{
2016-11-26 14:14:43 +02:00
SetResources sr ;
sr . abs = false ;
sr . player = player ;
sr . res = resources ;
2016-11-28 04:19:44 +02:00
sendAndApply ( & sr ) ;
2014-03-17 22:51:07 +03:00
}
void CGameHandler : : giveCreatures ( const CArmedInstance * obj , const CGHeroInstance * h , const CCreatureSet & creatures , bool remove )
{
COMPLAIN_RET_IF ( ! creatures . stacksCount ( ) , " Strange, giveCreatures called without args! " ) ;
COMPLAIN_RET_IF ( obj - > stacksCount ( ) , " Cannot give creatures from not-cleared object! " ) ;
COMPLAIN_RET_IF ( creatures . stacksCount ( ) > GameConstants : : ARMY_SIZE , " Too many stacks to give! " ) ;
//first we move creatures to give to make them army of object-source
for ( auto & elem : creatures . Slots ( ) )
{
addToSlot ( StackLocation ( obj , obj - > getSlotFor ( elem . second - > type ) ) , elem . second - > type , elem . second - > count ) ;
}
tryJoiningArmy ( obj , h , remove , true ) ;
}
void CGameHandler : : takeCreatures ( ObjectInstanceID objid , const std : : vector < CStackBasicDescriptor > & creatures )
{
std : : vector < CStackBasicDescriptor > cres = creatures ;
if ( cres . size ( ) < = 0 )
return ;
const CArmedInstance * obj = static_cast < const CArmedInstance * > ( getObj ( objid ) ) ;
2016-10-12 17:16:26 +02:00
for ( CStackBasicDescriptor & sbd : cres )
2014-03-17 22:51:07 +03:00
{
TQuantity collected = 0 ;
while ( collected < sbd . count )
{
bool foundSth = false ;
2016-10-12 17:16:26 +02:00
for ( auto i = obj - > Slots ( ) . begin ( ) ; i ! = obj - > Slots ( ) . end ( ) ; i + + )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( i - > second - > type = = sbd . type )
2014-03-17 22:51:07 +03:00
{
TQuantity take = std : : min ( sbd . count - collected , i - > second - > count ) ; //collect as much cres as we can
changeStackCount ( StackLocation ( obj , i - > first ) , - take , false ) ;
collected + = take ;
foundSth = true ;
break ;
}
}
2016-10-12 17:16:26 +02:00
if ( ! foundSth ) //we went through the whole loop and haven't found appropriate cres
2014-03-17 22:51:07 +03:00
{
complain ( " Unexpected failure during taking creatures! " ) ;
return ;
}
}
}
}
void CGameHandler : : heroVisitCastle ( const CGTownInstance * obj , const CGHeroInstance * hero )
{
HeroVisitCastle vc ;
vc . hid = hero - > id ;
vc . tid = obj - > id ;
vc . flags | = 1 ;
sendAndApply ( & vc ) ;
2020-09-28 00:32:33 +02:00
visitCastleObjects ( obj , hero ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
giveSpells ( obj , hero ) ;
2014-03-17 22:51:07 +03:00
checkVictoryLossConditionsForPlayer ( hero - > tempOwner ) ; //transported artifact?
}
2020-09-28 00:32:33 +02:00
void CGameHandler : : visitCastleObjects ( const CGTownInstance * t , const CGHeroInstance * h )
2014-03-17 22:51:07 +03:00
{
2020-10-07 14:12:32 +02:00
for ( auto building : t - > bonusingBuildings )
building - > onHeroVisit ( h ) ;
2014-03-17 22:51:07 +03:00
}
void CGameHandler : : stopHeroVisitCastle ( const CGTownInstance * obj , const CGHeroInstance * hero )
{
HeroVisitCastle vc ;
vc . hid = hero - > id ;
vc . tid = obj - > id ;
sendAndApply ( & vc ) ;
}
void CGameHandler : : removeArtifact ( const ArtifactLocation & al )
{
EraseArtifact ea ;
ea . al = al ;
sendAndApply ( & ea ) ;
}
void CGameHandler : : startBattlePrimary ( const CArmedInstance * army1 , const CArmedInstance * army2 , int3 tile ,
const CGHeroInstance * hero1 , const CGHeroInstance * hero2 , bool creatureBank ,
const CGTownInstance * town ) //use hero=nullptr for no hero
{
2023-04-06 17:34:07 +02:00
if ( gs - > curB )
gs - > curB . dellNull ( ) ;
2014-03-17 22:51:07 +03:00
engageIntoBattle ( army1 - > tempOwner ) ;
engageIntoBattle ( army2 - > tempOwner ) ;
static const CArmedInstance * armies [ 2 ] ;
armies [ 0 ] = army1 ;
armies [ 1 ] = army2 ;
static const CGHeroInstance * heroes [ 2 ] ;
heroes [ 0 ] = hero1 ;
heroes [ 1 ] = hero2 ;
setupBattle ( tile , armies , heroes , creatureBank , town ) ; //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
2023-04-06 17:34:07 +02:00
//existing battle query for retying auto-combat
auto battleQuery = std : : dynamic_pointer_cast < CBattleQuery > ( queries . topQuery ( gs - > curB - > sides [ 0 ] . color ) ) ;
if ( battleQuery )
{
2023-04-06 19:19:46 +02:00
for ( int i : { 0 , 1 } )
{
if ( heroes [ i ] )
{
SetMana restoreInitialMana ;
restoreInitialMana . val = battleQuery - > initialHeroMana [ i ] ;
restoreInitialMana . hid = heroes [ i ] - > id ;
sendAndApply ( & restoreInitialMana ) ;
}
}
2023-04-06 17:34:07 +02:00
battleQuery - > bi = gs - > curB ;
2023-04-16 19:42:56 +02:00
battleQuery - > result = std : : nullopt ;
2023-04-06 17:34:07 +02:00
battleQuery - > belligerents [ 0 ] = gs - > curB - > sides [ 0 ] . armyObject ;
battleQuery - > belligerents [ 1 ] = gs - > curB - > sides [ 1 ] . armyObject ;
}
2023-04-06 19:19:46 +02:00
battleQuery = std : : make_shared < CBattleQuery > ( this , gs - > curB ) ;
for ( int i : { 0 , 1 } )
{
if ( heroes [ i ] )
{
battleQuery - > initialHeroMana [ i ] = heroes [ i ] - > mana ;
}
}
2014-03-17 22:51:07 +03:00
queries . addQuery ( battleQuery ) ;
2023-03-25 21:29:33 +02:00
this - > battleThread = std : : make_unique < boost : : thread > ( boost : : thread ( & CGameHandler : : runBattle , this ) ) ;
2014-03-17 22:51:07 +03:00
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : startBattleI ( const CArmedInstance * army1 , const CArmedInstance * army2 , int3 tile , bool creatureBank )
2014-03-17 22:51:07 +03:00
{
startBattlePrimary ( army1 , army2 , tile ,
army1 - > ID = = Obj : : HERO ? static_cast < const CGHeroInstance * > ( army1 ) : nullptr ,
army2 - > ID = = Obj : : HERO ? static_cast < const CGHeroInstance * > ( army2 ) : nullptr ,
creatureBank ) ;
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : startBattleI ( const CArmedInstance * army1 , const CArmedInstance * army2 , bool creatureBank )
2014-03-17 22:51:07 +03:00
{
startBattleI ( army1 , army2 , army2 - > visitablePos ( ) , creatureBank ) ;
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : changeSpells ( const CGHeroInstance * hero , bool give , const std : : set < SpellID > & spells )
2014-03-17 22:51:07 +03:00
{
ChangeSpells cs ;
cs . hid = hero - > id ;
cs . spells = spells ;
cs . learn = give ;
sendAndApply ( & cs ) ;
}
2018-01-05 19:21:07 +02:00
void CGameHandler : : sendMessageTo ( std : : shared_ptr < CConnection > c , const std : : string & message )
2014-03-17 22:51:07 +03:00
{
SystemMessage sm ;
sm . text = message ;
2018-01-05 19:21:07 +02:00
boost : : unique_lock < boost : : mutex > lock ( * c - > mutexWrite ) ;
* ( c . get ( ) ) < < & sm ;
2014-03-17 22:51:07 +03:00
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : giveHeroBonus ( GiveBonus * bonus )
2014-03-17 22:51:07 +03:00
{
sendAndApply ( bonus ) ;
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : setMovePoints ( SetMovePoints * smp )
2014-03-17 22:51:07 +03:00
{
sendAndApply ( smp ) ;
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : setManaPoints ( ObjectInstanceID hid , int val )
2014-03-17 22:51:07 +03:00
{
SetMana sm ;
sm . hid = hid ;
sm . val = val ;
2014-11-26 12:30:55 +02:00
sm . absolute = true ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & sm ) ;
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : giveHero ( ObjectInstanceID id , PlayerColor player )
2014-03-17 22:51:07 +03:00
{
GiveHero gh ;
gh . id = id ;
gh . player = player ;
sendAndApply ( & gh ) ;
}
2023-02-16 21:35:15 +02:00
void CGameHandler : : changeObjPos ( ObjectInstanceID objid , int3 newPos )
2014-03-17 22:51:07 +03:00
{
ChangeObjPos cop ;
cop . objid = objid ;
cop . nPos = newPos ;
sendAndApply ( & cop ) ;
}
void CGameHandler : : useScholarSkill ( ObjectInstanceID fromHero , ObjectInstanceID toHero )
{
const CGHeroInstance * h1 = getHero ( fromHero ) ;
const CGHeroInstance * h2 = getHero ( toHero ) ;
2023-05-01 00:20:01 +02:00
int h1_scholarSpellLevel = h1 - > valOfBonuses ( BonusType : : LEARN_MEETING_SPELL_LIMIT , - 1 ) ;
int h2_scholarSpellLevel = h2 - > valOfBonuses ( BonusType : : LEARN_MEETING_SPELL_LIMIT , - 1 ) ;
2014-03-17 22:51:07 +03:00
2020-04-04 11:17:49 +02:00
if ( h1_scholarSpellLevel < h2_scholarSpellLevel )
2014-03-17 22:51:07 +03:00
{
std : : swap ( h1 , h2 ) ; //1st hero need to have higher scholar level for correct message
std : : swap ( fromHero , toHero ) ;
}
2020-04-04 11:17:49 +02:00
int ScholarSpellLevel = std : : max ( h1_scholarSpellLevel , h2_scholarSpellLevel ) ; //heroes can trade up to this level
if ( ! ScholarSpellLevel | | ! h1 - > hasSpellbook ( ) | | ! h2 - > hasSpellbook ( ) )
2014-03-17 22:51:07 +03:00
return ; //no scholar skill or no spellbook
2020-04-04 11:17:49 +02:00
int h1Lvl = std : : min ( ScholarSpellLevel , h1 - > maxSpellLevel ( ) ) ,
h2Lvl = std : : min ( ScholarSpellLevel , h2 - > maxSpellLevel ( ) ) ; //heroes can receive this levels
2014-03-17 22:51:07 +03:00
ChangeSpells cs1 ;
cs1 . learn = true ;
cs1 . hid = toHero ; //giving spells to first hero
2018-12-20 23:42:31 +02:00
for ( auto it : h1 - > getSpellsInSpellbook ( ) )
if ( h2Lvl > = it . toSpell ( ) - > level & & ! h2 - > spellbookContainsSpell ( it ) ) //hero can learn it and don't have it yet
2014-03-17 22:51:07 +03:00
cs1 . spells . insert ( it ) ; //spell to learn
ChangeSpells cs2 ;
cs2 . learn = true ;
cs2 . hid = fromHero ;
2018-12-20 23:42:31 +02:00
for ( auto it : h2 - > getSpellsInSpellbook ( ) )
if ( h1Lvl > = it . toSpell ( ) - > level & & ! h1 - > spellbookContainsSpell ( it ) )
2014-03-17 22:51:07 +03:00
cs2 . spells . insert ( it ) ;
if ( ! cs1 . spells . empty ( ) | | ! cs2 . spells . empty ( ) ) //create a message
{
2020-04-04 11:17:49 +02:00
int ScholarSkillLevel = std : : max ( h1 - > getSecSkillLevel ( SecondarySkill : : SCHOLAR ) ,
h2 - > getSecSkillLevel ( SecondarySkill : : SCHOLAR ) ) ;
2014-03-17 22:51:07 +03:00
InfoWindow iw ;
iw . player = h1 - > tempOwner ;
2023-03-10 14:54:12 +02:00
iw . components . emplace_back ( Component : : EComponentType : : SEC_SKILL , 18 , ScholarSkillLevel , 0 ) ;
2014-03-17 22:51:07 +03:00
iw . text . addTxt ( MetaString : : GENERAL_TXT , 139 ) ; //"%s, who has studied magic extensively,
2023-02-24 13:40:06 +02:00
iw . text . addReplacement ( h1 - > getNameTranslated ( ) ) ;
2014-03-17 22:51:07 +03:00
if ( ! cs2 . spells . empty ( ) ) //if found new spell - apply
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 140 ) ; //learns
2020-10-01 10:38:06 +02:00
int size = static_cast < int > ( cs2 . spells . size ( ) ) ;
2016-10-12 17:16:26 +02:00
for ( auto it : cs2 . spells )
2014-03-17 22:51:07 +03:00
{
2023-03-10 14:54:12 +02:00
iw . components . emplace_back ( Component : : EComponentType : : SPELL , it , 1 , 0 ) ;
2014-03-17 22:51:07 +03:00
iw . text . addTxt ( MetaString : : SPELL_NAME , it . toEnum ( ) ) ;
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
2023-02-24 13:40:06 +02:00
iw . text . addReplacement ( h2 - > getNameTranslated ( ) ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & cs2 ) ;
}
2016-10-12 17:16:26 +02:00
if ( ! cs1 . spells . empty ( ) & & ! cs2 . spells . empty ( ) )
2014-03-17 22:51:07 +03:00
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 141 ) ; //and
}
if ( ! cs1 . spells . empty ( ) )
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 147 ) ; //teaches
2020-10-01 10:38:06 +02:00
int size = static_cast < int > ( cs1 . spells . size ( ) ) ;
2016-10-12 17:16:26 +02:00
for ( auto it : cs1 . spells )
2014-03-17 22:51:07 +03:00
{
2023-03-10 14:54:12 +02:00
iw . components . emplace_back ( Component : : EComponentType : : SPELL , it , 1 , 0 ) ;
2014-03-17 22:51:07 +03:00
iw . text . addTxt ( MetaString : : SPELL_NAME , it . toEnum ( ) ) ;
switch ( size - - )
{
case 2 : iw . text . addTxt ( MetaString : : GENERAL_TXT , 141 ) ;
case 1 : break ;
default : iw . text < < " , " ;
2023-02-24 13:40:06 +02:00
}
}
2014-03-17 22:51:07 +03:00
iw . text . addTxt ( MetaString : : GENERAL_TXT , 148 ) ; //from %s
2023-02-24 13:40:06 +02:00
iw . text . addReplacement ( h2 - > getNameTranslated ( ) ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & cs1 ) ;
}
sendAndApply ( & iw ) ;
}
}
void CGameHandler : : heroExchange ( ObjectInstanceID hero1 , ObjectInstanceID hero2 )
{
auto h1 = getHero ( hero1 ) , h2 = getHero ( hero2 ) ;
2016-10-12 17:16:26 +02:00
if ( getPlayerRelations ( h1 - > getOwner ( ) , h2 - > getOwner ( ) ) )
2014-03-17 22:51:07 +03:00
{
2017-06-06 06:53:51 +02:00
auto exchange = std : : make_shared < CGarrisonDialogQuery > ( this , h1 , h2 ) ;
2014-03-17 22:51:07 +03:00
ExchangeDialog hex ;
hex . queryID = exchange - > queryID ;
2018-03-10 21:19:55 +02:00
hex . player = h1 - > getOwner ( ) ;
hex . hero1 = hero1 ;
hex . hero2 = hero2 ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & hex ) ;
2018-03-10 21:19:55 +02:00
2014-03-17 22:51:07 +03:00
useScholarSkill ( hero1 , hero2 ) ;
queries . addQuery ( exchange ) ;
}
}
2018-02-25 18:16:15 +02:00
void CGameHandler : : sendToAllClients ( CPackForClient * pack )
2014-03-17 22:51:07 +03:00
{
2018-02-25 18:16:15 +02:00
logNetwork - > trace ( " \t Sending to all clients: %s " , typeid ( * pack ) . name ( ) ) ;
2018-01-05 19:21:07 +02:00
for ( auto c : lobby - > connections )
2014-03-17 22:51:07 +03:00
{
2018-01-05 19:21:07 +02:00
if ( ! c - > isOpen ( ) )
2016-10-30 01:43:06 +02:00
continue ;
2018-02-25 18:16:15 +02:00
c - > sendPack ( pack ) ;
2014-03-17 22:51:07 +03:00
}
}
2018-02-25 18:16:15 +02:00
void CGameHandler : : sendAndApply ( CPackForClient * pack )
2014-03-17 22:51:07 +03:00
{
2018-02-25 18:16:15 +02:00
sendToAllClients ( pack ) ;
gs - > apply ( pack ) ;
logNetwork - > trace ( " \t Applied on gs: %s " , typeid ( * pack ) . name ( ) ) ;
2014-03-17 22:51:07 +03:00
}
2018-02-25 18:16:15 +02:00
void CGameHandler : : applyAndSend ( CPackForClient * pack )
2014-03-17 22:51:07 +03:00
{
2018-02-25 18:16:15 +02:00
gs - > apply ( pack ) ;
sendToAllClients ( pack ) ;
2014-03-17 22:51:07 +03:00
}
2018-02-25 18:16:15 +02:00
void CGameHandler : : sendAndApply ( CGarrisonOperationPack * pack )
2014-03-17 22:51:07 +03:00
{
2018-02-25 18:16:15 +02:00
sendAndApply ( static_cast < CPackForClient * > ( pack ) ) ;
2014-03-17 22:51:07 +03:00
checkVictoryLossConditionsForAll ( ) ;
}
2018-02-25 18:16:15 +02:00
void CGameHandler : : sendAndApply ( SetResources * pack )
2014-03-17 22:51:07 +03:00
{
2018-02-25 18:16:15 +02:00
sendAndApply ( static_cast < CPackForClient * > ( pack ) ) ;
checkVictoryLossConditionsForPlayer ( pack - > player ) ;
2014-03-17 22:51:07 +03:00
}
2018-02-25 18:16:15 +02:00
void CGameHandler : : sendAndApply ( NewStructures * pack )
2014-03-17 22:51:07 +03:00
{
2018-02-25 18:16:15 +02:00
sendAndApply ( static_cast < CPackForClient * > ( pack ) ) ;
checkVictoryLossConditionsForPlayer ( getTown ( pack - > tid ) - > tempOwner ) ;
2014-03-17 22:51:07 +03:00
}
2023-02-12 09:23:39 +02:00
bool CGameHandler : : isPlayerOwns ( CPackForServer * pack , ObjectInstanceID id )
{
return getPlayerAt ( pack - > c ) = = getOwner ( id ) ;
}
void CGameHandler : : throwNotAllowedAction ( CPackForServer * pack )
{
if ( pack - > c )
{
SystemMessage temp_message ( " You are not allowed to perform this action! " ) ;
pack - > c - > sendPack ( & temp_message ) ;
}
logNetwork - > error ( " Player is not allowed to perform this action! " ) ;
throw ExceptionNotAllowedAction ( ) ;
}
void CGameHandler : : wrongPlayerMessage ( CPackForServer * pack , PlayerColor expectedplayer )
{
std : : ostringstream oss ;
oss < < " You were identified as player " < < getPlayerAt ( pack - > c ) < < " while expecting " < < expectedplayer ;
logNetwork - > error ( oss . str ( ) ) ;
if ( pack - > c )
{
SystemMessage temp_message ( oss . str ( ) ) ;
pack - > c - > sendPack ( & temp_message ) ;
}
}
void CGameHandler : : throwOnWrongOwner ( CPackForServer * pack , ObjectInstanceID id )
{
if ( ! isPlayerOwns ( pack , id ) )
{
wrongPlayerMessage ( pack , getOwner ( id ) ) ;
throwNotAllowedAction ( pack ) ;
}
}
void CGameHandler : : throwOnWrongPlayer ( CPackForServer * pack , PlayerColor player )
{
if ( ! hasPlayerAt ( player , pack - > c ) & & player ! = getPlayerAt ( pack - > c ) )
{
wrongPlayerMessage ( pack , player ) ;
throwNotAllowedAction ( pack ) ;
}
}
void CGameHandler : : throwAndComplain ( CPackForServer * pack , std : : string txt )
{
complain ( txt ) ;
throwNotAllowedAction ( pack ) ;
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : save ( const std : : string & filename )
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
logGlobal - > info ( " Saving to %s " , filename ) ;
2016-01-26 15:51:38 +02:00
const auto stem = FileInfo : : GetPathStem ( filename ) ;
const auto savefname = stem . to_string ( ) + " .vsgm1 " ;
CResourceHandler : : get ( " local " ) - > createResource ( savefname ) ;
2014-03-17 22:51:07 +03:00
{
2016-08-30 00:11:54 +02:00
logGlobal - > info ( " Ordering clients to serialize... " ) ;
2018-01-05 19:21:07 +02:00
SaveGameClient sg ( savefname ) ;
2014-03-17 22:51:07 +03:00
sendToAllClients ( & sg ) ;
}
try
{
{
2016-01-26 15:51:38 +02:00
CSaveFile save ( * CResourceHandler : : get ( " local " ) - > getResourceName ( ResourceID ( stem . to_string ( ) , EResType : : SERVER_SAVEGAME ) ) ) ;
2014-03-17 22:51:07 +03:00
saveCommonState ( save ) ;
2016-08-30 00:11:54 +02:00
logGlobal - > info ( " Saving server state " ) ;
2014-03-17 22:51:07 +03:00
save < < * this ;
}
2016-08-30 00:11:54 +02:00
logGlobal - > info ( " Game has been successfully saved! " ) ;
2014-03-17 22:51:07 +03:00
}
catch ( std : : exception & e )
{
2016-08-30 00:11:54 +02:00
logGlobal - > error ( " Failed to save game: %s " , e . what ( ) ) ;
2014-03-17 22:51:07 +03:00
}
}
2022-09-23 13:02:19 +02:00
bool CGameHandler : : load ( const std : : string & filename )
2014-03-17 22:51:07 +03:00
{
2018-01-05 19:21:07 +02:00
logGlobal - > info ( " Loading from %s " , filename ) ;
const auto stem = FileInfo : : GetPathStem ( filename ) ;
2014-03-17 22:51:07 +03:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
reinitScripting ( ) ;
2018-01-05 19:21:07 +02:00
try
2017-05-31 08:45:26 +02:00
{
{
2018-01-05 19:21:07 +02:00
CLoadFile lf ( * CResourceHandler : : get ( " local " ) - > getResourceName ( ResourceID ( stem . to_string ( ) , EResType : : SERVER_SAVEGAME ) ) , MINIMAL_SERIALIZATION_VERSION ) ;
loadCommonState ( lf ) ;
logGlobal - > info ( " Loading server state " ) ;
lf > > * this ;
2017-05-31 08:45:26 +02:00
}
2018-01-05 19:21:07 +02:00
logGlobal - > info ( " Game has been successfully loaded! " ) ;
}
2022-09-23 13:02:19 +02:00
catch ( const CModHandler : : Incompatibility & e )
2018-01-05 19:21:07 +02:00
{
logGlobal - > error ( " Failed to load game: %s " , e . what ( ) ) ;
2022-12-27 22:19:05 +02:00
auto errorMsg = VLC - > generaltexth - > translate ( " vcmi.server.errors.modsIncompatibility " ) + ' \n ' ;
2022-09-23 13:20:11 +02:00
errorMsg + = e . what ( ) ;
lobby - > announceMessage ( errorMsg ) ;
2022-09-23 13:02:19 +02:00
return false ;
}
catch ( const std : : exception & e )
{
logGlobal - > error ( " Failed to load game: %s " , e . what ( ) ) ;
return false ;
2017-05-31 08:45:26 +02:00
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
gs - > preInit ( VLC ) ;
2018-01-05 19:21:07 +02:00
gs - > updateOnLoad ( lobby - > si . get ( ) ) ;
2022-09-23 13:02:19 +02:00
return true ;
2017-05-31 08:45:26 +02:00
}
2021-11-28 14:57:38 +02:00
bool CGameHandler : : bulkSplitStack ( SlotID slotSrc , ObjectInstanceID srcOwner , si32 howMany )
{
if ( ! slotSrc . validSlot ( ) & & complain ( complainInvalidSlot ) )
return false ;
const CArmedInstance * army = static_cast < const CArmedInstance * > ( getObjInstance ( srcOwner ) ) ;
const CCreatureSet & creatureSet = * army ;
if ( ( ! vstd : : contains ( creatureSet . stacks , slotSrc ) & & complain ( complainNoCreatures ) )
| | ( howMany < 1 & & complain ( " Invalid split parameter! " ) ) )
{
return false ;
}
auto actualAmount = army - > getStackCount ( slotSrc ) ;
if ( actualAmount < = howMany & & complain ( complainNotEnoughCreatures ) ) // '<=' because it's not intended just for moving a stack
return false ;
auto freeSlots = creatureSet . getFreeSlots ( ) ;
if ( freeSlots . empty ( ) & & complain ( " No empty stacks " ) )
return false ;
BulkRebalanceStacks bulkRS ;
for ( auto slot : freeSlots )
{
RebalanceStacks rs ;
rs . srcArmy = army - > id ;
rs . dstArmy = army - > id ;
rs . srcSlot = slotSrc ;
rs . dstSlot = slot ;
rs . count = howMany ;
bulkRS . moves . push_back ( rs ) ;
actualAmount - = howMany ;
if ( actualAmount < = howMany )
break ;
}
sendAndApply ( & bulkRS ) ;
return true ;
}
bool CGameHandler : : bulkMergeStacks ( SlotID slotSrc , ObjectInstanceID srcOwner )
{
if ( ! slotSrc . validSlot ( ) & & complain ( complainInvalidSlot ) )
return false ;
const CArmedInstance * army = static_cast < const CArmedInstance * > ( getObjInstance ( srcOwner ) ) ;
const CCreatureSet & creatureSet = * army ;
if ( ! vstd : : contains ( creatureSet . stacks , slotSrc ) & & complain ( complainNoCreatures ) )
return false ;
auto actualAmount = creatureSet . getStackCount ( slotSrc ) ;
if ( actualAmount < 1 & & complain ( complainNoCreatures ) )
return false ;
auto currentCreature = creatureSet . getCreature ( slotSrc ) ;
if ( ! currentCreature & & complain ( complainNoCreatures ) )
return false ;
auto creatureSlots = creatureSet . getCreatureSlots ( currentCreature , slotSrc ) ;
if ( ! creatureSlots . size ( ) )
return false ;
BulkRebalanceStacks bulkRS ;
for ( auto slot : creatureSlots )
{
RebalanceStacks rs ;
rs . srcArmy = army - > id ;
rs . dstArmy = army - > id ;
rs . srcSlot = slot ;
rs . dstSlot = slotSrc ;
rs . count = creatureSet . getStackCount ( slot ) ;
bulkRS . moves . push_back ( rs ) ;
}
sendAndApply ( & bulkRS ) ;
return true ;
}
bool CGameHandler : : bulkMoveArmy ( ObjectInstanceID srcArmy , ObjectInstanceID destArmy , SlotID srcSlot )
{
if ( ! srcSlot . validSlot ( ) & & complain ( complainInvalidSlot ) )
return false ;
const CArmedInstance * armySrc = static_cast < const CArmedInstance * > ( getObjInstance ( srcArmy ) ) ;
const CCreatureSet & setSrc = * armySrc ;
if ( ! vstd : : contains ( setSrc . stacks , srcSlot ) & & complain ( complainNoCreatures ) )
return false ;
const CArmedInstance * armyDest = static_cast < const CArmedInstance * > ( getObjInstance ( destArmy ) ) ;
const CCreatureSet & setDest = * armyDest ;
auto freeSlots = setDest . getFreeSlotsQueue ( ) ;
typedef std : : map < SlotID , std : : pair < SlotID , TQuantity > > TRebalanceMap ;
TRebalanceMap moves ;
auto srcQueue = setSrc . getCreatureQueue ( srcSlot ) ; // Exclude srcSlot, it should be moved last
auto slotsLeft = setSrc . stacksCount ( ) ;
auto destMap = setDest . getCreatureMap ( ) ;
TMapCreatureSlot : : key_compare keyComp = destMap . key_comp ( ) ;
while ( ! srcQueue . empty ( ) )
{
auto pair = srcQueue . top ( ) ;
srcQueue . pop ( ) ;
auto currCreature = pair . first ;
auto currSlot = pair . second ;
const auto quantity = setSrc . getStackCount ( currSlot ) ;
TMapCreatureSlot : : iterator lb = destMap . lower_bound ( currCreature ) ;
const bool alreadyExists = ( lb ! = destMap . end ( ) & & ! ( keyComp ( currCreature , lb - > first ) ) ) ;
if ( ! alreadyExists )
{
if ( freeSlots . empty ( ) )
continue ;
auto currFreeSlot = freeSlots . front ( ) ;
freeSlots . pop ( ) ;
destMap . insert ( lb , TMapCreatureSlot : : value_type ( currCreature , currFreeSlot ) ) ;
}
moves . insert ( std : : make_pair ( currSlot , std : : make_pair ( destMap [ currCreature ] , quantity ) ) ) ;
slotsLeft - - ;
}
if ( slotsLeft = = 1 )
{
auto lastCreature = setSrc . getCreature ( srcSlot ) ;
auto slotToMove = SlotID ( ) ;
// Try to find a slot for last creature
if ( destMap . find ( lastCreature ) = = destMap . end ( ) )
{
if ( ! freeSlots . empty ( ) )
slotToMove = freeSlots . front ( ) ;
}
else
{
slotToMove = destMap [ lastCreature ] ;
}
if ( slotToMove ! = SlotID ( ) )
{
const bool needsLastStack = armySrc - > needsLastStack ( ) ;
const auto quantity = setSrc . getStackCount ( srcSlot ) - ( needsLastStack ? 1 : 0 ) ;
moves . insert ( std : : make_pair ( srcSlot , std : : make_pair ( slotToMove , quantity ) ) ) ;
}
}
BulkRebalanceStacks bulkRS ;
for ( auto & move : moves )
{
RebalanceStacks rs ;
rs . srcArmy = armySrc - > id ;
rs . dstArmy = armyDest - > id ;
rs . srcSlot = move . first ;
rs . dstSlot = move . second . first ;
rs . count = move . second . second ;
bulkRS . moves . push_back ( rs ) ;
}
sendAndApply ( & bulkRS ) ;
return true ;
}
bool CGameHandler : : bulkSmartSplitStack ( SlotID slotSrc , ObjectInstanceID srcOwner )
{
if ( ! slotSrc . validSlot ( ) & & complain ( complainInvalidSlot ) )
return false ;
const CArmedInstance * army = static_cast < const CArmedInstance * > ( getObjInstance ( srcOwner ) ) ;
const CCreatureSet & creatureSet = * army ;
if ( ! vstd : : contains ( creatureSet . stacks , slotSrc ) & & complain ( complainNoCreatures ) )
return false ;
auto actualAmount = creatureSet . getStackCount ( slotSrc ) ;
if ( actualAmount < = 1 & & complain ( complainNoCreatures ) )
return false ;
auto freeSlot = creatureSet . getFreeSlot ( ) ;
auto currentCreature = creatureSet . getCreature ( slotSrc ) ;
if ( freeSlot = = SlotID ( ) & & creatureSet . isCreatureBalanced ( currentCreature ) )
return true ;
auto creatureSlots = creatureSet . getCreatureSlots ( currentCreature , SlotID ( - 1 ) , 1 ) ; // Ignore slots where's only 1 creature, don't ignore slotSrc
TQuantity totalCreatures = 0 ;
for ( auto slot : creatureSlots )
totalCreatures + = creatureSet . getStackCount ( slot ) ;
if ( totalCreatures < = 1 & & complain ( " Total creatures number is invalid " ) )
return false ;
if ( freeSlot ! = SlotID ( ) )
creatureSlots . push_back ( freeSlot ) ;
if ( creatureSlots . empty ( ) & & complain ( " No available slots for smart rebalancing " ) )
return false ;
const auto totalCreatureSlots = creatureSlots . size ( ) ;
const auto rem = totalCreatures % totalCreatureSlots ;
const auto quotient = totalCreatures / totalCreatureSlots ;
// totalCreatures == rem * (quotient + 1) + (totalCreatureSlots - rem) * quotient;
// Proof: r(q+1)+(s-r)q = rq+r+qs-rq = r+qs = total, where total/s = q+r/s
BulkSmartRebalanceStacks bulkSRS ;
if ( freeSlot ! = SlotID ( ) )
{
RebalanceStacks rs ;
rs . srcArmy = rs . dstArmy = army - > id ;
rs . srcSlot = slotSrc ;
rs . dstSlot = freeSlot ;
rs . count = 1 ;
bulkSRS . moves . push_back ( rs ) ;
}
auto currSlot = 0 ;
auto check = 0 ;
for ( auto slot : creatureSlots )
{
ChangeStackCount csc ;
csc . army = army - > id ;
csc . slot = slot ;
csc . count = ( currSlot < rem )
? quotient + 1
: quotient ;
csc . absoluteValue = true ;
bulkSRS . changes . push_back ( csc ) ;
currSlot + + ;
check + = csc . count ;
}
if ( check ! = totalCreatures )
{
complain ( ( boost : : format ( " Failure: totalCreatures=%d but check=%d " ) % totalCreatures % check ) . str ( ) ) ;
return false ;
}
sendAndApply ( & bulkSRS ) ;
return true ;
}
2016-10-12 17:16:26 +02:00
bool CGameHandler : : arrangeStacks ( ObjectInstanceID id1 , ObjectInstanceID id2 , ui8 what , SlotID p1 , SlotID p2 , si32 val , PlayerColor player )
2014-03-17 22:51:07 +03:00
{
2016-09-18 10:53:51 +02:00
const CArmedInstance * s1 = static_cast < const CArmedInstance * > ( getObjInstance ( id1 ) ) ,
* s2 = static_cast < const CArmedInstance * > ( getObjInstance ( id2 ) ) ;
2014-03-17 22:51:07 +03:00
const CCreatureSet & S1 = * s1 , & S2 = * s2 ;
StackLocation sl1 ( s1 , p1 ) , sl2 ( s2 , p2 ) ;
2016-10-12 17:16:26 +02:00
if ( ! sl1 . slot . validSlot ( ) | | ! sl2 . slot . validSlot ( ) )
2014-03-17 22:51:07 +03:00
{
2021-11-28 14:57:38 +02:00
complain ( complainInvalidSlot ) ;
2014-03-17 22:51:07 +03:00
return false ;
}
2016-10-12 17:16:26 +02:00
if ( ! isAllowedExchange ( id1 , id2 ) )
2014-03-17 22:51:07 +03:00
{
complain ( " Cannot exchange stacks between these two objects! \n " ) ;
return false ;
}
2016-09-19 02:20:44 +02:00
// We can always put stacks into locked garrison, but not take them out of it
auto notRemovable = [ & ] ( const CArmedInstance * army )
{
2016-10-12 17:16:26 +02:00
if ( id1 ! = id2 ) // Stack arrangement inside locked garrison is allowed
2016-09-19 02:20:44 +02:00
{
auto g = dynamic_cast < const CGGarrison * > ( army ) ;
2016-10-12 17:16:26 +02:00
if ( g & & ! g - > removableUnits )
2016-09-19 02:20:44 +02:00
{
complain ( " Stacks in this garrison are not removable! \n " ) ;
return true ;
}
}
return false ;
} ;
2016-10-12 17:16:26 +02:00
if ( what = = 1 ) //swap
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ( ( s1 - > tempOwner ! = player & & s1 - > tempOwner ! = PlayerColor : : UNFLAGGABLE ) & & s1 - > getStackCount ( p1 ) )
2014-03-17 22:51:07 +03:00
| | ( ( s2 - > tempOwner ! = player & & s2 - > tempOwner ! = PlayerColor : : UNFLAGGABLE ) & & s2 - > getStackCount ( p2 ) ) )
{
complain ( " Can't take troops from another player! " ) ;
return false ;
}
2015-02-22 19:36:13 +02:00
if ( sl1 . army = = sl2 . army & & sl1 . slot = = sl2 . slot )
{
complain ( " Cannot swap stacks - slots are the same! " ) ;
return false ;
}
2016-10-12 17:16:26 +02:00
if ( ! s1 - > slotEmpty ( p1 ) & & ! s2 - > slotEmpty ( p2 ) )
2016-09-19 02:20:44 +02:00
{
2016-10-12 17:16:26 +02:00
if ( notRemovable ( sl1 . army ) | | notRemovable ( sl2 . army ) )
2016-09-19 02:20:44 +02:00
return false ;
}
2016-10-12 17:16:26 +02:00
if ( s1 - > slotEmpty ( p1 ) & & notRemovable ( sl2 . army ) )
2016-09-19 02:20:44 +02:00
return false ;
2016-10-12 17:16:26 +02:00
else if ( s2 - > slotEmpty ( p2 ) & & notRemovable ( sl1 . army ) )
2016-09-19 02:20:44 +02:00
return false ;
2014-03-17 22:51:07 +03:00
swapStacks ( sl1 , sl2 ) ;
}
2016-10-12 17:16:26 +02:00
else if ( what = = 2 ) //merge
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ( s1 - > getCreature ( p1 ) ! = s2 - > getCreature ( p2 ) & & complain ( " Cannot merge different creatures stacks! " ) )
2014-03-17 22:51:07 +03:00
| | ( ( ( s1 - > tempOwner ! = player & & s1 - > tempOwner ! = PlayerColor : : UNFLAGGABLE ) & & s2 - > getStackCount ( p2 ) ) & & complain ( " Can't take troops from another player! " ) ) )
return false ;
2016-10-12 17:16:26 +02:00
if ( s1 - > slotEmpty ( p1 ) | | s2 - > slotEmpty ( p2 ) )
2016-09-19 02:20:44 +02:00
{
complain ( " Cannot merge empty stack! " ) ;
return false ;
}
2016-10-12 17:16:26 +02:00
else if ( notRemovable ( sl1 . army ) )
2016-09-19 02:20:44 +02:00
return false ;
2014-03-17 22:51:07 +03:00
moveStack ( sl1 , sl2 ) ;
}
2016-10-12 17:16:26 +02:00
else if ( what = = 3 ) //split
2014-03-17 22:51:07 +03:00
{
const int countToMove = val - s2 - > getStackCount ( p2 ) ;
const int countLeftOnSrc = s1 - > getStackCount ( p1 ) - countToMove ;
2016-10-12 17:16:26 +02:00
if ( ( s1 - > tempOwner ! = player & & countLeftOnSrc < s1 - > getStackCount ( p1 ) )
| | ( s2 - > tempOwner ! = player & & val < s2 - > getStackCount ( p2 ) ) )
2014-03-17 22:51:07 +03:00
{
complain ( " Can't move troops of another player! " ) ;
return false ;
}
//general conditions checking
2021-11-28 14:57:38 +02:00
if ( ( ! vstd : : contains ( S1 . stacks , p1 ) & & complain ( complainNoCreatures ) )
| | ( val < 1 & & complain ( complainNoCreatures ) ) )
2014-03-17 22:51:07 +03:00
{
return false ;
}
2016-10-12 17:16:26 +02:00
if ( vstd : : contains ( S2 . stacks , p2 ) ) //dest. slot not free - it must be "rebalancing"...
2014-03-17 22:51:07 +03:00
{
int total = s1 - > getStackCount ( p1 ) + s2 - > getStackCount ( p2 ) ;
2016-10-12 17:16:26 +02:00
if ( ( total < val & & complain ( " Cannot split that stack, not enough creatures! " ) )
2014-03-17 22:51:07 +03:00
| | ( s1 - > getCreature ( p1 ) ! = s2 - > getCreature ( p2 ) & & complain ( " Cannot rebalance different creatures stacks! " ) )
)
{
return false ;
}
2016-10-12 17:16:26 +02:00
if ( notRemovable ( sl1 . army ) )
2016-09-19 02:20:44 +02:00
{
2016-10-12 17:16:26 +02:00
if ( s1 - > getStackCount ( p1 ) > countLeftOnSrc )
2016-09-19 02:20:44 +02:00
return false ;
}
2016-10-12 17:16:26 +02:00
else if ( notRemovable ( sl2 . army ) )
2016-09-19 02:20:44 +02:00
{
2016-10-12 17:16:26 +02:00
if ( s2 - > getStackCount ( p1 ) < countLeftOnSrc )
2016-09-19 02:20:44 +02:00
return false ;
}
2014-03-17 22:51:07 +03:00
moveStack ( sl1 , sl2 , countToMove ) ;
//S2.slots[p2]->count = val;
//S1.slots[p1]->count = total - val;
}
else //split one stack to the two
{
2016-10-12 17:16:26 +02:00
if ( s1 - > getStackCount ( p1 ) < val ) //not enough creatures
2014-03-17 22:51:07 +03:00
{
2021-11-28 14:57:38 +02:00
complain ( complainNotEnoughCreatures ) ;
2014-03-17 22:51:07 +03:00
return false ;
}
2016-10-12 17:16:26 +02:00
if ( notRemovable ( sl1 . army ) )
2016-09-19 02:20:44 +02:00
return false ;
2014-03-17 22:51:07 +03:00
moveStack ( sl1 , sl2 , val ) ;
}
}
return true ;
}
2022-10-05 17:04:51 +02:00
bool CGameHandler : : hasPlayerAt ( PlayerColor player , std : : shared_ptr < CConnection > c ) const
{
return connections . at ( player ) . count ( c ) ;
}
2018-01-05 19:21:07 +02:00
PlayerColor CGameHandler : : getPlayerAt ( std : : shared_ptr < CConnection > c ) const
2014-03-17 22:51:07 +03:00
{
std : : set < PlayerColor > all ;
2016-10-12 17:16:26 +02:00
for ( auto i = connections . cbegin ( ) ; i ! = connections . cend ( ) ; i + + )
2018-01-05 19:21:07 +02:00
if ( vstd : : contains ( i - > second , c ) )
2014-03-17 22:51:07 +03:00
all . insert ( i - > first ) ;
switch ( all . size ( ) )
{
case 0 :
return PlayerColor : : NEUTRAL ;
case 1 :
return * all . begin ( ) ;
default :
{
//if we have more than one player at this connection, try to pick active one
2016-10-12 17:16:26 +02:00
if ( vstd : : contains ( all , gs - > currentPlayer ) )
2014-03-17 22:51:07 +03:00
return gs - > currentPlayer ;
else
return PlayerColor : : CANNOT_DETERMINE ; //cannot say which player is it
}
}
}
2016-10-12 17:16:26 +02:00
bool CGameHandler : : disbandCreature ( ObjectInstanceID id , SlotID pos )
2014-03-17 22:51:07 +03:00
{
2016-09-18 10:53:51 +02:00
const CArmedInstance * s1 = static_cast < const CArmedInstance * > ( getObjInstance ( id ) ) ;
2016-10-12 17:16:26 +02:00
if ( ! vstd : : contains ( s1 - > stacks , pos ) )
2014-03-17 22:51:07 +03:00
{
complain ( " Illegal call to disbandCreature - no such stack in army! " ) ;
return false ;
}
eraseStack ( StackLocation ( s1 , pos ) ) ;
return true ;
}
2017-07-15 13:08:20 +02:00
bool CGameHandler : : buildStructure ( ObjectInstanceID tid , BuildingID requestedID , bool force )
2014-03-17 22:51:07 +03:00
{
const CGTownInstance * t = getTown ( tid ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( ! t )
2014-03-17 22:51:07 +03:00
COMPLAIN_RETF ( " No such town (ID=%s)! " , tid ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( ! t - > town - > buildings . count ( requestedID ) )
2023-01-04 15:17:50 +02:00
COMPLAIN_RETF ( " Town of faction %s does not have info about building ID=%s! " , t - > town - > faction - > getNameTranslated ( ) % requestedID ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( t - > hasBuilt ( requestedID ) )
2023-01-04 15:17:50 +02:00
COMPLAIN_RETF ( " Building %s is already built in %s " , t - > town - > buildings . at ( requestedID ) - > getNameTranslated ( ) % t - > getNameTranslated ( ) ) ;
2014-03-17 22:51:07 +03:00
const CBuilding * requestedBuilding = t - > town - > buildings . at ( requestedID ) ;
//Vector with future list of built building and buildings in auto-mode that are not yet built.
std : : vector < const CBuilding * > remainingAutoBuildings ;
std : : set < BuildingID > buildingsThatWillBe ;
//Check validity of request
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( ! force )
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
switch ( requestedBuilding - > mode )
2014-03-17 22:51:07 +03:00
{
case CBuilding : : BUILD_NORMAL :
2016-10-12 17:16:26 +02:00
if ( canBuildStructure ( t , requestedID ) ! = EBuildingState : : ALLOWED )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Cannot build that building! " ) ;
break ;
case CBuilding : : BUILD_AUTO :
case CBuilding : : BUILD_SPECIAL :
COMPLAIN_RET ( " This building can not be constructed normally! " ) ;
case CBuilding : : BUILD_GRAIL :
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( requestedBuilding - > mode = = CBuilding : : BUILD_GRAIL ) //needs grail
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( ! t - > visitingHero | | ! t - > visitingHero - > hasArt ( ArtifactID : : GRAIL ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Cannot build this without grail! " )
else
removeArtifact ( ArtifactLocation ( t - > visitingHero , t - > visitingHero - > getArtPos ( ArtifactID : : GRAIL , false ) ) ) ;
}
break ;
}
}
2016-03-13 10:31:09 +02:00
//Performs stuff that has to be done before new building is built
auto processBeforeBuiltStructure = [ t , this ] ( const BuildingID buildingID )
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( buildingID > = BuildingID : : DWELL_FIRST ) //dwelling
2014-03-17 22:51:07 +03:00
{
int level = ( buildingID - BuildingID : : DWELL_FIRST ) % GameConstants : : CREATURES_PER_TOWN ;
int upgradeNumber = ( buildingID - BuildingID : : DWELL_FIRST ) / GameConstants : : CREATURES_PER_TOWN ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( upgradeNumber > = t - > town - > creatures . at ( level ) . size ( ) )
2014-03-17 22:51:07 +03:00
{
complain ( boost : : str ( boost : : format ( " Error ecountered when building dwelling (bid=%s): "
" no creature found (upgrade number %d, level %d! " )
% buildingID % upgradeNumber % level ) ) ;
return ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
CCreature * crea = VLC - > creh - > objects . at ( t - > town - > creatures . at ( level ) . at ( upgradeNumber ) ) ;
2014-03-17 22:51:07 +03:00
SetAvailableCreatures ssi ;
ssi . tid = t - > id ;
ssi . creatures = t - > creatures ;
if ( ssi . creatures [ level ] . second . empty ( ) ) // first creature in a dwelling
2023-04-05 02:26:29 +02:00
ssi . creatures [ level ] . first = crea - > getGrowth ( ) ;
ssi . creatures [ level ] . second . push_back ( crea - > getId ( ) ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & ssi ) ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( t - > town - > buildings . at ( buildingID ) - > subId = = BuildingSubID : : PORTAL_OF_SUMMONING )
2014-03-17 22:51:07 +03:00
{
setPortalDwelling ( t ) ;
}
2016-03-13 10:31:09 +02:00
} ;
2014-03-17 22:51:07 +03:00
2016-03-13 10:31:09 +02:00
//Performs stuff that has to be done after new building is built
auto processAfterBuiltStructure = [ t , this ] ( const BuildingID buildingID )
{
2020-10-02 23:55:46 +02:00
auto isMageGuild = ( buildingID < = BuildingID : : MAGES_GUILD_5 & & buildingID > = BuildingID : : MAGES_GUILD_1 ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
auto isLibrary = isMageGuild ? false
2020-10-02 23:55:46 +02:00
: t - > town - > buildings . at ( buildingID ) - > subId = = BuildingSubID : : EBuildingSubID : : LIBRARY ;
if ( isMageGuild | | isLibrary | | ( t - > subID = = ETownType : : CONFLUX & & buildingID = = BuildingID : : GRAIL ) )
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( t - > visitingHero )
2014-03-17 22:51:07 +03:00
giveSpells ( t , t - > visitingHero ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( t - > garrisonHero )
2014-03-17 22:51:07 +03:00
giveSpells ( t , t - > garrisonHero ) ;
}
} ;
//Checks if all requirements will be met with expected building list "buildingsThatWillBe"
auto areRequirementsFullfilled = [ & ] ( const BuildingID & buildID )
{
return buildingsThatWillBe . count ( buildID ) ;
} ;
//Init the vectors
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
for ( auto & build : t - > town - > buildings )
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( t - > hasBuilt ( build . first ) )
{
2014-03-17 22:51:07 +03:00
buildingsThatWillBe . insert ( build . first ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
}
2014-03-17 22:51:07 +03:00
else
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( build . second - > mode = = CBuilding : : BUILD_AUTO ) //not built auto building
2014-03-17 22:51:07 +03:00
remainingAutoBuildings . push_back ( build . second ) ;
}
}
//Prepare structure (list of building ids will be filled later)
NewStructures ns ;
ns . tid = tid ;
ns . builded = force ? t - > builded : ( t - > builded + 1 ) ;
std : : queue < const CBuilding * > buildingsToAdd ;
buildingsToAdd . push ( requestedBuilding ) ;
while ( ! buildingsToAdd . empty ( ) )
{
auto b = buildingsToAdd . front ( ) ;
buildingsToAdd . pop ( ) ;
ns . bid . insert ( b - > bid ) ;
buildingsThatWillBe . insert ( b - > bid ) ;
remainingAutoBuildings - = b ;
2017-10-28 01:25:44 +02:00
for ( auto autoBuilding : remainingAutoBuildings )
2014-03-17 22:51:07 +03:00
{
2017-10-28 01:25:44 +02:00
auto actualRequirements = t - > genBuildingRequirements ( autoBuilding - > bid ) ;
if ( actualRequirements . test ( areRequirementsFullfilled ) )
2014-03-17 22:51:07 +03:00
buildingsToAdd . push ( autoBuilding ) ;
}
}
2016-03-13 10:31:09 +02:00
// FIXME: it's done before NewStructures applied because otherwise town window wont be properly updated on client. That should be actually fixed on client and not on server.
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
for ( auto builtID : ns . bid )
2016-03-13 10:31:09 +02:00
processBeforeBuiltStructure ( builtID ) ;
2014-03-17 22:51:07 +03:00
//Take cost
2021-01-14 00:02:13 +02:00
if ( ! force )
2016-11-26 14:14:43 +02:00
giveResources ( t - > tempOwner , - requestedBuilding - > resources ) ;
2014-03-17 22:51:07 +03:00
2021-01-14 00:02:13 +02:00
//We know what has been built, apply changes. Do this as final step to properly update town window
2014-03-17 22:51:07 +03:00
sendAndApply ( & ns ) ;
2016-03-13 10:31:09 +02:00
//Other post-built events. To some logic like giving spells to work gamestate changes for new building must be already in place!
2021-01-14 00:02:13 +02:00
for ( auto builtID : ns . bid )
2016-03-13 10:31:09 +02:00
processAfterBuiltStructure ( builtID ) ;
2014-03-17 22:51:07 +03:00
// now when everything is built - reveal tiles for lookout tower
FoWChange fw ;
fw . player = t - > tempOwner ;
fw . mode = 1 ;
2016-01-31 17:01:58 +02:00
getTilesInRange ( fw . tiles , t - > getSightCenter ( ) , t - > getSightRadius ( ) , t - > tempOwner , 1 ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & fw ) ;
2021-01-14 00:02:13 +02:00
if ( t - > visitingHero )
2023-05-02 12:45:42 +02:00
objectVisited ( t , t - > visitingHero ) ;
2021-01-14 00:02:13 +02:00
if ( t - > garrisonHero )
2023-05-02 13:09:02 +02:00
visitCastleObjects ( t , t - > garrisonHero ) ;
2014-03-17 22:51:07 +03:00
checkVictoryLossConditionsForPlayer ( t - > tempOwner ) ;
return true ;
}
2021-01-14 00:02:13 +02:00
2014-03-17 22:51:07 +03:00
bool CGameHandler : : razeStructure ( ObjectInstanceID tid , BuildingID bid )
{
///incomplete, simply erases target building
const CGTownInstance * t = getTown ( tid ) ;
if ( ! vstd : : contains ( t - > builtBuildings , bid ) )
return false ;
RazeStructures rs ;
rs . tid = tid ;
rs . bid . insert ( bid ) ;
rs . destroyed = t - > destroyed + 1 ;
sendAndApply ( & rs ) ;
//TODO: Remove dwellers
// if (t->subID == 4 && bid == 17) //Veil of Darkness
// {
// RemoveBonus rb(RemoveBonus::TOWN);
// rb.whoID = t->id;
2023-05-01 00:20:01 +02:00
// rb.source = BonusSource::TOWN_STRUCTURE;
2014-03-17 22:51:07 +03:00
// rb.id = 17;
// sendAndApply(&rb);
// }
return true ;
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : sendMessageToAll ( const std : : string & message )
2014-03-17 22:51:07 +03:00
{
SystemMessage sm ;
sm . text = message ;
sendToAllClients ( & sm ) ;
}
2016-10-12 17:16:26 +02:00
bool CGameHandler : : recruitCreatures ( ObjectInstanceID objid , ObjectInstanceID dstid , CreatureID crid , ui32 cram , si32 fromLvl )
2014-03-17 22:51:07 +03:00
{
2016-09-18 10:53:51 +02:00
const CGDwelling * dw = static_cast < const CGDwelling * > ( getObj ( objid ) ) ;
2014-03-17 22:51:07 +03:00
const CArmedInstance * dst = nullptr ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const CCreature * c = VLC - > creh - > objects . at ( crid ) ;
2017-05-26 18:51:45 +02:00
const bool warMachine = c - > warMachine ! = ArtifactID : : NONE ;
2014-03-17 22:51:07 +03:00
//TODO: test for owning
2014-09-19 00:18:49 +03:00
//TODO: check if dst can recruit objects (e.g. hero is actually visiting object, town and source are same, etc)
dst = dynamic_cast < const CArmedInstance * > ( getObj ( dstid ) ) ;
2014-03-17 22:51:07 +03:00
assert ( dw & & dst ) ;
//verify
bool found = false ;
int level = 0 ;
2016-10-12 17:16:26 +02:00
for ( ; level < dw - > creatures . size ( ) ; level + + ) //iterate through all levels
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ( fromLvl ! = - 1 ) & & ( level ! = fromLvl ) )
2014-03-17 22:51:07 +03:00
continue ;
const auto & cur = dw - > creatures . at ( level ) ; //current level info <amount, list of cr. ids>
int i = 0 ;
2016-10-12 17:16:26 +02:00
for ( ; i < cur . second . size ( ) ; i + + ) //look for crid among available creatures list on current level
if ( cur . second . at ( i ) = = crid )
2014-03-17 22:51:07 +03:00
break ;
2016-10-12 17:16:26 +02:00
if ( i < cur . second . size ( ) )
2014-03-17 22:51:07 +03:00
{
found = true ;
cram = std : : min ( cram , cur . first ) ; //reduce recruited amount up to available amount
break ;
}
}
SlotID slot = dst - > getSlotFor ( crid ) ;
2016-10-12 17:16:26 +02:00
if ( ( ! found & & complain ( " Cannot recruit: no such creatures! " ) )
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
| | ( ( si32 ) cram > VLC - > creh - > objects . at ( crid ) - > maxAmount ( getPlayerState ( dst - > tempOwner ) - > resources ) & & complain ( " Cannot recruit: lack of resources! " ) )
2014-03-17 22:51:07 +03:00
| | ( cram < = 0 & & complain ( " Cannot recruit: cram <= 0! " ) )
| | ( ! slot . validSlot ( ) & & ! warMachine & & complain ( " Cannot recruit: no available slot! " ) ) )
{
return false ;
}
//recruit
2023-04-05 02:26:29 +02:00
giveResources ( dst - > tempOwner , - ( c - > getFullRecruitCost ( ) * cram ) ) ;
2014-03-17 22:51:07 +03:00
SetAvailableCreatures sac ;
sac . tid = objid ;
sac . creatures = dw - > creatures ;
sac . creatures [ level ] . first - = cram ;
sendAndApply ( & sac ) ;
2016-10-12 17:16:26 +02:00
if ( warMachine )
2014-03-17 22:51:07 +03:00
{
const CGHeroInstance * h = dynamic_cast < const CGHeroInstance * > ( dst ) ;
2017-05-26 18:51:45 +02:00
COMPLAIN_RET_FALSE_IF ( ! h , " Only hero can buy war machines " ) ;
ArtifactID artId = c - > warMachine ;
COMPLAIN_RET_FALSE_IF ( artId = = ArtifactID : : CATAPULT , " Catapult cannot be recruited! " ) ;
const CArtifact * art = artId . toArtifact ( ) ;
COMPLAIN_RET_FALSE_IF ( nullptr = = art , " Invalid war machine artifact " ) ;
return giveHeroNewArtifact ( h , art ) ;
2014-03-17 22:51:07 +03:00
}
else
{
addToSlot ( StackLocation ( dst , slot ) , c , cram ) ;
}
return true ;
}
2016-10-12 17:16:26 +02:00
bool CGameHandler : : upgradeCreature ( ObjectInstanceID objid , SlotID pos , CreatureID upgID )
2014-03-17 22:51:07 +03:00
{
2016-09-18 10:53:51 +02:00
const CArmedInstance * obj = static_cast < const CArmedInstance * > ( getObjInstance ( objid ) ) ;
2015-02-14 20:44:04 +02:00
if ( ! obj - > hasStackAtSlot ( pos ) )
{
COMPLAIN_RET ( " Cannot upgrade, no stack at slot " + boost : : to_string ( pos ) ) ;
}
2016-09-18 10:53:51 +02:00
UpgradeInfo ui ;
2022-12-25 14:03:43 +02:00
fillUpgradeInfo ( obj , pos , ui ) ;
2014-03-17 22:51:07 +03:00
PlayerColor player = obj - > tempOwner ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const PlayerState * p = getPlayerState ( player ) ;
2014-03-17 22:51:07 +03:00
int crQuantity = obj - > stacks . at ( pos ) - > count ;
int newIDpos = vstd : : find_pos ( ui . newID , upgID ) ; //get position of new id in UpgradeInfo
//check if upgrade is possible
2016-10-12 17:16:26 +02:00
if ( ( ui . oldID < 0 | | newIDpos = = - 1 ) & & complain ( " That upgrade is not possible! " ) )
2014-03-17 22:51:07 +03:00
{
return false ;
}
TResources totalCost = ui . cost . at ( newIDpos ) * crQuantity ;
//check if player has enough resources
2016-10-12 17:16:26 +02:00
if ( ! p - > resources . canAfford ( totalCost ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Cannot upgrade, not enough resources! " ) ;
//take resources
2016-11-26 14:14:43 +02:00
giveResources ( player , - totalCost ) ;
2014-03-17 22:51:07 +03:00
//upgrade creature
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
changeStackType ( StackLocation ( obj , pos ) , VLC - > creh - > objects . at ( upgID ) ) ;
2014-03-17 22:51:07 +03:00
return true ;
}
2015-09-04 17:08:25 +02:00
bool CGameHandler : : changeStackType ( const StackLocation & sl , const CCreature * c )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ! sl . army - > hasStackAtSlot ( sl . slot ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Cannot find a stack to change type " ) ;
SetStackType sst ;
2018-03-10 23:19:36 +02:00
sst . army = sl . army - > id ;
sst . slot = sl . slot ;
2023-04-05 02:26:29 +02:00
sst . type = c - > getId ( ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & sst ) ;
return true ;
}
void CGameHandler : : moveArmy ( const CArmedInstance * src , const CArmedInstance * dst , bool allowMerging )
{
assert ( src - > canBeMergedWith ( * dst , allowMerging ) ) ;
while ( src - > stacksCount ( ) ) //while there are unmoved creatures
{
auto i = src - > Slots ( ) . begin ( ) ; //iterator to stack to move
StackLocation sl ( src , i - > first ) ; //location of stack to move
SlotID pos = dst - > getSlotFor ( i - > second - > type ) ;
2016-10-12 17:16:26 +02:00
if ( ! pos . validSlot ( ) )
2014-03-17 22:51:07 +03:00
{
//try to merge two other stacks to make place
std : : pair < SlotID , SlotID > toMerge ;
2016-10-12 17:16:26 +02:00
if ( dst - > mergableStacks ( toMerge , i - > first ) & & allowMerging )
2014-03-17 22:51:07 +03:00
{
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 ) ) ;
}
}
}
2021-09-12 13:30:54 +02:00
bool CGameHandler : : swapGarrisonOnSiege ( ObjectInstanceID tid )
2021-03-23 16:47:07 +02:00
{
const CGTownInstance * town = getTown ( tid ) ;
if ( ! town - > garrisonHero = = ! town - > visitingHero )
return false ;
SetHeroesInTown intown ;
intown . tid = tid ;
if ( town - > garrisonHero ) //garrison -> vising
{
intown . garrison = ObjectInstanceID ( ) ;
intown . visiting = town - > garrisonHero - > id ;
}
else //visiting -> garrison
{
if ( town - > armedGarrison ( ) )
town - > mergeGarrisonOnSiege ( ) ;
intown . visiting = ObjectInstanceID ( ) ;
intown . garrison = town - > visitingHero - > id ;
}
sendAndApply ( & intown ) ;
return true ;
}
2016-10-12 17:16:26 +02:00
bool CGameHandler : : garrisonSwap ( ObjectInstanceID tid )
2014-03-17 22:51:07 +03:00
{
2016-09-18 10:53:51 +02:00
const CGTownInstance * town = getTown ( tid ) ;
2016-10-12 17:16:26 +02:00
if ( ! town - > garrisonHero & & town - > visitingHero ) //visiting => garrison, merge armies: town army => hero army
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ! town - > visitingHero - > canBeMergedWith ( * town ) )
2014-03-17 22:51:07 +03:00
{
complain ( " Cannot make garrison swap, not enough free slots! " ) ;
return false ;
}
moveArmy ( town , town - > visitingHero , true ) ;
SetHeroesInTown intown ;
intown . tid = tid ;
intown . visiting = ObjectInstanceID ( ) ;
intown . garrison = town - > visitingHero - > id ;
sendAndApply ( & intown ) ;
return true ;
}
else if ( town - > garrisonHero & & ! town - > visitingHero ) //move hero out of the garrison
{
//check if moving hero out of town will break 8 wandering heroes limit
2016-10-12 17:16:26 +02:00
if ( getHeroCount ( town - > garrisonHero - > tempOwner , false ) > = 8 )
2014-03-17 22:51:07 +03:00
{
complain ( " Cannot move hero out of the garrison, there are already 8 wandering heroes! " ) ;
return false ;
}
SetHeroesInTown intown ;
intown . tid = tid ;
intown . garrison = ObjectInstanceID ( ) ;
intown . visiting = town - > garrisonHero - > id ;
sendAndApply ( & intown ) ;
return true ;
}
2016-10-12 17:16:26 +02:00
else if ( ! ! town - > garrisonHero & & town - > visitingHero ) //swap visiting and garrison hero
2014-03-17 22:51:07 +03:00
{
SetHeroesInTown intown ;
intown . tid = tid ;
intown . garrison = town - > visitingHero - > id ;
intown . visiting = town - > garrisonHero - > id ;
sendAndApply ( & intown ) ;
return true ;
}
else
{
complain ( " Cannot swap garrison hero! " ) ;
return false ;
}
}
// With the amount of changes done to the function, it's more like transferArtifacts.
// 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 ( const ArtifactLocation & al1 , const ArtifactLocation & al2 )
{
ArtifactLocation src = al1 , dst = al2 ;
const PlayerColor srcPlayer = src . owningPlayer ( ) , dstPlayer = dst . owningPlayer ( ) ;
const CArmedInstance * srcObj = src . relatedObj ( ) , * dstObj = dst . relatedObj ( ) ;
// Make sure exchange is even possible between the two heroes.
2023-03-18 12:44:01 +02:00
if ( ! isAllowedExchange ( srcObj - > id , dstObj - > id ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " That heroes cannot make any exchange! " ) ;
const CArtifactInstance * srcArtifact = src . getArt ( ) ;
const CArtifactInstance * destArtifact = dst . getArt ( ) ;
2023-03-18 12:44:01 +02:00
const bool isDstSlotBackpack = ArtifactUtils : : isSlotBackpack ( dst . slot ) ;
2014-03-17 22:51:07 +03:00
2023-03-18 12:44:01 +02:00
if ( srcArtifact = = nullptr )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " No artifact to move! " ) ;
2023-04-23 13:18:55 +02:00
if ( destArtifact & & srcPlayer ! = dstPlayer & & ! isDstSlotBackpack )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Can't touch artifact on hero of another player! " ) ;
// Check if src/dest slots are appropriate for the artifacts exchanged.
// Moving to the backpack is always allowed.
2023-03-18 12:44:01 +02:00
if ( ( ! srcArtifact | | ! isDstSlotBackpack )
2014-03-17 22:51:07 +03:00
& & srcArtifact & & ! srcArtifact - > canBePutAt ( dst , true ) )
COMPLAIN_RET ( " Cannot move artifact! " ) ;
2016-08-18 05:35:29 +02:00
auto srcSlot = src . getSlot ( ) ;
auto dstSlot = dst . getSlot ( ) ;
2023-03-18 12:44:01 +02:00
if ( ( srcSlot & & srcSlot - > locked ) | | ( dstSlot & & dstSlot - > locked ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Cannot move artifact locks. " ) ;
2023-03-18 12:44:01 +02:00
if ( isDstSlotBackpack & & srcArtifact - > artType - > isBig ( ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Cannot put big artifacts in backpack! " ) ;
2023-03-18 12:44:01 +02:00
if ( src . slot = = ArtifactPosition : : MACH4 | | dst . slot = = ArtifactPosition : : MACH4 )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Cannot move catapult! " ) ;
2023-03-18 12:44:01 +02:00
if ( isDstSlotBackpack )
{
if ( ! ArtifactUtils : : isBackpackFreeSlots ( dst . getHolderArtSet ( ) ) )
COMPLAIN_RET ( " Backpack is full! " ) ;
vstd : : amin ( dst . slot , GameConstants : : BACKPACK_START + dst . getHolderArtSet ( ) - > artifactsInBackpack . size ( ) ) ;
}
2014-03-17 22:51:07 +03:00
2022-12-29 20:39:01 +02:00
if ( ! ( src . slot = = ArtifactPosition : : TRANSITION_POS & & dst . slot = = ArtifactPosition : : TRANSITION_POS ) )
2014-03-17 22:51:07 +03:00
{
2022-12-29 20:39:01 +02:00
if ( src . slot = = dst . slot & & src . artHolder = = dst . artHolder )
COMPLAIN_RET ( " Won't move artifact: Dest same as source! " ) ;
2023-01-09 23:27:38 +02:00
2022-12-29 20:39:01 +02:00
// Check if dst slot is occupied
2023-03-18 12:44:01 +02:00
if ( ! isDstSlotBackpack & & destArtifact )
2022-12-29 20:39:01 +02:00
{
// Previous artifact must be removed first
moveArtifact ( dst , ArtifactLocation ( dst . artHolder , ArtifactPosition : : TRANSITION_POS ) ) ;
}
2014-03-17 22:51:07 +03:00
2023-01-15 17:46:42 +02:00
try
{
2023-04-15 03:33:00 +02:00
auto hero = std : : get < ConstTransitivePtr < CGHeroInstance > > ( dst . artHolder ) ;
2023-01-02 15:58:56 +02:00
if ( ArtifactUtils : : checkSpellbookIsNeeded ( hero , srcArtifact - > artType - > getId ( ) , dst . slot ) )
2023-01-15 17:46:42 +02:00
giveHeroNewArtifact ( hero , VLC - > arth - > objects [ ArtifactID : : SPELLBOOK ] , ArtifactPosition : : SPELLBOOK ) ;
}
2023-04-15 03:33:00 +02:00
catch ( const std : : bad_variant_access & )
2023-01-15 17:46:42 +02:00
{
// object other than hero received an art - ignore
}
2014-03-17 22:51:07 +03:00
2022-12-29 20:39:01 +02:00
MoveArtifact ma ( & src , & dst ) ;
2022-12-30 21:43:32 +02:00
if ( dst . slot = = ArtifactPosition : : TRANSITION_POS )
ma . askAssemble = false ;
2022-12-29 20:39:01 +02:00
sendAndApply ( & ma ) ;
}
2022-11-06 23:59:30 +02:00
return true ;
}
2022-11-10 19:09:37 +02:00
bool CGameHandler : : bulkMoveArtifacts ( ObjectInstanceID srcHero , ObjectInstanceID dstHero , bool swap )
2022-11-06 23:59:30 +02:00
{
// Make sure exchange is even possible between the two heroes.
2022-11-10 19:09:37 +02:00
if ( ! isAllowedExchange ( srcHero , dstHero ) )
2022-11-06 23:59:30 +02:00
COMPLAIN_RET ( " That heroes cannot make any exchange! " ) ;
auto psrcHero = getHero ( srcHero ) ;
auto pdstHero = getHero ( dstHero ) ;
2022-11-13 16:24:23 +02:00
if ( ( ! psrcHero ) | | ( ! pdstHero ) )
COMPLAIN_RET ( " bulkMoveArtifacts: wrong hero's ID " ) ;
2022-11-06 23:59:30 +02:00
2022-11-10 19:09:37 +02:00
BulkMoveArtifacts ma ( static_cast < ConstTransitivePtr < CGHeroInstance > > ( psrcHero ) ,
static_cast < ConstTransitivePtr < CGHeroInstance > > ( pdstHero ) , swap ) ;
auto & slotsSrcDst = ma . artsPack0 ;
auto & slotsDstSrc = ma . artsPack1 ;
2022-11-06 23:59:30 +02:00
2023-03-07 13:13:30 +02:00
// Temporary fitting set for artifacts. Used to select available slots before sending data.
CArtifactFittingSet artFittingSet ( pdstHero - > bearerType ( ) ) ;
auto moveArtifact = [ this , & artFittingSet ] ( const CArtifactInstance * artifact ,
ArtifactPosition srcSlot , const CGHeroInstance * dstHero ,
std : : vector < BulkMoveArtifacts : : LinkedSlots > & slots ) - > void
{
assert ( artifact ) ;
2023-03-21 12:13:53 +02:00
auto dstSlot = ArtifactUtils : : getArtAnyPosition ( & artFittingSet , artifact - > getTypeId ( ) ) ;
2023-03-18 12:44:01 +02:00
if ( dstSlot ! = ArtifactPosition : : PRE_FIRST )
{
artFittingSet . putArtifact ( dstSlot , static_cast < ConstTransitivePtr < CArtifactInstance > > ( artifact ) ) ;
slots . push_back ( BulkMoveArtifacts : : LinkedSlots ( srcSlot , dstSlot ) ) ;
2023-03-07 13:13:30 +02:00
2023-03-21 12:13:53 +02:00
if ( ArtifactUtils : : checkSpellbookIsNeeded ( dstHero , artifact - > getTypeId ( ) , dstSlot ) )
2023-03-18 12:44:01 +02:00
giveHeroNewArtifact ( dstHero , VLC - > arth - > objects [ ArtifactID : : SPELLBOOK ] , ArtifactPosition : : SPELLBOOK ) ;
}
2023-03-07 13:13:30 +02:00
} ;
2022-11-10 19:09:37 +02:00
if ( swap )
2022-11-06 23:59:30 +02:00
{
2023-03-07 13:13:30 +02:00
auto moveArtsWorn = [ moveArtifact ] ( const CGHeroInstance * srcHero , const CGHeroInstance * dstHero ,
2022-11-10 19:09:37 +02:00
std : : vector < BulkMoveArtifacts : : LinkedSlots > & slots ) - > void
2022-11-06 23:59:30 +02:00
{
2022-11-10 19:09:37 +02:00
for ( auto & artifact : srcHero - > artifactsWorn )
{
2023-03-07 13:13:30 +02:00
if ( ArtifactUtils : : isArtRemovable ( artifact ) )
moveArtifact ( artifact . second . getArt ( ) , artifact . first , dstHero , slots ) ;
2022-11-10 19:09:37 +02:00
}
} ;
2023-03-07 13:13:30 +02:00
auto moveArtsInBackpack = [ ] ( const CArtifactSet * artSet ,
2022-11-11 01:01:55 +02:00
std : : vector < BulkMoveArtifacts : : LinkedSlots > & slots ) - > void
{
2023-03-07 13:13:30 +02:00
for ( auto & slotInfo : artSet - > artifactsInBackpack )
2022-11-11 01:01:55 +02:00
{
2023-03-07 13:13:30 +02:00
auto slot = artSet - > getArtPos ( slotInfo . artifact ) ;
2022-11-11 01:01:55 +02:00
slots . push_back ( BulkMoveArtifacts : : LinkedSlots ( slot , slot ) ) ;
}
} ;
2022-11-10 19:09:37 +02:00
// Move over artifacts that are worn srcHero -> dstHero
moveArtsWorn ( psrcHero , pdstHero , slotsSrcDst ) ;
2023-03-07 13:13:30 +02:00
artFittingSet . artifactsWorn . clear ( ) ;
2022-11-10 19:09:37 +02:00
// Move over artifacts that are worn dstHero -> srcHero
moveArtsWorn ( pdstHero , psrcHero , slotsDstSrc ) ;
// Move over artifacts that are in backpack srcHero -> dstHero
2022-11-11 01:01:55 +02:00
moveArtsInBackpack ( psrcHero , slotsSrcDst ) ;
2022-11-10 19:09:37 +02:00
// Move over artifacts that are in backpack dstHero -> srcHero
2022-11-11 01:01:55 +02:00
moveArtsInBackpack ( pdstHero , slotsDstSrc ) ;
2022-11-06 23:59:30 +02:00
}
2022-11-10 19:09:37 +02:00
else
2022-11-06 23:59:30 +02:00
{
2022-11-13 12:04:51 +02:00
artFittingSet . artifactsInBackpack = pdstHero - > artifactsInBackpack ;
artFittingSet . artifactsWorn = pdstHero - > artifactsWorn ;
2022-11-06 23:59:30 +02:00
2022-11-10 19:09:37 +02:00
// Move over artifacts that are worn
for ( auto & artInfo : psrcHero - > artifactsWorn )
2022-11-06 23:59:30 +02:00
{
2022-11-10 19:09:37 +02:00
if ( ArtifactUtils : : isArtRemovable ( artInfo ) )
{
2023-03-07 13:13:30 +02:00
moveArtifact ( psrcHero - > getArt ( artInfo . first ) , artInfo . first , pdstHero , slotsSrcDst ) ;
2022-11-10 19:09:37 +02:00
}
}
// Move over artifacts that are in backpack
for ( auto & slotInfo : psrcHero - > artifactsInBackpack )
{
2023-03-07 13:13:30 +02:00
moveArtifact ( psrcHero - > getArt ( psrcHero - > getArtPos ( slotInfo . artifact ) ) ,
psrcHero - > getArtPos ( slotInfo . artifact ) , pdstHero , slotsSrcDst ) ;
2022-11-06 23:59:30 +02:00
}
}
2014-03-17 22:51:07 +03:00
sendAndApply ( & ma ) ;
return true ;
}
/**
* 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 ( ObjectInstanceID heroID , ArtifactPosition artifactSlot , bool assemble , ArtifactID assembleTo )
{
2016-09-18 10:53:51 +02:00
const CGHeroInstance * hero = getHero ( heroID ) ;
2023-03-18 12:44:01 +02:00
const CArtifactInstance * destArtifact = hero - > getArt ( artifactSlot ) ;
2014-03-17 22:51:07 +03:00
2023-03-18 12:44:01 +02:00
if ( ! destArtifact )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " assembleArtifacts: there is no such artifact instance! " ) ;
2022-11-18 01:32:25 +02:00
if ( assemble )
2014-03-17 22:51:07 +03:00
{
2023-03-18 12:44:01 +02:00
CArtifact * combinedArt = VLC - > arth - > objects [ assembleTo ] ;
2022-11-18 01:32:25 +02:00
if ( ! combinedArt - > constituents )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " assembleArtifacts: Artifact being attempted to assemble is not a combined artifacts! " ) ;
2023-04-13 19:40:36 +02:00
if ( ! vstd : : contains ( ArtifactUtils : : assemblyPossibilities ( hero , destArtifact - > getTypeId ( ) ,
ArtifactUtils : : isSlotEquipment ( artifactSlot ) ) , combinedArt ) )
{
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " assembleArtifacts: It's impossible to assemble requested artifact! " ) ;
2023-04-13 19:40:36 +02:00
}
2022-11-18 01:32:25 +02:00
2022-11-07 00:18:05 +02:00
2022-11-10 18:29:39 +02:00
if ( ArtifactUtils : : checkSpellbookIsNeeded ( hero , assembleTo , artifactSlot ) )
2022-11-07 00:18:05 +02:00
giveHeroNewArtifact ( hero , VLC - > arth - > objects [ ArtifactID : : SPELLBOOK ] , ArtifactPosition : : SPELLBOOK ) ;
2014-03-17 22:51:07 +03:00
AssembledArtifact aa ;
aa . al = ArtifactLocation ( hero , artifactSlot ) ;
aa . builtArt = combinedArt ;
sendAndApply ( & aa ) ;
}
else
{
2023-03-18 12:44:01 +02:00
if ( ! destArtifact - > canBeDisassembled ( ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " assembleArtifacts: Artifact being attempted to disassemble is not a combined artifact! " ) ;
2023-03-18 12:44:01 +02:00
if ( ArtifactUtils : : isSlotBackpack ( artifactSlot )
& & ! ArtifactUtils : : isBackpackFreeSlots ( hero , destArtifact - > artType - > constituents - > size ( ) - 1 ) )
COMPLAIN_RET ( " assembleArtifacts: Artifact being attempted to disassemble but backpack is full! " ) ;
2014-03-17 22:51:07 +03:00
DisassembledArtifact da ;
da . al = ArtifactLocation ( hero , artifactSlot ) ;
sendAndApply ( & da ) ;
}
2016-01-23 14:20:51 +02:00
return true ;
2014-03-17 22:51:07 +03:00
}
2023-04-07 23:41:55 +02:00
bool CGameHandler : : eraseArtifactByClient ( const ArtifactLocation & al )
{
const auto * hero = getHero ( al . relatedObj ( ) - > id ) ;
if ( hero = = nullptr )
COMPLAIN_RET ( " eraseArtifactByClient: wrong hero's ID " ) ;
const auto * art = al . getArt ( ) ;
if ( art = = nullptr )
COMPLAIN_RET ( " Cannot remove artifact! " ) ;
if ( al . getArt ( ) - > artType - > canBePutAt ( hero ) | | al . slot ! = ArtifactPosition : : TRANSITION_POS )
2023-04-08 00:54:19 +02:00
COMPLAIN_RET ( " Illegal artifact removal request " ) ;
2023-04-07 23:41:55 +02:00
removeArtifact ( al ) ;
return true ;
}
2016-10-12 17:16:26 +02:00
bool CGameHandler : : buyArtifact ( ObjectInstanceID hid , ArtifactID aid )
2014-03-17 22:51:07 +03:00
{
2016-09-18 10:53:51 +02:00
const CGHeroInstance * hero = getHero ( hid ) ;
2017-05-26 18:51:45 +02:00
COMPLAIN_RET_FALSE_IF ( nullptr = = hero , " Invalid hero index " ) ;
2016-09-18 10:53:51 +02:00
const CGTownInstance * town = hero - > visitedTown ;
2017-05-26 18:51:45 +02:00
COMPLAIN_RET_FALSE_IF ( nullptr = = town , " Hero not in town " ) ;
2016-10-12 17:16:26 +02:00
if ( aid = = ArtifactID : : SPELLBOOK )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ( ! town - > hasBuilt ( BuildingID : : MAGES_GUILD_1 ) & & complain ( " Cannot buy a spellbook, no mage guild in the town! " ) )
2023-04-05 02:26:29 +02:00
| | ( getResource ( hero - > getOwner ( ) , EGameResID : : GOLD ) < GameConstants : : SPELLBOOK_GOLD_COST & & complain ( " Cannot buy a spellbook, not enough gold! " ) )
2014-03-17 22:51:07 +03:00
| | ( hero - > getArt ( ArtifactPosition : : SPELLBOOK ) & & complain ( " Cannot buy a spellbook, hero already has a one! " ) )
2016-10-12 17:16:26 +02:00
)
2014-03-17 22:51:07 +03:00
return false ;
2023-04-05 02:26:29 +02:00
giveResource ( hero - > getOwner ( ) , EGameResID : : GOLD , - GameConstants : : SPELLBOOK_GOLD_COST ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
giveHeroNewArtifact ( hero , VLC - > arth - > objects [ ArtifactID : : SPELLBOOK ] , ArtifactPosition : : SPELLBOOK ) ;
2014-03-17 22:51:07 +03:00
assert ( hero - > getArt ( ArtifactPosition : : SPELLBOOK ) ) ;
giveSpells ( town , hero ) ;
return true ;
}
2017-05-26 18:51:45 +02:00
else
2014-03-17 22:51:07 +03:00
{
2017-05-26 18:51:45 +02:00
const CArtifact * art = aid . toArtifact ( ) ;
COMPLAIN_RET_FALSE_IF ( nullptr = = art , " Invalid artifact index to buy " ) ;
COMPLAIN_RET_FALSE_IF ( art - > warMachine = = CreatureID : : NONE , " War machine artifact required " ) ;
COMPLAIN_RET_FALSE_IF ( hero - > hasArt ( aid ) , " Hero already has this machine! " ) ;
const int price = art - > price ;
2023-04-05 02:26:29 +02:00
COMPLAIN_RET_FALSE_IF ( getPlayerState ( hero - > getOwner ( ) ) - > resources [ EGameResID : : GOLD ] < price , " Not enough gold! " ) ;
2014-03-17 22:51:07 +03:00
2023-03-20 01:18:32 +02:00
if ( ( town - > hasBuilt ( BuildingID : : BLACKSMITH ) & & town - > town - > warMachine = = aid )
2020-10-15 14:03:01 +02:00
| | ( town - > hasBuilt ( BuildingSubID : : BALLISTA_YARD ) & & aid = = ArtifactID : : BALLISTA ) )
2014-03-17 22:51:07 +03:00
{
2023-04-05 02:26:29 +02:00
giveResource ( hero - > getOwner ( ) , EGameResID : : GOLD , - price ) ;
2017-05-26 18:51:45 +02:00
return giveHeroNewArtifact ( hero , art ) ;
2014-03-17 22:51:07 +03:00
}
else
COMPLAIN_RET ( " This machine is unavailable here! " ) ;
}
}
2023-04-05 02:26:29 +02:00
bool CGameHandler : : buyArtifact ( const IMarket * m , const CGHeroInstance * h , GameResID rid , ArtifactID aid )
2014-03-17 22:51:07 +03:00
{
2016-11-27 21:50:37 +02:00
if ( ! h )
COMPLAIN_RET ( " Only hero can buy artifacts! " ) ;
2016-10-12 17:16:26 +02:00
if ( ! vstd : : contains ( m - > availableItemsIds ( EMarketMode : : RESOURCE_ARTIFACT ) , aid ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " That artifact is unavailable! " ) ;
int b1 , b2 ;
m - > getOffer ( rid , aid , b1 , b2 , EMarketMode : : RESOURCE_ARTIFACT ) ;
2016-10-12 17:16:26 +02:00
if ( getResource ( h - > tempOwner , rid ) < b1 )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " You can't afford to buy this artifact! " ) ;
2016-11-26 14:14:43 +02:00
giveResource ( h - > tempOwner , rid , - b1 ) ;
2014-03-17 22:51:07 +03:00
SetAvailableArtifacts saa ;
2023-04-26 22:18:43 +02:00
if ( dynamic_cast < const CGTownInstance * > ( m ) )
2014-03-17 22:51:07 +03:00
{
saa . id = - 1 ;
saa . arts = CGTownInstance : : merchantArtifacts ;
}
2023-04-26 22:18:43 +02:00
else if ( const CGBlackMarket * bm = dynamic_cast < const CGBlackMarket * > ( m ) ) //black market
2014-03-17 22:51:07 +03:00
{
saa . id = bm - > id . getNum ( ) ;
saa . arts = bm - > artifacts ;
}
else
COMPLAIN_RET ( " Wrong marktet... " ) ;
bool found = false ;
2016-10-12 17:16:26 +02:00
for ( const CArtifact * & art : saa . arts )
2014-03-17 22:51:07 +03:00
{
2023-01-02 15:58:56 +02:00
if ( art & & art - > getId ( ) = = aid )
2014-03-17 22:51:07 +03:00
{
art = nullptr ;
found = true ;
break ;
}
}
2016-10-12 17:16:26 +02:00
if ( ! found )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Cannot find selected artifact on the list " ) ;
sendAndApply ( & saa ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
giveHeroNewArtifact ( h , VLC - > arth - > objects [ aid ] , ArtifactPosition : : FIRST_AVAILABLE ) ;
2014-03-17 22:51:07 +03:00
return true ;
}
2023-04-05 02:26:29 +02:00
bool CGameHandler : : sellArtifact ( const IMarket * m , const CGHeroInstance * h , ArtifactInstanceID aid , GameResID rid )
2014-03-17 22:51:07 +03:00
{
2016-11-27 21:50:37 +02:00
COMPLAIN_RET_FALSE_IF ( ( ! h ) , " Only hero can sell artifacts! " ) ;
2014-03-17 22:51:07 +03:00
const CArtifactInstance * art = h - > getArtByInstanceId ( aid ) ;
2016-11-27 21:50:37 +02:00
COMPLAIN_RET_FALSE_IF ( ( ! art ) , " There is no artifact to sell! " ) ;
COMPLAIN_RET_FALSE_IF ( ( ! art - > artType - > isTradable ( ) ) , " Cannot sell a war machine or spellbook! " ) ;
2014-03-17 22:51:07 +03:00
int resVal = 0 , dump = 1 ;
2023-01-02 15:58:56 +02:00
m - > getOffer ( art - > artType - > getId ( ) , rid , dump , resVal , EMarketMode : : ARTIFACT_RESOURCE ) ;
2014-03-17 22:51:07 +03:00
removeArtifact ( ArtifactLocation ( h , h - > getArtPos ( art ) ) ) ;
giveResource ( h - > tempOwner , rid , resVal ) ;
return true ;
}
2016-10-12 17:16:26 +02:00
bool CGameHandler : : buySecSkill ( const IMarket * m , const CGHeroInstance * h , SecondarySkill skill )
2014-03-17 22:51:07 +03:00
{
if ( ! h )
COMPLAIN_RET ( " You need hero to buy a skill! " ) ;
if ( h - > getSecSkillLevel ( SecondarySkill ( skill ) ) )
COMPLAIN_RET ( " Hero already know this skill " ) ;
if ( ! h - > canLearnSkill ( ) )
COMPLAIN_RET ( " Hero can't learn any more skills " ) ;
2022-11-14 19:08:49 +02:00
if ( ! h - > canLearnSkill ( skill ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " The hero can't learn this skill! " ) ;
2016-10-12 17:16:26 +02:00
if ( ! vstd : : contains ( m - > availableItemsIds ( EMarketMode : : RESOURCE_SKILL ) , skill ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " That skill is unavailable! " ) ;
2023-04-05 02:26:29 +02:00
if ( getResource ( h - > tempOwner , EGameResID : : GOLD ) < GameConstants : : SKILL_GOLD_COST ) //TODO: remove hardcoded resource\summ?
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " You can't afford to buy this skill " ) ;
2023-04-05 02:26:29 +02:00
giveResource ( h - > tempOwner , EGameResID : : GOLD , - GameConstants : : SKILL_GOLD_COST ) ;
2014-03-17 22:51:07 +03:00
changeSecSkill ( h , skill , 1 , true ) ;
return true ;
}
bool CGameHandler : : tradeResources ( const IMarket * market , ui32 val , PlayerColor player , ui32 id1 , ui32 id2 )
{
2023-03-31 13:12:38 +02:00
TResourceCap r1 = getPlayerState ( player ) - > resources [ id1 ] ;
2014-03-17 22:51:07 +03:00
vstd : : amin ( val , r1 ) ; //can't trade more resources than have
int b1 , b2 ; //base quantities for trade
market - > getOffer ( id1 , id2 , b1 , b2 , EMarketMode : : RESOURCE_RESOURCE ) ;
int units = val / b1 ; //how many base quantities we trade
2016-10-12 17:16:26 +02:00
if ( val % b1 ) //all offered units of resource should be used, if not -> somewhere in calculations must be an error
2014-03-17 22:51:07 +03:00
{
2016-11-26 14:14:43 +02:00
COMPLAIN_RET ( " Invalid deal, not all offered units of resource were used. " ) ;
2014-03-17 22:51:07 +03:00
}
2023-04-05 02:26:29 +02:00
giveResource ( player , GameResID ( id1 ) , - b1 * units ) ;
giveResource ( player , GameResID ( id2 ) , b2 * units ) ;
2014-03-17 22:51:07 +03:00
return true ;
}
2023-04-05 02:26:29 +02:00
bool CGameHandler : : sellCreatures ( ui32 count , const IMarket * market , const CGHeroInstance * hero , SlotID slot , GameResID resourceID )
2014-03-17 22:51:07 +03:00
{
2016-11-27 21:50:37 +02:00
if ( ! hero )
COMPLAIN_RET ( " Only hero can sell creatures! " ) ;
2016-10-12 17:16:26 +02:00
if ( ! vstd : : contains ( hero - > Slots ( ) , slot ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Hero doesn't have any creature in that slot! " ) ;
const CStackInstance & s = hero - > getStack ( slot ) ;
2020-10-01 10:38:06 +02:00
if ( s . count < ( TQuantity ) count //can't sell more creatures than have
2015-12-24 20:30:57 +02:00
| | ( hero - > stacksCount ( ) = = 1 & & hero - > needsLastStack ( ) & & s . count = = count ) ) //can't sell last stack
2014-03-17 22:51:07 +03:00
{
COMPLAIN_RET ( " Not enough creatures in army! " ) ;
}
int b1 , b2 ; //base quantities for trade
2023-04-05 02:26:29 +02:00
market - > getOffer ( s . type - > getId ( ) , resourceID , b1 , b2 , EMarketMode : : CREATURE_RESOURCE ) ;
2016-10-12 17:16:26 +02:00
int units = count / b1 ; //how many base quantities we trade
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( count % b1 ) //all offered units of resource should be used, if not -> somewhere in calculations must be an error
{
//TODO: complain?
assert ( 0 ) ;
}
2014-03-17 22:51:07 +03:00
2020-10-01 10:38:06 +02:00
changeStackCount ( StackLocation ( hero , slot ) , - ( TQuantity ) count ) ;
2014-03-17 22:51:07 +03:00
2016-11-26 14:14:43 +02:00
giveResource ( hero - > tempOwner , resourceID , b2 * units ) ;
2014-03-17 22:51:07 +03:00
return true ;
}
bool CGameHandler : : transformInUndead ( const IMarket * market , const CGHeroInstance * hero , SlotID slot )
{
const CArmedInstance * army = nullptr ;
if ( hero )
army = hero ;
else
2023-04-26 22:18:43 +02:00
army = dynamic_cast < const CGTownInstance * > ( market ) ;
2014-03-17 22:51:07 +03:00
if ( ! army )
COMPLAIN_RET ( " Incorrect call to transform in undead! " ) ;
2016-10-12 17:16:26 +02:00
if ( ! army - > hasStackAtSlot ( slot ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Army doesn't have any creature in that slot! " ) ;
const CStackInstance & s = army - > getStack ( slot ) ;
2015-09-04 17:08:25 +02:00
//resulting creature - bone dragons or skeletons
CreatureID resCreature = CreatureID : : SKELETON ;
2014-03-17 22:51:07 +03:00
2023-05-01 00:20:01 +02:00
if ( ( s . hasBonusOfType ( BonusType : : DRAGON_NATURE )
& & ! ( s . hasBonusOfType ( BonusType : : UNDEAD ) ) )
2015-09-04 17:08:25 +02:00
| | ( s . getCreatureID ( ) = = CreatureID : : HYDRA )
| | ( s . getCreatureID ( ) = = CreatureID : : CHAOS_HYDRA ) )
resCreature = CreatureID : : BONE_DRAGON ;
changeStackType ( StackLocation ( army , slot ) , resCreature . toCreature ( ) ) ;
2014-03-17 22:51:07 +03:00
return true ;
}
2023-04-05 02:26:29 +02:00
bool CGameHandler : : sendResources ( ui32 val , PlayerColor player , GameResID r1 , PlayerColor r2 )
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const PlayerState * p2 = getPlayerState ( r2 , false ) ;
2016-10-12 17:16:26 +02:00
if ( ! p2 | | p2 - > status ! = EPlayerStatus : : INGAME )
2014-03-17 22:51:07 +03:00
{
complain ( " Dest player must be in game! " ) ;
return false ;
}
2023-03-31 13:12:38 +02:00
TResourceCap curRes1 = getPlayerState ( player ) - > resources [ r1 ] ;
2014-03-17 22:51:07 +03:00
2016-11-26 14:14:43 +02:00
vstd : : amin ( val , curRes1 ) ;
2014-03-17 22:51:07 +03:00
2020-10-01 10:38:06 +02:00
giveResource ( player , r1 , - ( int ) val ) ;
2016-11-26 14:14:43 +02:00
giveResource ( r2 , r1 , val ) ;
2014-03-17 22:51:07 +03:00
return true ;
}
2016-09-08 18:29:15 +02:00
bool CGameHandler : : setFormation ( ObjectInstanceID hid , ui8 formation )
2014-03-17 22:51:07 +03:00
{
2016-09-08 18:29:15 +02:00
const CGHeroInstance * h = getHero ( hid ) ;
2016-10-12 17:16:26 +02:00
if ( ! h )
2016-09-08 18:29:15 +02:00
{
logGlobal - > error ( " Hero doesn't exist! " ) ;
return false ;
}
ChangeFormation cf ;
cf . hid = hid ;
cf . formation = formation ;
sendAndApply ( & cf ) ;
2014-03-17 22:51:07 +03:00
return true ;
}
bool CGameHandler : : hireHero ( const CGObjectInstance * obj , ui8 hid , PlayerColor player )
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const PlayerState * p = getPlayerState ( player ) ;
2016-09-18 10:53:51 +02:00
const CGTownInstance * t = getTown ( obj - > id ) ;
2014-03-17 22:51:07 +03:00
//common preconditions
2023-04-05 02:26:29 +02:00
// if ((p->resources.at(EGameResID::GOLD)<GOLD_NEEDED && complain("Not enough gold for buying hero!"))
2014-04-27 09:43:42 +03:00
// || (getHeroCount(player, false) >= GameConstants::MAX_HEROES_PER_PLAYER && complain("Cannot hire hero, only 8 wandering heroes are allowed!")))
2023-04-05 02:26:29 +02:00
if ( ( p - > resources [ EGameResID : : GOLD ] < GameConstants : : HERO_GOLD_COST & & complain ( " Not enough gold for buying hero! " ) )
2023-03-15 23:47:26 +02:00
| | ( ( getHeroCount ( player , false ) > = VLC - > settings ( ) - > getInteger ( EGameSettings : : HEROES_PER_PLAYER_ON_MAP_CAP ) & & complain ( " Cannot hire hero, too many wandering heroes already! " ) ) )
| | ( ( getHeroCount ( player , true ) > = VLC - > settings ( ) - > getInteger ( EGameSettings : : HEROES_PER_PLAYER_TOTAL_CAP ) & & complain ( " Cannot hire hero, too many heroes garrizoned and wandering already! " ) ) ) )
2016-01-26 21:24:38 +02:00
{
return false ;
}
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( t ) //tavern in town
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ( ! t - > hasBuilt ( BuildingID : : TAVERN ) & & complain ( " No tavern! " ) )
2016-01-26 21:24:38 +02:00
| | ( t - > visitingHero & & complain ( " There is visiting hero - no place! " ) ) )
{
2014-03-17 22:51:07 +03:00
return false ;
2016-01-26 21:24:38 +02:00
}
2014-03-17 22:51:07 +03:00
}
2016-10-12 17:16:26 +02:00
else if ( obj - > ID = = Obj : : TAVERN )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( getTile ( obj - > visitablePos ( ) ) - > visitableObjects . back ( ) ! = obj & & complain ( " Tavern entry must be unoccupied! " ) )
2016-01-26 21:24:38 +02:00
{
2014-03-17 22:51:07 +03:00
return false ;
2016-01-26 21:24:38 +02:00
}
2014-03-17 22:51:07 +03:00
}
const CGHeroInstance * nh = p - > availableHeroes . at ( hid ) ;
if ( ! nh )
{
complain ( " Hero is not available for hiring! " ) ;
return false ;
}
HeroRecruited hr ;
hr . tid = obj - > id ;
hr . hid = nh - > subID ;
hr . player = player ;
2022-12-09 14:42:47 +02:00
hr . tile = nh - > convertFromVisitablePos ( obj - > visitablePos ( ) ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & hr ) ;
std : : map < ui32 , ConstTransitivePtr < CGHeroInstance > > pool = gs - > unusedHeroesFromPool ( ) ;
const CGHeroInstance * theOtherHero = p - > availableHeroes . at ( ! hid ) ;
const CGHeroInstance * newHero = nullptr ;
if ( theOtherHero ) //on XXL maps all heroes can be imprisoned :(
2016-01-26 21:24:38 +02:00
{
2016-09-07 23:54:26 +02:00
newHero = gs - > hpool . pickHeroFor ( false , player , getNativeTown ( player ) , pool , getRandomGenerator ( ) , theOtherHero - > type - > heroClass ) ;
2016-01-26 21:24:38 +02:00
}
2014-03-17 22:51:07 +03:00
SetAvailableHeroes sah ;
sah . player = player ;
2016-10-12 17:16:26 +02:00
if ( newHero )
2014-03-17 22:51:07 +03:00
{
sah . hid [ hid ] = newHero - > subID ;
sah . army [ hid ] . clear ( ) ;
sah . army [ hid ] . setCreature ( SlotID ( 0 ) , newHero - > type - > initialArmy [ 0 ] . creature , 1 ) ;
}
else
2016-01-26 21:24:38 +02:00
{
2014-03-17 22:51:07 +03:00
sah . hid [ hid ] = - 1 ;
2016-01-26 21:24:38 +02:00
}
2014-03-17 22:51:07 +03:00
sah . hid [ ! hid ] = theOtherHero ? theOtherHero - > subID : - 1 ;
sendAndApply ( & sah ) ;
2023-04-05 02:26:29 +02:00
giveResource ( player , EGameResID : : GOLD , - GameConstants : : HERO_GOLD_COST ) ;
2014-03-17 22:51:07 +03:00
2023-05-02 02:44:09 +02:00
if ( t )
2014-03-17 22:51:07 +03:00
{
2023-05-02 02:44:09 +02:00
objectVisited ( t , nh ) ;
2014-03-17 22:51:07 +03:00
}
return true ;
}
2017-06-06 06:53:51 +02:00
bool CGameHandler : : queryReply ( QueryID qid , const JsonNode & answer , PlayerColor player )
2014-03-17 22:51:07 +03:00
{
boost : : unique_lock < boost : : recursive_mutex > lock ( gsm ) ;
2017-06-06 06:53:51 +02:00
logGlobal - > trace ( " Player %s attempts answering query %d with answer: " , player , qid ) ;
2017-08-11 15:27:42 +02:00
logGlobal - > trace ( answer . toJson ( ) ) ;
2014-03-17 22:51:07 +03:00
auto topQuery = queries . topQuery ( player ) ;
2020-10-19 21:39:57 +02:00
2014-03-17 22:51:07 +03:00
COMPLAIN_RET_FALSE_IF ( ! topQuery , " This player doesn't have any queries! " ) ;
2020-10-19 21:39:57 +02:00
if ( topQuery - > queryID ! = qid )
{
auto currentQuery = queries . getQuery ( qid ) ;
if ( currentQuery ! = nullptr & & currentQuery - > endsByPlayerAnswer ( ) )
currentQuery - > setReply ( answer ) ;
COMPLAIN_RET ( " This player top query has different ID! " ) ; //topQuery->queryID != qid
}
2014-03-17 22:51:07 +03:00
COMPLAIN_RET_FALSE_IF ( ! topQuery - > endsByPlayerAnswer ( ) , " This query cannot be ended by player's answer! " ) ;
2017-06-06 06:53:51 +02:00
topQuery - > setReply ( answer ) ;
2014-03-17 22:51:07 +03:00
queries . popQuery ( topQuery ) ;
return true ;
}
static EndAction end_action ;
2016-02-13 16:40:31 +02:00
void CGameHandler : : updateGateState ( )
2016-01-29 21:43:35 +02:00
{
2023-01-13 15:44:42 +02:00
// GATE_BRIDGE - leftmost tile, located over moat
// GATE_OUTER - central tile, mostly covered by gate image
// GATE_INNER - rightmost tile, inside the walls
// GATE_OUTER or GATE_INNER:
// - if defender moves unit on these tiles, bridge will open
// - if there is a creature (dead or alive) on these tiles, bridge will always remain open
// - blocked to attacker if bridge is closed
// GATE_BRIDGE
// - if there is a unit or corpse here, bridge can't open (and can't close in fortress)
// - if Force Field is cast here, bridge can't open (but can close, in any town)
// - deals moat damage to attacker if bridge is closed (fortress only)
bool hasForceFieldOnBridge = ! battleGetAllObstaclesOnPos ( BattleHex ( ESiegeHex : : GATE_BRIDGE ) , true ) . empty ( ) ;
2023-04-12 00:52:12 +02:00
bool hasStackAtGateInner = gs - > curB - > battleGetUnitByPos ( BattleHex ( ESiegeHex : : GATE_INNER ) , false ) ! = nullptr ;
bool hasStackAtGateOuter = gs - > curB - > battleGetUnitByPos ( BattleHex ( ESiegeHex : : GATE_OUTER ) , false ) ! = nullptr ;
bool hasStackAtGateBridge = gs - > curB - > battleGetUnitByPos ( BattleHex ( ESiegeHex : : GATE_BRIDGE ) , false ) ! = nullptr ;
bool hasWideMoat = vstd : : contains_if ( battleGetAllObstaclesOnPos ( BattleHex ( ESiegeHex : : GATE_BRIDGE ) , false ) , [ ] ( const std : : shared_ptr < const CObstacleInstance > & obst )
2023-03-23 00:16:11 +02:00
{
return obst - > obstacleType = = CObstacleInstance : : MOAT ;
} ) ;
2023-01-13 15:44:42 +02:00
2016-02-13 16:40:31 +02:00
BattleUpdateGateState db ;
db . state = gs - > curB - > si . gateState ;
2016-10-12 17:16:26 +02:00
if ( gs - > curB - > si . wallState [ EWallPart : : GATE ] = = EWallState : : DESTROYED )
2016-01-29 21:43:35 +02:00
{
2016-02-13 16:40:31 +02:00
db . state = EGateState : : DESTROYED ;
2016-01-29 21:43:35 +02:00
}
2016-10-12 17:16:26 +02:00
else if ( db . state = = EGateState : : OPENED )
2016-01-29 21:43:35 +02:00
{
2023-03-23 00:16:11 +02:00
bool hasStackOnLongBridge = hasStackAtGateBridge & & hasWideMoat ;
2023-01-13 15:44:42 +02:00
bool gateCanClose = ! hasStackAtGateInner & & ! hasStackAtGateOuter & & ! hasStackOnLongBridge ;
if ( gateCanClose )
db . state = EGateState : : CLOSED ;
else
db . state = EGateState : : OPENED ;
}
else // CLOSED or BLOCKED
{
bool gateBlocked = hasForceFieldOnBridge | | hasStackAtGateBridge ;
if ( gateBlocked )
db . state = EGateState : : BLOCKED ;
else
db . state = EGateState : : CLOSED ;
2016-01-29 21:43:35 +02:00
}
2016-02-08 11:15:07 +02:00
2016-10-12 17:16:26 +02:00
if ( db . state ! = gs - > curB - > si . gateState )
2016-02-08 11:15:07 +02:00
sendAndApply ( & db ) ;
2016-01-29 21:43:35 +02:00
}
2016-10-12 17:16:26 +02:00
bool CGameHandler : : makeBattleAction ( BattleAction & ba )
2014-03-17 22:51:07 +03:00
{
bool ok = true ;
2017-07-20 06:08:49 +02:00
battle : : Target target = ba . getTarget ( gs - > curB ) ;
const CStack * stack = battleGetStackByID ( ba . stackNumber ) ; //may be nullptr if action is not about stack
2014-03-17 22:51:07 +03:00
2017-07-20 06:08:49 +02:00
const bool isAboutActiveStack = stack & & ( ba . stackNumber = = gs - > curB - > getActiveStackID ( ) ) ;
logGlobal - > trace ( " Making action: %s " , ba . toString ( ) ) ;
2014-03-17 22:51:07 +03:00
switch ( ba . actionType )
{
2017-07-20 06:08:49 +02:00
case EActionType : : WALK : //walk
case EActionType : : DEFEND : //defend
case EActionType : : WAIT : //wait
case EActionType : : WALK_AND_ATTACK : //walk or attack
case EActionType : : SHOOT : //shoot
case EActionType : : CATAPULT : //catapult
case EActionType : : STACK_HEAL : //healing with First Aid Tent
case EActionType : : MONSTER_SPELL :
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( ! stack )
2014-03-17 22:51:07 +03:00
{
complain ( " No such stack! " ) ;
return false ;
}
2016-10-12 17:16:26 +02:00
if ( ! stack - > alive ( ) )
2014-03-17 22:51:07 +03:00
{
complain ( " This stack is dead: " + stack - > nodeName ( ) ) ;
return false ;
}
2016-10-12 17:16:26 +02:00
if ( battleTacticDist ( ) )
2014-03-17 22:51:07 +03:00
{
2023-04-27 19:43:20 +02:00
if ( stack & & stack - > unitSide ( ) ! = battleGetTacticsSide ( ) )
2014-03-17 22:51:07 +03:00
{
complain ( " This is not a stack of side that has tactics! " ) ;
return false ;
}
}
2016-10-12 17:16:26 +02:00
else if ( ! isAboutActiveStack )
2014-03-17 22:51:07 +03:00
{
complain ( " Action has to be about active stack! " ) ;
return false ;
}
}
2016-11-26 18:46:34 +02:00
auto wrapAction = [ this ] ( BattleAction & ba )
{
StartAction startAction ( ba ) ;
sendAndApply ( & startAction ) ;
2017-07-19 01:06:05 +02:00
return vstd : : makeScopeGuard ( [ & ] ( )
2016-11-26 18:46:34 +02:00
{
sendAndApply ( & end_action ) ;
} ) ;
} ;
2014-03-17 22:51:07 +03:00
switch ( ba . actionType )
{
2017-07-20 06:08:49 +02:00
case EActionType : : END_TACTIC_PHASE : //wait
case EActionType : : BAD_MORALE :
case EActionType : : NO_ACTION :
2014-03-17 22:51:07 +03:00
{
2016-11-26 18:46:34 +02:00
auto wrapper = wrapAction ( ba ) ;
2014-03-17 22:51:07 +03:00
break ;
}
2017-07-20 06:08:49 +02:00
case EActionType : : WALK :
2014-03-17 22:51:07 +03:00
{
2016-11-26 18:46:34 +02:00
auto wrapper = wrapAction ( ba ) ;
2017-07-20 06:08:49 +02:00
if ( target . size ( ) < 1 )
{
complain ( " Destination required for move action. " ) ;
ok = false ;
break ;
}
int walkedTiles = moveStack ( ba . stackNumber , target . at ( 0 ) . hexValue ) ; //move
2016-10-12 17:16:26 +02:00
if ( ! walkedTiles )
2014-03-17 22:51:07 +03:00
complain ( " Stack failed movement! " ) ;
break ;
}
2017-07-20 06:08:49 +02:00
case EActionType : : DEFEND :
2014-03-17 22:51:07 +03:00
{
2019-03-25 01:16:47 +02:00
//defensive stance, TODO: filter out spell boosts from bonus (stone skin etc.)
2014-03-17 22:51:07 +03:00
SetStackEffect sse ;
2023-05-01 00:20:01 +02:00
Bonus defenseBonusToAdd ( BonusDuration : : STACK_GETS_TURN , BonusType : : PRIMARY_SKILL , BonusSource : : OTHER , 20 , - 1 , PrimarySkill : : DEFENSE , BonusValueType : : PERCENT_TO_ALL ) ;
Bonus bonus2 ( BonusDuration : : STACK_GETS_TURN , BonusType : : PRIMARY_SKILL , BonusSource : : OTHER , stack - > valOfBonuses ( BonusType : : DEFENSIVE_STANCE ) ,
- 1 , PrimarySkill : : DEFENSE , BonusValueType : : ADDITIVE_VALUE ) ;
Bonus alternativeWeakCreatureBonus ( BonusDuration : : STACK_GETS_TURN , BonusType : : PRIMARY_SKILL , BonusSource : : OTHER , 1 , - 1 , PrimarySkill : : DEFENSE , BonusValueType : : ADDITIVE_VALUE ) ;
2019-03-25 01:16:47 +02:00
2023-05-01 00:20:01 +02:00
BonusList defence = * stack - > getBonuses ( Selector : : typeSubtype ( BonusType : : PRIMARY_SKILL , PrimarySkill : : DEFENSE ) ) ;
2017-07-10 07:53:29 +02:00
int oldDefenceValue = defence . totalValue ( ) ;
2017-07-10 03:05:36 +02:00
2019-03-25 01:16:47 +02:00
defence . push_back ( std : : make_shared < Bonus > ( defenseBonusToAdd ) ) ;
2017-07-10 07:53:29 +02:00
defence . push_back ( std : : make_shared < Bonus > ( bonus2 ) ) ;
2017-07-10 03:05:36 +02:00
2017-07-10 07:53:29 +02:00
int difference = defence . totalValue ( ) - oldDefenceValue ;
2019-03-25 01:16:47 +02:00
std : : vector < Bonus > buffer ;
if ( difference = = 0 ) //give replacement bonus for creatures not reaching 5 defense points (20% of def becomes 0)
{
difference = 1 ;
buffer . push_back ( alternativeWeakCreatureBonus ) ;
}
else
{
buffer . push_back ( defenseBonusToAdd ) ;
}
buffer . push_back ( bonus2 ) ;
2017-07-10 03:05:36 +02:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
sse . toUpdate . push_back ( std : : make_pair ( ba . stackNumber , buffer ) ) ;
sendAndApply ( & sse ) ;
BattleLogMessage message ;
2017-07-10 03:05:36 +02:00
MetaString text ;
stack - > addText ( text , MetaString : : GENERAL_TXT , 120 ) ;
stack - > addNameReplacement ( text ) ;
text . addReplacement ( difference ) ;
2014-03-17 22:51:07 +03:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
message . lines . push_back ( text ) ;
sendAndApply ( & message ) ;
2014-03-17 22:51:07 +03:00
//don't break - we share code with next case
}
2023-04-10 16:44:41 +02:00
[[fallthrough]] ;
2017-07-20 06:08:49 +02:00
case EActionType : : WAIT :
2014-03-17 22:51:07 +03:00
{
2016-11-26 18:46:34 +02:00
auto wrapper = wrapAction ( ba ) ;
2014-03-17 22:51:07 +03:00
break ;
}
2017-07-20 06:08:49 +02:00
case EActionType : : RETREAT : //retreat/flee
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ! gs - > curB - > battleCanFlee ( gs - > curB - > sides . at ( ba . side ) . color ) )
2014-03-17 22:51:07 +03:00
complain ( " Cannot retreat! " ) ;
else
setBattleResult ( BattleResult : : ESCAPE , ! ba . side ) ; //surrendering side loses
break ;
}
2017-07-20 06:08:49 +02:00
case EActionType : : SURRENDER :
2014-03-17 22:51:07 +03:00
{
PlayerColor player = gs - > curB - > sides . at ( ba . side ) . color ;
int cost = gs - > curB - > battleGetSurrenderCost ( player ) ;
2016-10-12 17:16:26 +02:00
if ( cost < 0 )
2014-03-17 22:51:07 +03:00
complain ( " Cannot surrender! " ) ;
2023-04-05 02:26:29 +02:00
else if ( getResource ( player , EGameResID : : GOLD ) < cost )
2014-03-17 22:51:07 +03:00
complain ( " Not enough gold to surrender! " ) ;
else
{
2023-04-05 02:26:29 +02:00
giveResource ( player , EGameResID : : GOLD , - cost ) ;
2014-03-17 22:51:07 +03:00
setBattleResult ( BattleResult : : SURRENDER , ! ba . side ) ; //surrendering side loses
}
break ;
}
2017-07-20 06:08:49 +02:00
case EActionType : : WALK_AND_ATTACK : //walk or attack
2014-03-17 22:51:07 +03:00
{
2016-11-26 18:46:34 +02:00
auto wrapper = wrapAction ( ba ) ;
2017-07-20 06:08:49 +02:00
if ( ! stack )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
complain ( " No attacker " ) ;
ok = false ;
break ;
}
if ( target . size ( ) < 2 )
{
complain ( " Two destinations required for attack action. " ) ;
ok = false ;
2014-03-17 22:51:07 +03:00
break ;
}
2017-07-20 06:08:49 +02:00
BattleHex attackPos = target . at ( 0 ) . hexValue ;
BattleHex destinationTile = target . at ( 1 ) . hexValue ;
const CStack * destinationStack = gs - > curB - > battleGetStackByPos ( destinationTile , true ) ;
if ( ! destinationStack )
{
complain ( " Invalid target to attack " ) ;
ok = false ;
break ;
}
BattleHex startingPos = stack - > getPosition ( ) ;
int distance = moveStack ( ba . stackNumber , attackPos ) ;
2014-03-17 22:51:07 +03:00
2016-08-30 00:11:54 +02:00
logGlobal - > trace ( " %s will attack %s " , stack - > nodeName ( ) , destinationStack - > nodeName ( ) ) ;
2014-03-17 22:51:07 +03:00
2022-12-17 14:40:54 +02:00
if ( stack - > getPosition ( ) ! = attackPos
& & ! ( stack - > doubleWide ( ) & & ( stack - > getPosition ( ) = = attackPos . cloneInDirection ( stack - > destShiftDir ( ) , false ) ) )
2014-03-17 22:51:07 +03:00
)
{
2022-12-17 14:40:54 +02:00
// we were not able to reach destination tile, nor occupy specified hex
// abort attack attempt, but treat this case as legal - we may have stepped onto a quicksands/mine
2014-03-17 22:51:07 +03:00
break ;
}
2023-04-27 19:43:20 +02:00
if ( destinationStack & & stack - > unitId ( ) = = destinationStack - > unitId ( ) ) //we should just move, it will be handled by following check
2014-03-17 22:51:07 +03:00
{
destinationStack = nullptr ;
}
2017-07-20 06:08:49 +02:00
if ( ! destinationStack )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
complain ( " Unit can not attack itself " ) ;
2014-03-17 22:51:07 +03:00
ok = false ;
break ;
}
2017-07-20 06:08:49 +02:00
if ( ! CStack : : isMeleeAttackPossible ( stack , destinationStack ) )
2014-03-17 22:51:07 +03:00
{
complain ( " Attack cannot be performed! " ) ;
ok = false ;
break ;
}
//attack
2017-07-20 06:08:49 +02:00
int totalAttacks = stack - > totalAttacks . getMeleeValue ( ) ;
2014-03-17 22:51:07 +03:00
2023-02-26 00:19:39 +02:00
//TODO: move to CUnitState
const auto * attackingHero = gs - > curB - > battleGetFightingHero ( ba . side ) ;
if ( attackingHero )
{
2023-05-01 00:20:01 +02:00
totalAttacks + = attackingHero - > valOfBonuses ( BonusType : : HERO_GRANTS_ATTACKS , stack - > creatureIndex ( ) ) ;
2023-02-26 00:19:39 +02:00
}
2023-05-01 00:20:01 +02:00
const bool firstStrike = destinationStack - > hasBonusOfType ( BonusType : : FIRST_STRIKE ) ;
2018-07-18 15:35:31 +02:00
const bool retaliation = destinationStack - > ableToRetaliate ( ) ;
2014-03-17 22:51:07 +03:00
for ( int i = 0 ; i < totalAttacks ; + + i )
{
2017-08-19 20:39:24 +02:00
//first strike
2018-02-09 23:02:56 +02:00
if ( i = = 0 & & firstStrike & & retaliation )
2017-08-19 20:39:24 +02:00
{
2017-07-20 06:08:49 +02:00
makeAttack ( destinationStack , stack , 0 , stack - > getPosition ( ) , true , false , true ) ;
2017-08-19 20:39:24 +02:00
}
2022-12-16 21:52:46 +02:00
//move can cause death, eg. by walking into the moat, first strike can cause death or paralysis/petrification
2023-05-01 00:20:01 +02:00
if ( stack - > alive ( ) & & ! stack - > hasBonusOfType ( BonusType : : NOT_ACTIVE ) & & destinationStack - > alive ( ) )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
makeAttack ( stack , destinationStack , ( i ? 0 : distance ) , destinationTile , i = = 0 , false , false ) ; //no distance travelled on second attack
2014-03-17 22:51:07 +03:00
}
//counterattack
2018-02-09 23:02:56 +02:00
//we check retaliation twice, so if it unblocked during attack it will work only on next attack
2017-07-20 06:08:49 +02:00
if ( stack - > alive ( )
2023-05-01 00:20:01 +02:00
& & ! stack - > hasBonusOfType ( BonusType : : BLOCKS_RETALIATION )
2018-07-18 15:35:31 +02:00
& & ( i = = 0 & & ! firstStrike )
2018-02-09 23:02:56 +02:00
& & retaliation & & destinationStack - > ableToRetaliate ( ) )
2014-03-17 22:51:07 +03:00
{
2018-07-18 15:35:31 +02:00
makeAttack ( destinationStack , stack , 0 , stack - > getPosition ( ) , true , false , true ) ;
2014-03-17 22:51:07 +03:00
}
}
//return
2023-05-01 00:20:01 +02:00
if ( stack - > hasBonusOfType ( BonusType : : RETURN_AFTER_STRIKE )
2017-07-20 06:08:49 +02:00
& & target . size ( ) = = 3
& & startingPos ! = stack - > getPosition ( )
& & startingPos = = target . at ( 2 ) . hexValue
& & stack - > alive ( ) )
2014-03-17 22:51:07 +03:00
{
moveStack ( ba . stackNumber , startingPos ) ;
2023-04-27 19:43:20 +02:00
//NOTE: curStack->unitId() == ba.stackNumber (rev 1431)
2014-03-17 22:51:07 +03:00
}
break ;
}
2017-07-20 06:08:49 +02:00
case EActionType : : SHOOT :
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
if ( target . size ( ) < 1 )
{
complain ( " Destination required for shot action. " ) ;
ok = false ;
break ;
}
auto destination = target . at ( 0 ) . hexValue ;
const CStack * destinationStack = gs - > curB - > battleGetStackByPos ( destination ) ;
if ( ! gs - > curB - > battleCanShoot ( stack , destination ) )
2014-03-17 22:51:07 +03:00
{
complain ( " Cannot shoot! " ) ;
break ;
}
2017-05-28 14:00:55 +02:00
if ( ! destinationStack )
{
complain ( " No target to shoot! " ) ;
break ;
}
2014-03-17 22:51:07 +03:00
2016-11-26 18:46:34 +02:00
auto wrapper = wrapAction ( ba ) ;
2014-03-17 22:51:07 +03:00
2017-07-20 06:08:49 +02:00
makeAttack ( stack , destinationStack , 0 , destination , true , true , false ) ;
2014-03-17 22:51:07 +03:00
2017-02-04 11:33:45 +02:00
//ranged counterattack
2023-05-01 00:20:01 +02:00
if ( destinationStack - > hasBonusOfType ( BonusType : : RANGED_RETALIATION )
& & ! stack - > hasBonusOfType ( BonusType : : BLOCKS_RANGED_RETALIATION )
2017-02-04 11:33:45 +02:00
& & destinationStack - > ableToRetaliate ( )
2017-07-20 06:08:49 +02:00
& & gs - > curB - > battleCanShoot ( destinationStack , stack - > getPosition ( ) )
2017-02-04 11:33:45 +02:00
& & stack - > alive ( ) ) //attacker may have died (fire shield)
{
2017-07-20 06:08:49 +02:00
makeAttack ( destinationStack , stack , 0 , stack - > getPosition ( ) , true , true , true ) ;
2017-02-04 11:33:45 +02:00
}
2023-02-26 00:19:39 +02:00
//allow more than one additional attack
int totalRangedAttacks = stack - > totalAttacks . getRangedValue ( ) ;
2017-02-04 11:33:45 +02:00
2017-07-20 06:08:49 +02:00
//TODO: move to CUnitState
2023-02-26 00:19:39 +02:00
const auto * attackingHero = gs - > curB - > battleGetFightingHero ( ba . side ) ;
if ( attackingHero )
2014-03-17 22:51:07 +03:00
{
2023-05-01 00:20:01 +02:00
totalRangedAttacks + = attackingHero - > valOfBonuses ( BonusType : : HERO_GRANTS_ATTACKS , stack - > creatureIndex ( ) ) ;
2014-03-17 22:51:07 +03:00
}
2017-07-20 06:08:49 +02:00
for ( int i = 1 ; i < totalRangedAttacks ; + + i )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
if (
2014-03-17 22:51:07 +03:00
stack - > alive ( )
& & destinationStack - > alive ( )
2017-07-04 13:24:46 +02:00
& & stack - > shots . canUse ( )
2014-03-17 22:51:07 +03:00
)
{
2017-07-20 06:08:49 +02:00
makeAttack ( stack , destinationStack , 0 , destination , false , true , false ) ;
2014-03-17 22:51:07 +03:00
}
}
break ;
}
2017-07-20 06:08:49 +02:00
case EActionType : : CATAPULT :
2014-03-17 22:51:07 +03:00
{
2023-01-12 23:53:29 +02:00
auto wrapper = wrapAction ( ba ) ;
2023-02-21 01:47:40 +02:00
const CStack * shooter = gs - > curB - > battleGetStackByID ( ba . stackNumber ) ;
2023-05-01 00:20:01 +02:00
std : : shared_ptr < const Bonus > catapultAbility = stack - > getBonusLocalFirst ( Selector : : type ( ) ( BonusType : : CATAPULT ) ) ;
2023-02-21 01:47:40 +02:00
if ( ! catapultAbility | | catapultAbility - > subtype < 0 )
2014-03-17 22:51:07 +03:00
{
2023-02-21 01:47:40 +02:00
complain ( " We do not know how to shoot :P " ) ;
}
else
{
const CSpell * spell = SpellID ( catapultAbility - > subtype ) . toSpell ( ) ;
spells : : BattleCast parameters ( gs - > curB , shooter , spells : : Mode : : SPELL_LIKE_ATTACK , spell ) ; //We can shot infinitely by catapult
2023-05-01 00:20:01 +02:00
auto shotLevel = stack - > valOfBonuses ( Selector : : typeSubtype ( BonusType : : CATAPULT_EXTRA_SHOTS , catapultAbility - > subtype ) ) ;
2023-02-21 01:47:40 +02:00
parameters . setSpellLevel ( shotLevel ) ;
parameters . cast ( spellEnv , target ) ;
2014-03-17 22:51:07 +03:00
}
//finish by scope guard
break ;
}
2017-07-20 06:08:49 +02:00
case EActionType : : STACK_HEAL : //healing with First Aid Tent
2014-03-17 22:51:07 +03:00
{
2016-11-26 18:46:34 +02:00
auto wrapper = wrapAction ( ba ) ;
2017-07-20 06:08:49 +02:00
const CStack * healer = gs - > curB - > battleGetStackByID ( ba . stackNumber ) ;
if ( target . size ( ) < 1 )
{
complain ( " Destination required for heal action. " ) ;
ok = false ;
break ;
}
2014-03-17 22:51:07 +03:00
2017-07-20 06:08:49 +02:00
const battle : : Unit * destStack = nullptr ;
2023-05-01 00:20:01 +02:00
std : : shared_ptr < const Bonus > healerAbility = stack - > getBonusLocalFirst ( Selector : : type ( ) ( BonusType : : HEALER ) ) ;
2017-07-20 06:08:49 +02:00
if ( target . at ( 0 ) . unitValue )
destStack = target . at ( 0 ) . unitValue ;
else
2023-04-01 01:58:40 +02:00
destStack = gs - > curB - > battleGetUnitByPos ( target . at ( 0 ) . hexValue ) ;
2015-09-16 09:08:40 +02:00
2023-02-21 20:46:08 +02:00
if ( healer = = nullptr | | destStack = = nullptr | | ! healerAbility | | healerAbility - > subtype < 0 )
2014-03-17 22:51:07 +03:00
{
complain ( " There is either no healer, no destination, or healer cannot heal :P " ) ;
}
2015-09-16 09:08:40 +02:00
else
{
2023-02-21 20:46:08 +02:00
const CSpell * spell = SpellID ( healerAbility - > subtype ) . toSpell ( ) ;
spells : : BattleCast parameters ( gs - > curB , healer , spells : : Mode : : SPELL_LIKE_ATTACK , spell ) ; //We can heal infinitely by first aid tent
2023-04-05 23:21:21 +02:00
auto dest = battle : : Destination ( destStack , target . at ( 0 ) . hexValue ) ;
2023-02-21 20:46:08 +02:00
parameters . setSpellLevel ( 0 ) ;
2023-04-05 23:21:21 +02:00
parameters . cast ( spellEnv , { dest } ) ;
2014-03-17 22:51:07 +03:00
}
break ;
}
2017-07-20 06:08:49 +02:00
case EActionType : : MONSTER_SPELL :
2014-03-17 22:51:07 +03:00
{
2016-11-26 18:46:34 +02:00
auto wrapper = wrapAction ( ba ) ;
2014-03-17 22:51:07 +03:00
const CStack * stack = gs - > curB - > battleGetStackByID ( ba . stackNumber ) ;
2017-07-20 06:08:49 +02:00
SpellID spellID = SpellID ( ba . actionSubtype ) ;
2014-03-17 22:51:07 +03:00
2023-05-01 00:20:01 +02:00
std : : shared_ptr < const Bonus > randSpellcaster = stack - > getBonus ( Selector : : type ( ) ( BonusType : : RANDOM_SPELLCASTER ) ) ;
std : : shared_ptr < const Bonus > spellcaster = stack - > getBonus ( Selector : : typeSubtype ( BonusType : : SPELLCASTER , spellID ) ) ;
2014-03-17 22:51:07 +03:00
//TODO special bonus for genies ability
2016-10-12 17:16:26 +02:00
if ( randSpellcaster & & battleGetRandomStackSpell ( getRandomGenerator ( ) , stack , CBattleInfoCallback : : RANDOM_AIMED ) < 0 )
2016-09-09 19:30:36 +02:00
spellID = battleGetRandomStackSpell ( getRandomGenerator ( ) , stack , CBattleInfoCallback : : RANDOM_GENIE ) ;
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( spellID < 0 )
2014-03-17 22:51:07 +03:00
complain ( " That stack can't cast spells! " ) ;
else
{
2015-10-28 22:53:44 +02:00
const CSpell * spell = SpellID ( spellID ) . toSpell ( ) ;
2017-07-20 06:08:49 +02:00
spells : : BattleCast parameters ( gs - > curB , stack , spells : : Mode : : CREATURE_ACTIVE , spell ) ;
int32_t spellLvl = 0 ;
if ( spellcaster )
vstd : : amax ( spellLvl , spellcaster - > val ) ;
if ( randSpellcaster )
vstd : : amax ( spellLvl , randSpellcaster - > val ) ;
parameters . setSpellLevel ( spellLvl ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
parameters . cast ( spellEnv , target ) ;
2014-03-17 22:51:07 +03:00
}
break ;
}
}
2022-12-22 23:11:55 +02:00
if ( ba . actionType = = EActionType : : WAIT | | ba . actionType = = EActionType : : DEFEND
2017-07-20 06:08:49 +02:00
| | ba . actionType = = EActionType : : SHOOT | | ba . actionType = = EActionType : : MONSTER_SPELL )
2023-04-12 15:20:40 +02:00
handleObstacleTriggersForUnit ( * spellEnv , * stack ) ;
2017-07-09 10:18:46 +02:00
if ( ba . stackNumber = = gs - > curB - > activeStack | | battleResult . get ( ) ) //active stack has moved or battle has finished
2014-03-17 22:51:07 +03:00
battleMadeAction . setn ( true ) ;
return ok ;
}
2016-10-02 15:21:46 +02:00
void CGameHandler : : playerMessage ( PlayerColor player , const std : : string & message , ObjectInstanceID currObj )
2014-09-21 16:42:08 +03:00
{
2022-09-30 21:08:37 +02:00
bool cheated = false ;
2023-03-31 22:10:48 +02:00
if ( ! getPlayerSettings ( player ) - > isControlledByAI ( ) )
{
PlayerMessageClient temp_message ( player , message ) ;
sendAndApply ( & temp_message ) ;
}
2014-03-17 22:51:07 +03:00
2022-10-08 17:52:29 +02:00
std : : vector < std : : string > words ;
boost : : split ( words , message , boost : : is_any_of ( " " ) ) ;
2022-10-06 13:54:46 +02:00
bool isHost = false ;
for ( auto & c : connections [ player ] )
if ( lobby - > isClientHost ( c - > connectionID ) )
isHost = true ;
2022-10-08 17:52:29 +02:00
if ( isHost & & words . size ( ) > = 2 & & words [ 0 ] = = " game " )
2022-10-06 13:54:46 +02:00
{
2022-10-08 17:52:29 +02:00
if ( words [ 1 ] = = " exit " | | words [ 1 ] = = " quit " | | words [ 1 ] = = " end " )
2022-10-06 13:54:46 +02:00
{
SystemMessage temp_message ( " game was terminated " ) ;
sendAndApply ( & temp_message ) ;
lobby - > state = EServerState : : SHUTDOWN ;
return ;
}
2022-10-08 17:52:29 +02:00
if ( words . size ( ) = = 3 & & words [ 1 ] = = " save " )
2022-10-06 13:54:46 +02:00
{
2022-10-08 17:52:29 +02:00
save ( " Saves/ " + words [ 2 ] ) ;
SystemMessage temp_message ( " game saved as " + words [ 2 ] ) ;
2022-10-06 13:54:46 +02:00
sendAndApply ( & temp_message ) ;
return ;
}
2022-10-08 17:52:29 +02:00
if ( words . size ( ) = = 3 & & words [ 1 ] = = " kick " )
2022-10-06 13:54:46 +02:00
{
2022-10-08 17:52:29 +02:00
auto playername = words [ 2 ] ;
2022-10-06 13:54:46 +02:00
PlayerColor playerToKick ( PlayerColor : : CANNOT_DETERMINE ) ;
if ( std : : all_of ( playername . begin ( ) , playername . end ( ) , : : isdigit ) )
playerToKick = PlayerColor ( std : : stoi ( playername ) ) ;
else
{
for ( auto & c : connections )
{
if ( c . first . getStr ( false ) = = playername )
playerToKick = c . first ;
}
}
if ( playerToKick ! = PlayerColor : : CANNOT_DETERMINE )
{
PlayerCheated pc ;
pc . player = playerToKick ;
pc . losingCheatCode = true ;
sendAndApply ( & pc ) ;
checkVictoryLossConditionsForPlayer ( playerToKick ) ;
}
return ;
}
}
2016-10-02 15:21:46 +02:00
int obj = 0 ;
2023-04-01 11:29:49 +02:00
if ( words . size ( ) = = 2 & & words [ 0 ] ! = " vcmiexp " & & words [ 0 ] ! = " vcmiolorin " )
2014-03-17 22:51:07 +03:00
{
2022-10-08 17:52:29 +02:00
obj = std : : atoi ( words [ 1 ] . c_str ( ) ) ;
2016-10-12 17:16:26 +02:00
if ( obj )
2016-10-02 15:21:46 +02:00
currObj = ObjectInstanceID ( obj ) ;
}
2014-03-17 22:51:07 +03:00
2016-10-02 15:21:46 +02:00
const CGHeroInstance * hero = getHero ( currObj ) ;
const CGTownInstance * town = getTown ( currObj ) ;
2016-10-12 17:16:26 +02:00
if ( ! town & & hero )
2016-10-02 15:21:46 +02:00
town = hero - > visitedTown ;
2014-03-17 22:51:07 +03:00
2023-04-01 11:29:49 +02:00
if ( words . size ( ) > 1 & & ( words [ 0 ] = = " vcmiarmy " | | words [ 0 ] = = " vcminissi " | | words [ 0 ] = = " vcmiexp " | | words [ 0 ] = = " vcmiolorin " ) )
2023-01-23 22:18:43 +02:00
{
2023-01-24 00:12:41 +02:00
std : : string cheatCodeWithOneParameter = std : : string ( words [ 0 ] ) + " " + words [ 1 ] ;
handleCheatCode ( cheatCodeWithOneParameter , player , hero , town , cheated ) ;
2023-01-23 22:18:43 +02:00
}
2023-01-24 00:12:41 +02:00
else if ( words . size ( ) = = 1 | | obj )
2023-01-23 22:18:43 +02:00
{
2023-01-24 00:12:41 +02:00
handleCheatCode ( words [ 0 ] , player , hero , town , cheated ) ;
2023-01-23 22:18:43 +02:00
}
2016-10-02 15:21:46 +02:00
else
{
2016-10-12 17:16:26 +02:00
for ( const auto & i : gs - > players )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( i . first = = PlayerColor : : NEUTRAL )
2016-10-02 15:21:46 +02:00
continue ;
2022-10-08 17:52:29 +02:00
if ( words [ 1 ] = = " ai " )
2016-10-02 15:21:46 +02:00
{
2016-10-12 17:16:26 +02:00
if ( i . second . human )
2016-10-02 15:21:46 +02:00
continue ;
}
2022-10-08 17:52:29 +02:00
else if ( words [ 1 ] ! = " all " & & words [ 1 ] ! = i . first . getStr ( ) )
2016-10-02 15:21:46 +02:00
continue ;
2023-01-25 13:10:48 +02:00
if ( words [ 0 ] = = " vcmiformenos " | | words [ 0 ] = = " vcmieagles " | | words [ 0 ] = = " vcmiungoliant "
| | words [ 0 ] = = " vcmiresources " | | words [ 0 ] = = " vcmimap " | | words [ 0 ] = = " vcmihidemap " )
2016-10-02 15:21:46 +02:00
{
2022-10-08 17:52:29 +02:00
handleCheatCode ( words [ 0 ] , i . first , nullptr , nullptr , cheated ) ;
2016-10-02 15:21:46 +02:00
}
2023-01-25 13:10:48 +02:00
else if ( words [ 0 ] = = " vcmiarmenelos " | | words [ 0 ] = = " vcmibuild " )
2016-10-02 15:21:46 +02:00
{
2016-10-12 17:16:26 +02:00
for ( const auto & t : i . second . towns )
2016-10-02 15:21:46 +02:00
{
2022-10-08 17:52:29 +02:00
handleCheatCode ( words [ 0 ] , i . first , nullptr , t , cheated ) ;
2016-10-02 15:21:46 +02:00
}
}
else
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
for ( const auto & h : i . second . heroes )
2014-03-17 22:51:07 +03:00
{
2022-10-08 17:52:29 +02:00
handleCheatCode ( words [ 0 ] , i . first , h , nullptr , cheated ) ;
2014-03-17 22:51:07 +03:00
}
}
}
}
2016-10-12 17:16:26 +02:00
if ( cheated )
2014-03-17 22:51:07 +03:00
{
2023-01-31 20:14:44 +02:00
if ( ! getPlayerSettings ( player ) - > isControlledByAI ( ) )
{
SystemMessage temp_message ( VLC - > generaltexth - > allTexts [ 260 ] ) ;
sendAndApply ( & temp_message ) ;
}
2017-06-03 07:25:10 +02:00
if ( ! player . isSpectator ( ) )
checkVictoryLossConditionsForPlayer ( player ) ; //Player enter win code or got required art\creature
2014-09-21 16:42:08 +03:00
}
2014-03-17 22:51:07 +03:00
}
2017-07-20 06:08:49 +02:00
bool CGameHandler : : makeCustomAction ( BattleAction & ba )
2014-03-17 22:51:07 +03:00
{
switch ( ba . actionType )
{
2017-07-20 06:08:49 +02:00
case EActionType : : HERO_SPELL :
2014-03-17 22:51:07 +03:00
{
COMPLAIN_RET_FALSE_IF ( ba . side > 1 , " Side must be 0 or 1! " ) ;
const CGHeroInstance * h = gs - > curB - > battleGetFightingHero ( ba . side ) ;
2016-08-30 00:11:54 +02:00
COMPLAIN_RET_FALSE_IF ( ( ! h ) , " Wrong caster! " ) ;
2017-07-20 06:08:49 +02:00
const CSpell * s = SpellID ( ba . actionSubtype ) . toSpell ( ) ;
2016-10-12 17:16:26 +02:00
if ( ! s )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
logGlobal - > error ( " Wrong spell id (%d)! " , ba . actionSubtype ) ;
2014-03-17 22:51:07 +03:00
return false ;
}
2017-07-20 06:08:49 +02:00
spells : : BattleCast parameters ( gs - > curB , h , spells : : Mode : : HERO , s ) ;
2014-03-17 22:51:07 +03:00
2017-07-20 06:08:49 +02:00
spells : : detail : : ProblemImpl problem ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
auto m = s - > battleMechanics ( & parameters ) ;
if ( ! m - > canBeCast ( problem ) ) //todo: should we check aimed cast?
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
logGlobal - > warn ( " Spell cannot be cast! " ) ;
std : : vector < std : : string > texts ;
problem . getAll ( texts ) ;
for ( auto s : texts )
logGlobal - > warn ( s ) ;
2014-11-25 21:00:04 +02:00
return false ;
}
2014-03-17 22:51:07 +03:00
2014-11-25 21:00:04 +02:00
StartAction start_action ( ba ) ;
sendAndApply ( & start_action ) ; //start spell casting
2015-10-28 22:53:44 +02:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
parameters . cast ( spellEnv , ba . getTarget ( gs - > curB ) ) ;
2015-10-28 22:53:44 +02:00
2014-11-25 21:00:04 +02:00
sendAndApply ( & end_action ) ;
2016-10-12 17:16:26 +02:00
if ( ! gs - > curB - > battleGetStackByID ( gs - > curB - > activeStack ) )
2014-11-25 21:00:04 +02:00
{
battleMadeAction . setn ( true ) ;
2014-03-17 22:51:07 +03:00
}
2016-02-09 09:59:33 +02:00
checkBattleStateChanges ( ) ;
2016-10-12 17:16:26 +02:00
if ( battleResult . get ( ) )
2014-03-17 22:51:07 +03:00
{
2014-11-25 21:00:04 +02:00
battleMadeAction . setn ( true ) ;
//battle will be ended by startBattle function
//endBattle(gs->curB->tile, gs->curB->heroes[0], gs->curB->heroes[1]);
2014-03-17 22:51:07 +03:00
}
2014-11-25 21:00:04 +02:00
return true ;
2014-03-17 22:51:07 +03:00
}
}
return false ;
}
2016-11-02 19:11:01 +02:00
void CGameHandler : : stackEnchantedTrigger ( const CStack * st )
2016-10-28 23:37:45 +02:00
{
2023-05-01 00:20:01 +02:00
auto bl = * ( st - > getBonuses ( Selector : : type ( ) ( BonusType : : ENCHANTED ) ) ) ;
2016-11-02 19:11:01 +02:00
for ( auto b : bl )
2016-10-29 13:45:08 +02:00
{
2017-07-20 06:08:49 +02:00
const CSpell * sp = SpellID ( b - > subtype ) . toSpell ( ) ;
if ( ! sp )
continue ;
const int32_t val = bl . valOfBonuses ( Selector : : typeSubtype ( b - > type , b - > subtype ) ) ;
const int32_t level = ( ( val > 3 ) ? ( val - 3 ) : val ) ;
spells : : BattleCast battleCast ( gs - > curB , st , spells : : Mode : : PASSIVE , sp ) ;
//this makes effect accumulate for at most 50 turns by default, but effect may be permanent and last till the end of battle
battleCast . setEffectDuration ( 50 ) ;
battleCast . setSpellLevel ( level ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
spells : : Target target ;
2017-07-20 06:08:49 +02:00
2016-11-02 19:11:01 +02:00
if ( val > 3 )
2016-10-29 13:45:08 +02:00
{
2016-11-02 19:11:01 +02:00
for ( auto s : gs - > curB - > battleGetAllStacks ( ) )
if ( battleMatchOwner ( st , s , true ) & & s - > isValidTarget ( ) ) //all allied
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
target . emplace_back ( s ) ;
2016-10-29 13:45:08 +02:00
}
else
2017-07-20 06:08:49 +02:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
target . emplace_back ( st ) ;
2017-07-20 06:08:49 +02:00
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
battleCast . applyEffects ( spellEnv , target , false , true ) ;
2016-10-29 13:45:08 +02:00
}
2016-10-28 23:37:45 +02:00
}
void CGameHandler : : stackTurnTrigger ( const CStack * st )
2014-03-17 22:51:07 +03:00
{
BattleTriggerEffect bte ;
2023-04-27 19:43:20 +02:00
bte . stackID = st - > unitId ( ) ;
2014-03-17 22:51:07 +03:00
bte . effect = - 1 ;
bte . val = 0 ;
bte . additionalInfo = 0 ;
if ( st - > alive ( ) )
{
//unbind
2023-05-01 00:20:01 +02:00
if ( st - > hasBonus ( Selector : : type ( ) ( BonusType : : BIND_EFFECT ) ) )
2014-03-17 22:51:07 +03:00
{
bool unbind = true ;
2023-05-01 00:20:01 +02:00
BonusList bl = * ( st - > getBonuses ( Selector : : type ( ) ( BonusType : : BIND_EFFECT ) ) ) ;
2017-07-20 06:08:49 +02:00
auto adjacent = gs - > curB - > battleAdjacentUnits ( st ) ;
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
for ( auto b : bl )
2014-03-17 22:51:07 +03:00
{
2018-03-12 07:20:18 +02:00
if ( b - > additionalInfo ! = CAddInfo : : NONE )
2018-03-04 10:13:07 +02:00
{
2018-03-12 07:20:18 +02:00
const CStack * stack = gs - > curB - > battleGetStackByID ( b - > additionalInfo [ 0 ] ) ; //binding stack must be alive and adjacent
2018-03-04 10:13:07 +02:00
if ( stack )
{
if ( vstd : : contains ( adjacent , stack ) ) //binding stack is still present
unbind = false ;
}
}
else
2014-03-17 22:51:07 +03:00
{
2018-03-04 10:13:07 +02:00
unbind = false ;
2014-03-17 22:51:07 +03:00
}
}
if ( unbind )
{
BattleSetStackProperty ssp ;
ssp . which = BattleSetStackProperty : : UNBIND ;
2023-04-27 19:43:20 +02:00
ssp . stackID = st - > unitId ( ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & ssp ) ;
}
}
2023-05-01 00:20:01 +02:00
if ( st - > hasBonusOfType ( BonusType : : POISON ) )
2014-03-17 22:51:07 +03:00
{
2023-05-01 00:20:01 +02:00
std : : shared_ptr < const Bonus > b = st - > getBonusLocalFirst ( Selector : : source ( BonusSource : : SPELL_EFFECT , SpellID : : POISON ) . And ( Selector : : type ( ) ( BonusType : : STACK_HEALTH ) ) ) ;
2014-03-17 22:51:07 +03:00
if ( b ) //TODO: what if not?...
{
2023-05-01 00:20:01 +02:00
bte . val = std : : max ( b - > val - 10 , - ( st - > valOfBonuses ( BonusType : : POISON ) ) ) ;
2014-03-17 22:51:07 +03:00
if ( bte . val < b - > val ) //(negative) poison effect increases - update it
{
2023-05-01 00:20:01 +02:00
bte . effect = vstd : : to_underlying ( BonusType : : POISON ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & bte ) ;
}
}
}
2023-05-01 00:20:01 +02:00
if ( st - > hasBonusOfType ( BonusType : : MANA_DRAIN ) & & ! st - > drainedMana )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
const PlayerColor opponent = gs - > curB - > otherPlayer ( gs - > curB - > battleGetOwner ( st ) ) ;
2015-09-16 17:28:14 +02:00
const CGHeroInstance * opponentHero = gs - > curB - > getHero ( opponent ) ;
2017-07-20 06:08:49 +02:00
if ( opponentHero )
2014-03-17 22:51:07 +03:00
{
2023-05-01 00:20:01 +02:00
ui32 manaDrained = st - > valOfBonuses ( BonusType : : MANA_DRAIN ) ;
2015-09-16 17:28:14 +02:00
vstd : : amin ( manaDrained , opponentHero - > mana ) ;
2017-07-20 06:08:49 +02:00
if ( manaDrained )
2014-03-17 22:51:07 +03:00
{
2023-05-01 00:20:01 +02:00
bte . effect = vstd : : to_underlying ( BonusType : : MANA_DRAIN ) ;
2014-03-17 22:51:07 +03:00
bte . val = manaDrained ;
2015-09-16 17:28:14 +02:00
bte . additionalInfo = opponentHero - > id . getNum ( ) ; //for sanity
2014-03-17 22:51:07 +03:00
sendAndApply ( & bte ) ;
}
}
}
2023-05-01 00:20:01 +02:00
if ( st - > isLiving ( ) & & ! st - > hasBonusOfType ( BonusType : : FEARLESS ) )
2014-03-17 22:51:07 +03:00
{
bool fearsomeCreature = false ;
2016-10-12 17:16:26 +02:00
for ( CStack * stack : gs - > curB - > stacks )
2014-03-17 22:51:07 +03:00
{
2023-05-01 00:20:01 +02:00
if ( battleMatchOwner ( st , stack ) & & stack - > alive ( ) & & stack - > hasBonusOfType ( BonusType : : FEAR ) )
2014-03-17 22:51:07 +03:00
{
fearsomeCreature = true ;
break ;
}
}
if ( fearsomeCreature )
{
2016-09-07 23:54:26 +02:00
if ( getRandomGenerator ( ) . nextInt ( 99 ) < 10 ) //fixed 10%
2014-03-17 22:51:07 +03:00
{
2023-05-01 00:20:01 +02:00
bte . effect = vstd : : to_underlying ( BonusType : : FEAR ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & bte ) ;
}
}
}
2023-05-01 00:20:01 +02:00
BonusList bl = * ( st - > getBonuses ( Selector : : type ( ) ( BonusType : : ENCHANTER ) ) ) ;
2023-04-27 19:43:20 +02:00
int side = gs - > curB - > whatSide ( st - > unitOwner ( ) ) ;
2017-07-20 06:08:49 +02:00
if ( st - > canCast ( ) & & gs - > curB - > battleGetEnchanterCounter ( side ) = = 0 )
2014-03-17 22:51:07 +03:00
{
2015-09-21 11:19:35 +02:00
bool cast = false ;
2017-07-20 06:08:49 +02:00
while ( ! bl . empty ( ) & & ! cast )
2014-03-17 22:51:07 +03:00
{
2016-09-07 23:54:26 +02:00
auto bonus = * RandomGeneratorUtil : : nextItem ( bl , getRandomGenerator ( ) ) ;
2015-06-21 20:27:58 +02:00
auto spellID = SpellID ( bonus - > subtype ) ;
const CSpell * spell = SpellID ( spellID ) . toSpell ( ) ;
2017-07-20 06:08:49 +02:00
bl . remove_if ( [ & bonus ] ( const Bonus * b )
2017-06-05 20:41:27 +02:00
{
2017-07-20 06:08:49 +02:00
return b = = bonus . get ( ) ;
} ) ;
spells : : BattleCast parameters ( gs - > curB , st , spells : : Mode : : ENCHANTER , spell ) ;
parameters . setSpellLevel ( bonus - > val ) ;
2018-02-10 06:31:02 +02:00
parameters . massive = true ;
parameters . smart = true ;
2017-07-20 06:08:49 +02:00
//todo: recheck effect level
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( parameters . castIfPossible ( spellEnv , spells : : Target ( 1 , spells : : Destination ( ) ) ) )
2017-07-20 06:08:49 +02:00
{
cast = true ;
2018-03-12 07:20:18 +02:00
int cooldown = bonus - > additionalInfo [ 0 ] ;
2015-06-21 20:27:58 +02:00
BattleSetStackProperty ssp ;
ssp . which = BattleSetStackProperty : : ENCHANTER_COUNTER ;
ssp . absolute = false ;
2017-07-20 06:08:49 +02:00
ssp . val = cooldown ;
ssp . stackID = st - > unitId ( ) ;
2015-06-21 20:27:58 +02:00
sendAndApply ( & ssp ) ;
2015-10-28 22:53:44 +02:00
}
2014-03-17 22:51:07 +03:00
}
}
}
}
void CGameHandler : : handleTimeEvents ( )
{
gs - > map - > events . sort ( evntCmp ) ;
while ( gs - > map - > events . size ( ) & & gs - > map - > events . front ( ) . firstOccurence + 1 = = gs - > day )
{
CMapEvent ev = gs - > map - > events . front ( ) ;
2015-10-28 22:53:44 +02:00
2014-09-17 12:03:53 +03:00
for ( int player = 0 ; player < PlayerColor : : PLAYER_LIMIT_I ; player + + )
2014-03-17 22:51:07 +03:00
{
2014-09-17 12:03:53 +03:00
auto color = PlayerColor ( player ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const PlayerState * pinfo = getPlayerState ( color , false ) ; //do not output error if player does not exist
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( pinfo //player exists
2014-03-17 22:51:07 +03:00
& & ( ev . players & 1 < < player ) //event is enabled to this player
& & ( ( ev . computerAffected & & ! pinfo - > human )
| | ( ev . humanAffected & & pinfo - > human )
)
)
{
//give resources
2016-11-26 14:14:43 +02:00
giveResources ( color , ev . resources ) ;
2014-03-17 22:51:07 +03:00
//prepare dialog
InfoWindow iw ;
2014-09-17 12:03:53 +03:00
iw . player = color ;
2014-03-17 22:51:07 +03:00
iw . text < < ev . message ;
for ( int i = 0 ; i < ev . resources . size ( ) ; i + + )
{
2023-03-31 13:12:38 +02:00
if ( ev . resources [ i ] ) //if resource is changed, we add it to the dialog
iw . components . emplace_back ( Component : : EComponentType : : RESOURCE , i , ev . resources [ i ] , 0 ) ;
2014-03-17 22:51:07 +03:00
}
sendAndApply ( & iw ) ; //show dialog
}
} //PLAYERS LOOP
2016-10-12 17:16:26 +02:00
if ( ev . nextOccurence )
2014-03-17 22:51:07 +03:00
{
gs - > map - > events . pop_front ( ) ;
ev . firstOccurence + = ev . nextOccurence ;
auto it = gs - > map - > events . begin ( ) ;
2016-03-12 03:41:27 +02:00
while ( it ! = gs - > map - > events . end ( ) & & it - > earlierThanOrEqual ( ev ) )
2014-03-17 22:51:07 +03:00
it + + ;
gs - > map - > events . insert ( it , ev ) ;
}
else
{
gs - > map - > events . pop_front ( ) ;
}
}
//TODO send only if changed
UpdateMapEvents ume ;
ume . events = gs - > map - > events ;
sendAndApply ( & ume ) ;
}
void CGameHandler : : handleTownEvents ( CGTownInstance * town , NewTurn & n )
{
town - > events . sort ( evntCmp ) ;
while ( town - > events . size ( ) & & town - > events . front ( ) . firstOccurence = = gs - > day )
{
PlayerColor player = town - > tempOwner ;
CCastleEvent ev = town - > events . front ( ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const PlayerState * pinfo = getPlayerState ( player , false ) ;
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( pinfo //player exists
2014-03-17 22:51:07 +03:00
& & ( ev . players & 1 < < player . getNum ( ) ) //event is enabled to this player
& & ( ( ev . computerAffected & & ! pinfo - > human )
2016-10-12 17:16:26 +02:00
| | ( ev . humanAffected & & pinfo - > human ) ) )
2014-03-17 22:51:07 +03:00
{
// dialog
InfoWindow iw ;
iw . player = player ;
iw . text < < ev . message ;
2016-10-12 17:16:26 +02:00
if ( ev . resources . nonZero ( ) )
2014-03-17 22:51:07 +03:00
{
TResources was = n . res [ player ] ;
n . res [ player ] + = ev . resources ;
n . res [ player ] . amax ( 0 ) ;
for ( int i = 0 ; i < ev . resources . size ( ) ; i + + )
2023-03-31 13:12:38 +02:00
if ( ev . resources [ i ] & & pinfo - > resources [ i ] ! = n . res . at ( player ) [ i ] ) //if resource had changed, we add it to the dialog
iw . components . emplace_back ( Component : : EComponentType : : RESOURCE , i , n . res . at ( player ) [ i ] - was [ i ] , 0 ) ;
2014-03-17 22:51:07 +03:00
}
2016-10-12 17:16:26 +02:00
for ( auto & i : ev . buildings )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ! town - > hasBuilt ( i ) )
2014-03-17 22:51:07 +03:00
{
buildStructure ( town - > id , i , true ) ;
2023-03-10 14:54:12 +02:00
iw . components . emplace_back ( Component : : EComponentType : : BUILDING , town - > subID , i , 0 ) ;
2014-03-17 22:51:07 +03:00
}
}
if ( ! ev . creatures . empty ( ) & & ! vstd : : contains ( n . cres , town - > id ) )
{
n . cres [ town - > id ] . tid = town - > id ;
n . cres [ town - > id ] . creatures = town - > creatures ;
}
auto & sac = n . cres [ town - > id ] ;
2016-10-12 17:16:26 +02:00
for ( si32 i = 0 ; i < ev . creatures . size ( ) ; i + + ) //creature growths
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ! town - > creatures . at ( i ) . second . empty ( ) & & ev . creatures . at ( i ) > 0 ) //there is dwelling
2014-03-17 22:51:07 +03:00
{
sac . creatures [ i ] . first + = ev . creatures . at ( i ) ;
2023-03-10 14:54:12 +02:00
iw . components . emplace_back ( Component : : EComponentType : : CREATURE ,
town - > creatures . at ( i ) . second . back ( ) , ev . creatures . at ( i ) , 0 ) ;
2014-03-17 22:51:07 +03:00
}
}
sendAndApply ( & iw ) ; //show dialog
}
2016-10-12 17:16:26 +02:00
if ( ev . nextOccurence )
2014-03-17 22:51:07 +03:00
{
town - > events . pop_front ( ) ;
ev . firstOccurence + = ev . nextOccurence ;
auto it = town - > events . begin ( ) ;
2016-03-12 03:41:27 +02:00
while ( it ! = town - > events . end ( ) & & it - > earlierThanOrEqual ( ev ) )
2014-03-17 22:51:07 +03:00
it + + ;
town - > events . insert ( it , ev ) ;
}
else
{
town - > events . pop_front ( ) ;
}
}
//TODO send only if changed
UpdateCastleEvents uce ;
uce . town = town - > id ;
uce . events = town - > events ;
sendAndApply ( & uce ) ;
}
2016-10-12 17:16:26 +02:00
bool CGameHandler : : complain ( const std : : string & problem )
2014-03-17 22:51:07 +03:00
{
sendMessageToAll ( " Server encountered a problem: " + problem ) ;
2016-08-30 00:11:54 +02:00
logGlobal - > error ( problem ) ;
2014-03-17 22:51:07 +03:00
return true ;
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : showGarrisonDialog ( ObjectInstanceID upobj , ObjectInstanceID hid , bool removableUnits )
2014-03-17 22:51:07 +03:00
{
//PlayerColor player = getOwner(hid);
auto upperArmy = dynamic_cast < const CArmedInstance * > ( getObj ( upobj ) ) ;
auto lowerArmy = dynamic_cast < const CArmedInstance * > ( getObj ( hid ) ) ;
assert ( lowerArmy ) ;
assert ( upperArmy ) ;
2017-06-06 06:53:51 +02:00
auto garrisonQuery = std : : make_shared < CGarrisonDialogQuery > ( this , upperArmy , lowerArmy ) ;
2014-03-17 22:51:07 +03:00
queries . addQuery ( garrisonQuery ) ;
GarrisonDialog gd ;
gd . hid = hid ;
gd . objid = upobj ;
gd . removableUnits = removableUnits ;
gd . queryID = garrisonQuery - > queryID ;
sendAndApply ( & gd ) ;
}
void CGameHandler : : showThievesGuildWindow ( PlayerColor player , ObjectInstanceID requestingObjId )
{
OpenWindow ow ;
2023-03-08 00:32:21 +02:00
ow . window = EOpenWindowMode : : THIEVES_GUILD ;
2014-03-17 22:51:07 +03:00
ow . id1 = player . getNum ( ) ;
ow . id2 = requestingObjId . getNum ( ) ;
sendAndApply ( & ow ) ;
}
2016-10-12 17:16:26 +02:00
bool CGameHandler : : isAllowedExchange ( ObjectInstanceID id1 , ObjectInstanceID id2 )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( id1 = = id2 )
2014-03-17 22:51:07 +03:00
return true ;
const CGObjectInstance * o1 = getObj ( id1 ) , * o2 = getObj ( id2 ) ;
2016-10-12 17:16:26 +02:00
if ( ! o1 | | ! o2 )
2014-03-17 22:51:07 +03:00
return true ; //arranging stacks within an object should be always allowed
if ( o1 & & o2 )
{
2016-10-12 17:16:26 +02:00
if ( o1 - > ID = = Obj : : TOWN )
2014-03-17 22:51:07 +03:00
{
const CGTownInstance * t = static_cast < const CGTownInstance * > ( o1 ) ;
2016-10-12 17:16:26 +02:00
if ( t - > visitingHero = = o2 | | t - > garrisonHero = = o2 )
2014-03-17 22:51:07 +03:00
return true ;
}
2016-10-12 17:16:26 +02:00
if ( o2 - > ID = = Obj : : TOWN )
2014-03-17 22:51:07 +03:00
{
const CGTownInstance * t = static_cast < const CGTownInstance * > ( o2 ) ;
2016-10-12 17:16:26 +02:00
if ( t - > visitingHero = = o1 | | t - > garrisonHero = = o1 )
2014-03-17 22:51:07 +03:00
return true ;
}
if ( o1 - > ID = = Obj : : HERO & & o2 - > ID = = Obj : : HERO )
{
const CGHeroInstance * h1 = static_cast < const CGHeroInstance * > ( o1 ) ;
const CGHeroInstance * h2 = static_cast < const CGHeroInstance * > ( o2 ) ;
// two heroes in same town (garrisoned and visiting)
if ( h1 - > visitedTown ! = nullptr & & h2 - > visitedTown ! = nullptr & & h1 - > visitedTown = = h2 - > visitedTown )
return true ;
}
2022-09-09 19:30:24 +02:00
//Ongoing garrison exchange - usually picking from top garison (from o1 to o2), but who knows
auto dialog = std : : dynamic_pointer_cast < CGarrisonDialogQuery > ( queries . topQuery ( o1 - > tempOwner ) ) ;
if ( ! dialog )
2014-03-17 22:51:07 +03:00
{
2022-09-09 19:30:24 +02:00
dialog = std : : dynamic_pointer_cast < CGarrisonDialogQuery > ( queries . topQuery ( o2 - > tempOwner ) ) ;
}
if ( dialog )
{
auto topArmy = dialog - > exchangingArmies . at ( 0 ) ;
auto bottomArmy = dialog - > exchangingArmies . at ( 1 ) ;
2022-09-22 10:22:14 +02:00
if ( ( topArmy = = o1 & & bottomArmy = = o2 ) | | ( bottomArmy = = o1 & & topArmy = = o2 ) )
2014-03-17 22:51:07 +03:00
return true ;
}
}
return false ;
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : objectVisited ( const CGObjectInstance * obj , const CGHeroInstance * h )
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
using events : : ObjectVisitStarted ;
2016-08-30 00:11:54 +02:00
logGlobal - > debug ( " %s visits %s (%d:%d) " , h - > nodeName ( ) , obj - > getObjectName ( ) , obj - > ID , obj - > subID ) ;
2014-03-17 22:51:07 +03:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
std : : shared_ptr < CObjectVisitQuery > visitQuery ;
auto startVisit = [ & ] ( ObjectVisitStarted & event )
{
2021-10-27 00:37:45 +02:00
auto visitedObject = obj ;
if ( obj - > ID = = Obj : : HERO )
{
auto visitedHero = static_cast < const CGHeroInstance * > ( obj ) ;
const auto visitedTown = visitedHero - > visitedTown ;
if ( visitedTown )
{
const bool isEnemy = visitedHero - > getOwner ( ) ! = h - > getOwner ( ) ;
if ( isEnemy & & ! visitedTown - > isBattleOutsideTown ( visitedHero ) )
visitedObject = visitedTown ;
}
}
visitQuery = std : : make_shared < CObjectVisitQuery > ( this , visitedObject , h , visitedObject - > visitablePos ( ) ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
queries . addQuery ( visitQuery ) ; //TODO real visit pos
HeroVisit hv ;
hv . objId = obj - > id ;
hv . heroId = h - > id ;
hv . player = h - > tempOwner ;
hv . starting = true ;
sendAndApply ( & hv ) ;
obj - > onHeroVisit ( h ) ;
} ;
2014-03-17 22:51:07 +03:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
ObjectVisitStarted : : defaultExecute ( serverEventBus . get ( ) , startVisit , h - > tempOwner , h - > id , obj - > id ) ;
2014-03-17 22:51:07 +03:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( visitQuery )
queries . popIfTop ( visitQuery ) ; //visit ends here if no queries were created
2014-03-17 22:51:07 +03:00
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
void CGameHandler : : objectVisitEnded ( const CObjectVisitQuery & query )
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
using events : : ObjectVisitEnded ;
2016-08-30 00:11:54 +02:00
logGlobal - > debug ( " %s visit ends. \n " , query . visitingHero - > nodeName ( ) ) ;
2014-03-17 22:51:07 +03:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
auto endVisit = [ & ] ( ObjectVisitEnded & event )
{
HeroVisit hv ;
hv . player = event . getPlayer ( ) ;
hv . heroId = event . getHero ( ) ;
hv . starting = false ;
sendAndApply ( & hv ) ;
} ;
//TODO: ObjectVisitEnded should also have id of visited object,
//but this requires object being deleted only by `removeAfterVisit()` but not `removeObject()`
ObjectVisitEnded : : defaultExecute ( serverEventBus . get ( ) , endVisit , query . players . front ( ) , query . visitingHero - > id ) ;
2014-03-17 22:51:07 +03:00
}
2023-03-31 19:02:09 +02:00
bool CGameHandler : : buildBoat ( ObjectInstanceID objid , PlayerColor playerID )
2014-03-17 22:51:07 +03:00
{
const IShipyard * obj = IShipyard : : castFrom ( getObj ( objid ) ) ;
2016-10-12 17:16:26 +02:00
if ( obj - > shipyardStatus ( ) ! = IBoatGenerator : : GOOD )
2014-03-17 22:51:07 +03:00
{
complain ( " Cannot build boat in this shipyard! " ) ;
return false ;
}
2016-10-12 17:16:26 +02:00
else if ( obj - > o - > ID = = Obj : : TOWN
2014-03-17 22:51:07 +03:00
& & ! static_cast < const CGTownInstance * > ( obj ) - > hasBuilt ( BuildingID : : SHIPYARD ) )
{
complain ( " Cannot build boat in the town - no shipyard! " ) ;
return false ;
}
TResources boatCost ;
obj - > getBoatCost ( boatCost ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
TResources aviable = getPlayerState ( playerID ) - > resources ;
2014-03-17 22:51:07 +03:00
if ( ! aviable . canAfford ( boatCost ) )
{
complain ( " Not enough resources to build a boat! " ) ;
return false ;
}
int3 tile = obj - > bestLocation ( ) ;
2016-10-12 17:16:26 +02:00
if ( ! gs - > map - > isInTheMap ( tile ) )
2014-03-17 22:51:07 +03:00
{
complain ( " Cannot find appropriate tile for a boat! " ) ;
return false ;
}
//take boat cost
2016-11-26 14:14:43 +02:00
giveResources ( playerID , - boatCost ) ;
2014-03-17 22:51:07 +03:00
//create boat
NewObject no ;
no . ID = Obj : : BOAT ;
2023-04-20 19:20:51 +02:00
no . subID = obj - > getBoatType ( ) . getNum ( ) ;
2014-03-17 22:51:07 +03:00
no . pos = tile + int3 ( 1 , 0 , 0 ) ;
sendAndApply ( & no ) ;
return true ;
}
2016-10-12 17:16:26 +02:00
void CGameHandler : : engageIntoBattle ( PlayerColor player )
2014-03-17 22:51:07 +03:00
{
//notify interfaces
PlayerBlocked pb ;
pb . player = player ;
pb . reason = PlayerBlocked : : UPCOMING_BATTLE ;
pb . startOrEnd = PlayerBlocked : : BLOCKADE_STARTED ;
sendAndApply ( & pb ) ;
}
void CGameHandler : : checkVictoryLossConditions ( const std : : set < PlayerColor > & playerColors )
{
2016-10-12 17:16:26 +02:00
for ( auto playerColor : playerColors )
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( getPlayerState ( playerColor , false ) )
2014-03-17 22:51:07 +03:00
checkVictoryLossConditionsForPlayer ( playerColor ) ;
}
}
void CGameHandler : : checkVictoryLossConditionsForAll ( )
{
std : : set < PlayerColor > playerColors ;
2016-10-12 17:16:26 +02:00
for ( int i = 0 ; i < PlayerColor : : PLAYER_LIMIT_I ; + + i )
2014-03-17 22:51:07 +03:00
{
playerColors . insert ( PlayerColor ( i ) ) ;
}
checkVictoryLossConditions ( playerColors ) ;
}
void CGameHandler : : checkVictoryLossConditionsForPlayer ( PlayerColor player )
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const PlayerState * p = getPlayerState ( player ) ;
2021-07-15 23:32:13 +02:00
if ( ! p | | p - > status ! = EPlayerStatus : : INGAME ) return ;
2014-03-17 22:51:07 +03:00
auto victoryLossCheckResult = gs - > checkForVictoryAndLoss ( player ) ;
2016-10-12 17:16:26 +02:00
if ( victoryLossCheckResult . victory ( ) | | victoryLossCheckResult . loss ( ) )
2014-03-17 22:51:07 +03:00
{
InfoWindow iw ;
getVictoryLossMessage ( player , victoryLossCheckResult , iw ) ;
sendAndApply ( & iw ) ;
PlayerEndsGame peg ;
peg . player = player ;
peg . victoryLossCheckResult = victoryLossCheckResult ;
sendAndApply ( & peg ) ;
2016-10-12 17:16:26 +02:00
if ( victoryLossCheckResult . victory ( ) )
2014-03-17 22:51:07 +03:00
{
//one player won -> all enemies lost
for ( auto i = gs - > players . cbegin ( ) ; i ! = gs - > players . cend ( ) ; i + + )
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( i - > first ! = player & & getPlayerState ( i - > first ) - > status = = EPlayerStatus : : INGAME )
2014-03-17 22:51:07 +03:00
{
peg . player = i - > first ;
2016-09-18 10:53:51 +02:00
peg . victoryLossCheckResult = getPlayerRelations ( player , i - > first ) = = PlayerRelations : : ALLIES ?
2014-03-17 22:51:07 +03:00
victoryLossCheckResult : victoryLossCheckResult . invert ( ) ; // ally of winner
InfoWindow iw ;
getVictoryLossMessage ( player , peg . victoryLossCheckResult , iw ) ;
iw . player = i - > first ;
sendAndApply ( & iw ) ;
sendAndApply ( & peg ) ;
}
}
2018-01-05 19:21:07 +02:00
if ( p - > human )
2014-03-17 22:51:07 +03:00
{
2018-01-05 19:21:07 +02:00
lobby - > state = EServerState : : GAMEPLAY_ENDED ;
2014-03-17 22:51:07 +03:00
}
}
else
{
2015-12-10 11:43:55 +02:00
//copy heroes vector to avoid iterator invalidation as removal change PlayerState
auto hlp = p - > heroes ;
2016-10-12 17:16:26 +02:00
for ( auto h : hlp ) //eliminate heroes
2015-12-10 11:43:55 +02:00
{
2016-10-12 17:16:26 +02:00
if ( h . get ( ) )
2015-10-24 15:09:46 +02:00
removeObject ( h ) ;
2015-12-10 11:43:55 +02:00
}
2014-03-17 22:51:07 +03:00
2015-12-10 11:43:55 +02:00
//player lost -> all his objects become unflagged (neutral)
2015-10-24 15:09:46 +02:00
for ( auto obj : gs - > map - > objects ) //unflag objs
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( obj . get ( ) & & obj - > tempOwner = = player )
2015-10-24 15:09:46 +02:00
setOwner ( obj , PlayerColor : : NEUTRAL ) ;
2014-03-17 22:51:07 +03:00
}
//eliminating one player may cause victory of another:
std : : set < PlayerColor > playerColors ;
2015-10-24 17:15:21 +02:00
//do not copy player state (CBonusSystemNode) by value
2015-10-25 08:07:01 +02:00
for ( auto & p : gs - > players ) //players may have different colors, iterate over players and not integers
2014-03-17 22:51:07 +03:00
{
2015-10-25 08:07:01 +02:00
if ( p . first ! = player )
playerColors . insert ( p . first ) ;
2014-03-17 22:51:07 +03:00
}
//notify all players
2015-10-24 15:09:46 +02:00
for ( auto pc : playerColors )
2014-03-17 22:51:07 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( getPlayerState ( pc ) - > status = = EPlayerStatus : : INGAME )
2014-03-17 22:51:07 +03:00
{
InfoWindow iw ;
getVictoryLossMessage ( player , victoryLossCheckResult . invert ( ) , iw ) ;
2015-10-24 15:09:46 +02:00
iw . player = pc ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & iw ) ;
}
}
checkVictoryLossConditions ( playerColors ) ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
auto playerInfo = getPlayerState ( gs - > currentPlayer , false ) ;
2014-03-17 22:51:07 +03:00
// If we are called before the actual game start, there might be no current player
2015-10-24 15:09:46 +02:00
if ( playerInfo & & playerInfo - > status ! = EPlayerStatus : : INGAME )
2014-03-17 22:51:07 +03:00
{
// If player making turn has lost his turn must be over as well
states . setFlag ( gs - > currentPlayer , & PlayerStatus : : makingTurn , false ) ;
}
}
}
2014-06-25 17:11:07 +03:00
void CGameHandler : : getVictoryLossMessage ( PlayerColor player , const EVictoryLossCheckResult & victoryLossCheckResult , InfoWindow & out ) const
2014-03-17 22:51:07 +03:00
{
out . player = player ;
out . text . clear ( ) ;
out . text < < victoryLossCheckResult . messageToSelf ;
// hackish, insert one player-specific string, if applicable
if ( victoryLossCheckResult . messageToSelf . find ( " %s " ) ! = std : : string : : npos )
out . text . addReplacement ( MetaString : : COLOR , player . getNum ( ) ) ;
2023-03-10 14:54:12 +02:00
out . components . emplace_back ( Component : : EComponentType : : FLAG , player . getNum ( ) , 0 , 0 ) ;
2014-03-17 22:51:07 +03:00
}
2016-10-12 17:16:26 +02:00
bool CGameHandler : : dig ( const CGHeroInstance * h )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( h - > diggingStatus ( ) ! = EDiggingStatus : : CAN_DIG ) //checks for terrain and movement
2014-03-17 22:51:07 +03:00
COMPLAIN_RETF ( " Hero cannot dig (error code %d)! " , h - > diggingStatus ( ) ) ;
//create a hole
NewObject no ;
no . ID = Obj : : HOLE ;
2022-12-07 21:50:45 +02:00
no . pos = h - > visitablePos ( ) ;
2014-03-17 22:51:07 +03:00
no . subID = 0 ;
sendAndApply ( & no ) ;
//take MPs
SetMovePoints smp ;
smp . hid = h - > id ;
smp . val = 0 ;
sendAndApply ( & smp ) ;
InfoWindow iw ;
2023-03-07 03:09:19 +02:00
iw . type = EInfoWindowMode : : AUTO ;
2014-03-17 22:51:07 +03:00
iw . player = h - > tempOwner ;
2022-12-07 21:50:45 +02:00
if ( gs - > map - > grailPos = = h - > visitablePos ( ) )
2014-03-17 22:51:07 +03:00
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 58 ) ; //"Congratulations! After spending many hours digging here, your hero has uncovered the "
2015-11-06 20:54:51 +02:00
iw . text . addTxt ( MetaString : : ART_NAMES , ArtifactID : : GRAIL ) ;
2014-03-17 22:51:07 +03:00
iw . soundID = soundBase : : ULTIMATEARTIFACT ;
2023-05-16 15:51:45 +02:00
giveHeroNewArtifact ( h , VLC - > arth - > objects [ ArtifactID : : GRAIL ] , ArtifactPosition : : FIRST_AVAILABLE ) ; //give grail
2014-03-17 22:51:07 +03:00
sendAndApply ( & iw ) ;
iw . soundID = soundBase : : invalid ;
2023-03-10 23:52:38 +02:00
iw . components . emplace_back ( Component : : EComponentType : : ARTIFACT , ArtifactID : : GRAIL , 0 , 0 ) ;
2014-03-17 22:51:07 +03:00
iw . text . clear ( ) ;
2015-11-06 20:54:51 +02:00
iw . text . addTxt ( MetaString : : ART_DESCR , ArtifactID : : GRAIL ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & iw ) ;
}
else
{
iw . text . addTxt ( MetaString : : GENERAL_TXT , 59 ) ; //"Nothing here. \n Where could it be?"
iw . soundID = soundBase : : Dig ;
sendAndApply ( & iw ) ;
}
return true ;
}
2023-05-01 00:20:01 +02:00
void CGameHandler : : attackCasting ( bool ranged , BonusType attackMode , const battle : : Unit * attacker , const battle : : Unit * defender )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
if ( attacker - > hasBonusOfType ( attackMode ) )
2014-03-17 22:51:07 +03:00
{
std : : set < SpellID > spellsToCast ;
2020-11-11 21:43:40 +02:00
TConstBonusListPtr spells = attacker - > getBonuses ( Selector : : type ( ) ( attackMode ) ) ;
2020-10-01 07:55:41 +02:00
for ( const auto & sf : * spells )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
spellsToCast . insert ( SpellID ( sf - > subtype ) ) ;
2014-03-17 22:51:07 +03:00
}
2017-07-20 06:08:49 +02:00
for ( SpellID spellID : spellsToCast )
2014-03-17 22:51:07 +03:00
{
bool castMe = false ;
2017-07-20 06:08:49 +02:00
if ( ! defender - > alive ( ) )
2016-10-12 17:16:26 +02:00
{
logGlobal - > debug ( " attackCasting: all attacked creatures have been killed " ) ;
2014-03-17 22:51:07 +03:00
return ;
2016-10-12 17:16:26 +02:00
}
2017-07-20 06:08:49 +02:00
int32_t spellLevel = 0 ;
2020-10-01 07:55:41 +02:00
TConstBonusListPtr spellsByType = attacker - > getBonuses ( Selector : : typeSubtype ( attackMode , spellID ) ) ;
for ( const auto & sf : * spellsByType )
2014-03-17 22:51:07 +03:00
{
2018-03-12 07:20:18 +02:00
int meleeRanged ;
if ( sf - > additionalInfo . size ( ) < 2 )
{
// legacy format
vstd : : amax ( spellLevel , sf - > additionalInfo [ 0 ] % 1000 ) ;
meleeRanged = sf - > additionalInfo [ 0 ] / 1000 ;
}
else
{
vstd : : amax ( spellLevel , sf - > additionalInfo [ 0 ] ) ;
meleeRanged = sf - > additionalInfo [ 1 ] ;
}
2017-07-20 06:08:49 +02:00
if ( meleeRanged = = 0 | | ( meleeRanged = = 1 & & ranged ) | | ( meleeRanged = = 2 & & ! ranged ) )
2014-03-17 22:51:07 +03:00
castMe = true ;
}
int chance = attacker - > valOfBonuses ( ( Selector : : typeSubtype ( attackMode , spellID ) ) ) ;
2016-10-12 17:16:26 +02:00
vstd : : amin ( chance , 100 ) ;
2014-03-17 22:51:07 +03:00
const CSpell * spell = SpellID ( spellID ) . toSpell ( ) ;
2018-03-02 12:22:51 +02:00
spells : : AbilityCaster caster ( attacker , spellLevel ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
spells : : Target target ;
target . emplace_back ( defender ) ;
spells : : BattleCast parameters ( gs - > curB , & caster , spells : : Mode : : PASSIVE , spell ) ;
auto m = spell - > battleMechanics ( & parameters ) ;
2021-02-20 03:57:50 +02:00
spells : : detail : : ProblemImpl ignored ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
2021-02-20 03:57:50 +02:00
if ( ! m - > canBeCastAt ( target , ignored ) )
2014-03-17 22:51:07 +03:00
continue ;
2015-09-21 11:19:35 +02:00
//check if spell should be cast (probability handling)
2017-07-20 06:08:49 +02:00
if ( getRandomGenerator ( ) . nextInt ( 99 ) > = chance )
2014-03-17 22:51:07 +03:00
continue ;
2014-11-25 21:00:04 +02:00
//casting
2017-07-20 06:08:49 +02:00
if ( castMe )
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
parameters . cast ( spellEnv , target ) ;
2014-11-25 21:00:04 +02:00
}
2014-03-17 22:51:07 +03:00
}
}
}
2017-07-20 06:08:49 +02:00
void CGameHandler : : handleAttackBeforeCasting ( bool ranged , const CStack * attacker , const CStack * defender )
2014-03-17 22:51:07 +03:00
{
2023-05-01 00:20:01 +02:00
attackCasting ( ranged , BonusType : : SPELL_BEFORE_ATTACK , attacker , defender ) ; //no death stare / acid breath needed?
2014-03-17 22:51:07 +03:00
}
2017-07-20 06:08:49 +02:00
void CGameHandler : : handleAfterAttackCasting ( bool ranged , const CStack * attacker , const CStack * defender )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
if ( ! attacker - > alive ( ) | | ! defender - > alive ( ) ) // can be already dead
2014-03-17 22:51:07 +03:00
return ;
2015-10-28 22:53:44 +02:00
2023-05-01 00:20:01 +02:00
attackCasting ( ranged , BonusType : : SPELL_AFTER_ATTACK , attacker , defender ) ;
2014-03-17 22:51:07 +03:00
2017-07-04 13:24:46 +02:00
if ( ! defender - > alive ( ) )
2014-03-17 22:51:07 +03:00
{
//don't try death stare or acid breath on dead stack (crash!)
return ;
}
2023-05-01 00:20:01 +02:00
if ( attacker - > hasBonusOfType ( BonusType : : DEATH_STARE ) )
2014-03-17 22:51:07 +03:00
{
// mechanics of Death Stare as in H3:
// each gorgon have 10% chance to kill (counted separately in H3) -> binomial distribution
//original formula x = min(x, (gorgons_count + 9)/10);
2023-05-01 00:20:01 +02:00
double chanceToKill = attacker - > valOfBonuses ( BonusType : : DEATH_STARE , 0 ) / 100.0f ;
2014-03-17 22:51:07 +03:00
vstd : : amin ( chanceToKill , 1 ) ; //cap at 100%
2017-07-04 13:24:46 +02:00
std : : binomial_distribution < > distribution ( attacker - > getCount ( ) , chanceToKill ) ;
2014-03-17 22:51:07 +03:00
2016-09-18 12:39:02 +02:00
int staredCreatures = distribution ( getRandomGenerator ( ) . getStdGenerator ( ) ) ;
2014-03-17 22:51:07 +03:00
double cap = 1 / std : : max ( chanceToKill , ( double ) ( 0.01 ) ) ; //don't divide by 0
2020-10-01 10:38:06 +02:00
int maxToKill = static_cast < int > ( ( attacker - > getCount ( ) + cap - 1 ) / cap ) ; //not much more than chance * count
2014-03-17 22:51:07 +03:00
vstd : : amin ( staredCreatures , maxToKill ) ;
2023-05-01 00:20:01 +02:00
staredCreatures + = ( attacker - > level ( ) * attacker - > valOfBonuses ( BonusType : : DEATH_STARE , 1 ) ) / defender - > level ( ) ;
2017-07-04 13:24:46 +02:00
if ( staredCreatures )
2014-03-17 22:51:07 +03:00
{
2017-07-04 13:24:46 +02:00
//TODO: death stare was not originally available for multiple-hex attacks, but...
2017-07-20 06:08:49 +02:00
const CSpell * spell = SpellID ( SpellID : : DEATH_STARE ) . toSpell ( ) ;
2018-03-02 12:22:51 +02:00
spells : : AbilityCaster caster ( attacker , 0 ) ;
spells : : BattleCast parameters ( gs - > curB , & caster , spells : : Mode : : PASSIVE , spell ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
spells : : Target target ;
target . emplace_back ( defender ) ;
2017-07-20 06:08:49 +02:00
parameters . setEffectValue ( staredCreatures ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
parameters . cast ( spellEnv , target ) ;
2014-03-17 22:51:07 +03:00
}
}
2017-07-04 13:24:46 +02:00
if ( ! defender - > alive ( ) )
return ;
2017-07-20 06:08:49 +02:00
int64_t acidDamage = 0 ;
2023-05-01 00:20:01 +02:00
TConstBonusListPtr acidBreath = attacker - > getBonuses ( Selector : : type ( ) ( BonusType : : ACID_BREATH ) ) ;
2020-10-01 07:55:41 +02:00
for ( const auto & b : * acidBreath )
2014-03-17 22:51:07 +03:00
{
2018-03-12 07:20:18 +02:00
if ( b - > additionalInfo [ 0 ] > getRandomGenerator ( ) . nextInt ( 99 ) )
2014-03-17 22:51:07 +03:00
acidDamage + = b - > val ;
}
2017-01-22 21:28:08 +02:00
2017-07-20 06:08:49 +02:00
if ( acidDamage > 0 )
{
const CSpell * spell = SpellID ( SpellID : : ACID_BREATH_DAMAGE ) . toSpell ( ) ;
2018-03-02 12:22:51 +02:00
spells : : AbilityCaster caster ( attacker , 0 ) ;
spells : : BattleCast parameters ( gs - > curB , & caster , spells : : Mode : : PASSIVE , spell ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
spells : : Target target ;
target . emplace_back ( defender ) ;
2017-07-20 06:08:49 +02:00
parameters . setEffectValue ( acidDamage * attacker - > getCount ( ) ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
parameters . cast ( spellEnv , target ) ;
2017-07-20 06:08:49 +02:00
}
2017-07-04 13:24:46 +02:00
if ( ! defender - > alive ( ) )
return ;
2023-05-01 00:20:01 +02:00
if ( attacker - > hasBonusOfType ( BonusType : : TRANSMUTATION ) & & defender - > isLiving ( ) ) //transmutation mechanics, similar to WoG werewolf ability
2017-01-22 21:28:08 +02:00
{
2023-05-01 00:20:01 +02:00
double chanceToTrigger = attacker - > valOfBonuses ( BonusType : : TRANSMUTATION ) / 100.0f ;
2017-01-22 21:28:08 +02:00
vstd : : amin ( chanceToTrigger , 1 ) ; //cap at 100%
2017-07-04 13:24:46 +02:00
if ( getRandomGenerator ( ) . getDoubleRange ( 0 , 1 ) ( ) > chanceToTrigger )
2017-01-22 21:28:08 +02:00
return ;
2017-03-18 12:25:12 +02:00
2023-05-01 00:20:01 +02:00
int bonusAdditionalInfo = attacker - > getBonus ( Selector : : type ( ) ( BonusType : : TRANSMUTATION ) ) - > additionalInfo [ 0 ] ;
2017-01-22 21:28:08 +02:00
2023-04-12 00:52:12 +02:00
if ( defender - > unitType ( ) - > getId ( ) = = bonusAdditionalInfo | |
( bonusAdditionalInfo = = CAddInfo : : NONE & & defender - > unitType ( ) - > getId ( ) = = attacker - > unitType ( ) - > getId ( ) ) )
2017-01-22 21:28:08 +02:00
return ;
2017-07-20 06:08:49 +02:00
battle : : UnitInfo resurrectInfo ;
resurrectInfo . id = gs - > curB - > battleNextUnitId ( ) ;
resurrectInfo . summoned = false ;
resurrectInfo . position = defender - > getPosition ( ) ;
resurrectInfo . side = defender - > unitSide ( ) ;
2017-01-22 21:28:08 +02:00
2018-03-12 07:20:18 +02:00
if ( bonusAdditionalInfo ! = CAddInfo : : NONE )
2017-07-20 06:08:49 +02:00
resurrectInfo . type = CreatureID ( bonusAdditionalInfo ) ;
2017-01-22 21:28:08 +02:00
else
2017-07-20 06:08:49 +02:00
resurrectInfo . type = attacker - > creatureId ( ) ;
2017-03-18 12:25:12 +02:00
2023-05-01 00:20:01 +02:00
if ( attacker - > hasBonusOfType ( ( BonusType : : TRANSMUTATION ) , 0 ) )
2023-05-01 19:29:53 +02:00
resurrectInfo . count = std : : max ( ( defender - > getCount ( ) * defender - > getMaxHealth ( ) ) / resurrectInfo . type . toCreature ( ) - > getMaxHealth ( ) , 1u ) ;
2023-05-01 00:20:01 +02:00
else if ( attacker - > hasBonusOfType ( ( BonusType : : TRANSMUTATION ) , 1 ) )
2017-07-20 06:08:49 +02:00
resurrectInfo . count = defender - > getCount ( ) ;
2017-01-22 21:28:08 +02:00
else
return ; //wrong subtype
2017-07-20 06:08:49 +02:00
BattleUnitsChanged addUnits ;
addUnits . changedStacks . emplace_back ( resurrectInfo . id , UnitChanges : : EOperation : : ADD ) ;
resurrectInfo . save ( addUnits . changedStacks . back ( ) . data ) ;
2017-01-22 21:28:08 +02:00
2017-07-20 06:08:49 +02:00
BattleUnitsChanged removeUnits ;
removeUnits . changedStacks . emplace_back ( defender - > unitId ( ) , UnitChanges : : EOperation : : REMOVE ) ;
sendAndApply ( & removeUnits ) ;
sendAndApply ( & addUnits ) ;
2017-01-22 21:28:08 +02:00
}
2017-07-20 06:08:49 +02:00
2023-05-01 00:20:01 +02:00
if ( attacker - > hasBonusOfType ( BonusType : : DESTRUCTION , 0 ) | | attacker - > hasBonusOfType ( BonusType : : DESTRUCTION , 1 ) )
2017-09-09 21:01:12 +02:00
{
double chanceToTrigger = 0 ;
int amountToDie = 0 ;
2023-05-01 00:20:01 +02:00
if ( attacker - > hasBonusOfType ( BonusType : : DESTRUCTION , 0 ) ) //killing by percentage
2017-09-09 21:01:12 +02:00
{
2023-05-01 00:20:01 +02:00
chanceToTrigger = attacker - > valOfBonuses ( BonusType : : DESTRUCTION , 0 ) / 100.0f ;
int percentageToDie = attacker - > getBonus ( Selector : : type ( ) ( BonusType : : DESTRUCTION ) . And ( Selector : : subtype ( ) ( 0 ) ) ) - > additionalInfo [ 0 ] ;
2020-10-01 10:38:06 +02:00
amountToDie = static_cast < int > ( defender - > getCount ( ) * percentageToDie * 0.01f ) ;
2017-09-09 21:01:12 +02:00
}
2023-05-01 00:20:01 +02:00
else if ( attacker - > hasBonusOfType ( BonusType : : DESTRUCTION , 1 ) ) //killing by count
2017-09-09 21:01:12 +02:00
{
2023-05-01 00:20:01 +02:00
chanceToTrigger = attacker - > valOfBonuses ( BonusType : : DESTRUCTION , 1 ) / 100.0f ;
amountToDie = attacker - > getBonus ( Selector : : type ( ) ( BonusType : : DESTRUCTION ) . And ( Selector : : subtype ( ) ( 1 ) ) ) - > additionalInfo [ 0 ] ;
2017-09-09 21:01:12 +02:00
}
vstd : : amin ( chanceToTrigger , 1 ) ; //cap trigger chance at 100%
if ( getRandomGenerator ( ) . getDoubleRange ( 0 , 1 ) ( ) > chanceToTrigger )
return ;
BattleStackAttacked bsa ;
bsa . attackerID = - 1 ;
2023-04-27 19:43:20 +02:00
bsa . stackAttacked = defender - > unitId ( ) ;
2023-05-01 19:29:53 +02:00
bsa . damageAmount = amountToDie * defender - > getMaxHealth ( ) ;
2017-09-09 21:01:12 +02:00
bsa . flags = BattleStackAttacked : : SPELL_EFFECT ;
bsa . spellID = SpellID : : SLAYER ;
2017-11-13 02:59:41 +02:00
defender - > prepareAttacked ( bsa , getRandomGenerator ( ) ) ;
2017-07-20 06:08:49 +02:00
StacksInjured si ;
si . stacks . push_back ( bsa ) ;
sendAndApply ( & si ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
sendGenericKilledLog ( defender , bsa . killedAmount , false ) ;
2017-09-09 21:01:12 +02:00
}
2014-03-17 22:51:07 +03:00
}
void CGameHandler : : visitObjectOnTile ( const TerrainTile & t , const CGHeroInstance * h )
{
if ( ! t . visitableObjects . empty ( ) )
{
//to prevent self-visiting heroes on space press
2016-10-12 17:16:26 +02:00
if ( t . visitableObjects . back ( ) ! = h )
2014-03-17 22:51:07 +03:00
objectVisited ( t . visitableObjects . back ( ) , h ) ;
2016-10-12 17:16:26 +02:00
else if ( t . visitableObjects . size ( ) > 1 )
2014-03-17 22:51:07 +03:00
objectVisited ( * ( t . visitableObjects . end ( ) - 2 ) , h ) ;
}
}
2017-10-28 11:04:55 +02:00
bool CGameHandler : : sacrificeCreatures ( const IMarket * market , const CGHeroInstance * hero , const std : : vector < SlotID > & slot , const std : : vector < ui32 > & count )
2014-03-17 22:51:07 +03:00
{
2016-11-27 21:50:37 +02:00
if ( ! hero )
COMPLAIN_RET ( " You need hero to sacrifice creature! " ) ;
2017-10-14 21:30:56 +02:00
int expSum = 0 ;
auto finish = [ this , & hero , & expSum ] ( )
{
changePrimSkill ( hero , PrimarySkill : : EXPERIENCE , hero - > calculateXp ( expSum ) ) ;
} ;
2017-10-28 11:04:55 +02:00
for ( int i = 0 ; i < slot . size ( ) ; + + i )
2017-10-14 21:30:56 +02:00
{
int oldCount = hero - > getStackCount ( slot [ i ] ) ;
2020-10-01 10:38:06 +02:00
if ( oldCount < ( int ) count [ i ] )
2017-10-14 21:30:56 +02:00
{
finish ( ) ;
COMPLAIN_RET ( " Not enough creatures to sacrifice! " )
}
2017-10-28 11:04:55 +02:00
else if ( oldCount = = count [ i ] & & hero - > stacksCount ( ) = = 1 & & hero - > needsLastStack ( ) )
2017-10-14 21:30:56 +02:00
{
finish ( ) ;
COMPLAIN_RET ( " Cannot sacrifice last creature! " ) ;
}
2014-03-17 22:51:07 +03:00
2023-04-05 02:26:29 +02:00
int crid = hero - > getStack ( slot [ i ] ) . type - > getId ( ) ;
2014-03-17 22:51:07 +03:00
2020-10-01 10:38:06 +02:00
changeStackCount ( StackLocation ( hero , slot [ i ] ) , - ( TQuantity ) count [ i ] ) ;
2014-03-17 22:51:07 +03:00
2017-10-14 21:30:56 +02:00
int dump , exp ;
market - > getOffer ( crid , 0 , dump , exp , EMarketMode : : CREATURE_EXP ) ;
exp * = count [ i ] ;
expSum + = exp ;
}
2014-03-17 22:51:07 +03:00
2017-10-14 21:30:56 +02:00
finish ( ) ;
2014-03-17 22:51:07 +03:00
return true ;
}
2017-10-28 11:04:55 +02:00
bool CGameHandler : : sacrificeArtifact ( const IMarket * m , const CGHeroInstance * hero , const std : : vector < ArtifactPosition > & slot )
2014-03-17 22:51:07 +03:00
{
2016-11-27 21:50:37 +02:00
if ( ! hero )
COMPLAIN_RET ( " You need hero to sacrifice artifact! " ) ;
2017-10-14 21:30:56 +02:00
int expSum = 0 ;
auto finish = [ this , & hero , & expSum ] ( )
{
2017-10-14 22:10:59 +02:00
changePrimSkill ( hero , PrimarySkill : : EXPERIENCE , hero - > calculateXp ( expSum ) ) ;
2017-10-14 21:30:56 +02:00
} ;
2017-10-28 11:04:55 +02:00
for ( int i = 0 ; i < slot . size ( ) ; + + i )
2017-10-14 21:30:56 +02:00
{
ArtifactLocation al ( hero , slot [ i ] ) ;
2017-10-28 11:04:55 +02:00
const CArtifactInstance * a = al . getArt ( ) ;
2017-10-14 21:30:56 +02:00
2017-10-28 11:04:55 +02:00
if ( ! a )
2017-10-14 21:30:56 +02:00
{
finish ( ) ;
COMPLAIN_RET ( " Cannot find artifact to sacrifice! " ) ;
}
const CArtifactInstance * art = hero - > getArt ( slot [ i ] ) ;
2017-10-28 11:04:55 +02:00
if ( ! art )
2017-10-14 21:30:56 +02:00
{
finish ( ) ;
COMPLAIN_RET ( " No artifact at position to sacrifice! " ) ;
}
2023-01-02 15:58:56 +02:00
si32 typId = art - > artType - > getId ( ) ;
2017-10-14 21:30:56 +02:00
int dmp , expToGive ;
2014-03-17 22:51:07 +03:00
2017-10-14 21:30:56 +02:00
m - > getOffer ( typId , 0 , dmp , expToGive , EMarketMode : : ARTIFACT_EXP ) ;
2014-03-17 22:51:07 +03:00
2017-10-14 21:30:56 +02:00
expSum + = expToGive ;
2016-11-26 21:06:12 +02:00
2017-10-14 21:30:56 +02:00
removeArtifact ( al ) ;
}
2016-11-26 21:06:12 +02:00
2017-10-14 21:30:56 +02:00
finish ( ) ;
2014-03-17 22:51:07 +03:00
return true ;
}
void CGameHandler : : makeStackDoNothing ( const CStack * next )
{
BattleAction doNothing ;
2017-07-20 06:08:49 +02:00
doNothing . actionType = EActionType : : NO_ACTION ;
2023-04-27 19:43:20 +02:00
doNothing . side = next - > unitSide ( ) ;
doNothing . stackNumber = next - > unitId ( ) ;
2014-03-17 22:51:07 +03:00
makeAutomaticAction ( next , doNothing ) ;
}
bool CGameHandler : : insertNewStack ( const StackLocation & sl , const CCreature * c , TQuantity count )
{
2016-10-12 17:16:26 +02:00
if ( sl . army - > hasStackAtSlot ( sl . slot ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Slot is already taken! " ) ;
2016-10-12 17:16:26 +02:00
if ( ! sl . slot . validSlot ( ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Cannot insert stack to that slot! " ) ;
InsertNewStack ins ;
2018-03-10 23:19:36 +02:00
ins . army = sl . army - > id ;
ins . slot = sl . slot ;
2023-04-05 02:26:29 +02:00
ins . type = c - > getId ( ) ;
2018-03-10 23:19:36 +02:00
ins . count = count ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & ins ) ;
return true ;
}
2017-07-15 13:08:20 +02:00
bool CGameHandler : : eraseStack ( const StackLocation & sl , bool forceRemoval )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ! sl . army - > hasStackAtSlot ( sl . slot ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Cannot find a stack to erase " ) ;
2016-10-12 17:16:26 +02:00
if ( sl . army - > stacksCount ( ) = = 1 //from the last stack
2014-03-17 22:51:07 +03:00
& & sl . army - > needsLastStack ( ) //that must be left
& & ! forceRemoval ) //ignore above conditions if we are forcing removal
{
COMPLAIN_RET ( " Cannot erase the last stack! " ) ;
}
EraseStack es ;
2018-03-10 23:19:36 +02:00
es . army = sl . army - > id ;
es . slot = sl . slot ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & es ) ;
return true ;
}
2017-07-15 13:08:20 +02:00
bool CGameHandler : : changeStackCount ( const StackLocation & sl , TQuantity count , bool absoluteValue )
2014-03-17 22:51:07 +03:00
{
TQuantity currentCount = sl . army - > getStackCount ( sl . slot ) ;
2016-10-12 17:16:26 +02:00
if ( ( absoluteValue & & count < 0 )
2014-03-17 22:51:07 +03:00
| | ( ! absoluteValue & & - count > currentCount ) )
{
COMPLAIN_RET ( " Cannot take more stacks than present! " ) ;
}
2016-10-12 17:16:26 +02:00
if ( ( currentCount = = - count & & ! absoluteValue )
2014-03-17 22:51:07 +03:00
| | ( ! count & & absoluteValue ) )
{
eraseStack ( sl ) ;
}
else
{
ChangeStackCount csc ;
2018-03-10 23:19:36 +02:00
csc . army = sl . army - > id ;
csc . slot = sl . slot ;
2014-03-17 22:51:07 +03:00
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 ) ;
2016-10-12 17:16:26 +02:00
if ( ! slotC ) //slot is empty
2014-03-17 22:51:07 +03:00
insertNewStack ( sl , c , count ) ;
2016-10-12 17:16:26 +02:00
else if ( c = = slotC )
2014-03-17 22:51:07 +03:00
changeStackCount ( sl , count ) ;
else
{
2023-01-02 18:00:51 +02:00
COMPLAIN_RET ( " Cannot add " + c - > getNamePluralTranslated ( ) + " to slot " + boost : : lexical_cast < std : : string > ( sl . slot ) + " ! " ) ;
2014-03-17 22:51:07 +03:00
}
return true ;
}
void CGameHandler : : tryJoiningArmy ( const CArmedInstance * src , const CArmedInstance * dst , bool removeObjWhenFinished , bool allowMerging )
{
2016-10-12 17:16:26 +02:00
if ( removeObjWhenFinished )
2014-03-17 22:51:07 +03:00
removeAfterVisit ( src ) ;
2016-10-12 17:16:26 +02:00
if ( ! src - > canBeMergedWith ( * dst , allowMerging ) )
2014-03-17 22:51:07 +03:00
{
if ( allowMerging ) //do that, add all matching creatures.
{
bool cont = true ;
while ( cont )
{
2016-10-12 17:16:26 +02:00
for ( auto i = src - > stacks . begin ( ) ; i ! = src - > stacks . end ( ) ; i + + ) //while there are unmoved creatures
2014-03-17 22:51:07 +03:00
{
SlotID pos = dst - > getSlotFor ( i - > second - > type ) ;
2016-10-12 17:16:26 +02:00
if ( pos . validSlot ( ) )
2014-03-17 22:51:07 +03:00
{
moveStack ( StackLocation ( src , i - > first ) , StackLocation ( dst , pos ) ) ;
cont = true ;
break ; //or iterator crashes
}
cont = false ;
}
}
}
showGarrisonDialog ( src - > id , dst - > id , true ) ; //show garrison window and optionally remove ourselves from map when player ends
}
else //merge
{
moveArmy ( src , dst , allowMerging ) ;
}
}
bool CGameHandler : : moveStack ( const StackLocation & src , const StackLocation & dst , TQuantity count )
{
2016-10-12 17:16:26 +02:00
if ( ! src . army - > hasStackAtSlot ( src . slot ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " No stack to move! " ) ;
2016-10-12 17:16:26 +02:00
if ( dst . army - > hasStackAtSlot ( dst . slot ) & & dst . army - > getCreature ( dst . slot ) ! = src . army - > getCreature ( src . slot ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Cannot move: stack of different type at destination pos! " ) ;
2016-10-12 17:16:26 +02:00
if ( ! dst . slot . validSlot ( ) )
2014-03-17 22:51:07 +03:00
COMPLAIN_RET ( " Cannot move stack to that slot! " ) ;
2016-10-12 17:16:26 +02:00
if ( count = = - 1 )
2014-03-17 22:51:07 +03:00
{
count = src . army - > getStackCount ( src . slot ) ;
}
2016-10-12 17:16:26 +02:00
if ( src . army ! = dst . army //moving away
2014-03-17 22:51:07 +03:00
& & count = = src . army - > getStackCount ( src . slot ) //all creatures
2015-12-24 20:30:57 +02:00
& & src . army - > stacksCount ( ) = = 1 //from the last stack
2014-03-17 22:51:07 +03:00
& & src . army - > needsLastStack ( ) ) //that must be left
{
COMPLAIN_RET ( " Cannot move away the last creature! " ) ;
}
RebalanceStacks rs ;
2018-03-10 23:19:36 +02:00
rs . srcArmy = src . army - > id ;
rs . dstArmy = dst . army - > id ;
rs . srcSlot = src . slot ;
rs . dstSlot = dst . slot ;
2014-03-17 22:51:07 +03:00
rs . count = count ;
sendAndApply ( & rs ) ;
return true ;
}
2023-04-10 03:40:03 +02:00
void CGameHandler : : castSpell ( const spells : : Caster * caster , SpellID spellID , const int3 & pos )
2023-04-10 02:34:24 +02:00
{
const CSpell * s = spellID . toSpell ( ) ;
if ( ! s )
return ;
AdventureSpellCastParameters p ;
2023-04-10 03:40:03 +02:00
p . caster = caster ;
2023-04-10 02:34:24 +02:00
p . pos = pos ;
s - > adventureCast ( spellEnv , p ) ;
}
2018-03-10 23:19:36 +02:00
bool CGameHandler : : swapStacks ( const StackLocation & sl1 , const StackLocation & sl2 )
2014-03-17 22:51:07 +03:00
{
2018-03-10 23:19:36 +02:00
if ( ! sl1 . army - > hasStackAtSlot ( sl1 . slot ) )
{
2014-03-17 22:51:07 +03:00
return moveStack ( sl2 , sl1 ) ;
2018-03-10 23:19:36 +02:00
}
else if ( ! sl2 . army - > hasStackAtSlot ( sl2 . slot ) )
{
2014-03-17 22:51:07 +03:00
return moveStack ( sl1 , sl2 ) ;
2018-03-10 23:19:36 +02:00
}
2014-03-17 22:51:07 +03:00
else
{
SwapStacks ss ;
2018-03-10 23:19:36 +02:00
ss . srcArmy = sl1 . army - > id ;
ss . dstArmy = sl2 . army - > id ;
ss . srcSlot = sl1 . slot ;
ss . dstSlot = sl2 . slot ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & ss ) ;
return true ;
}
}
2016-10-29 13:45:08 +02:00
void CGameHandler : : runBattle ( )
{
2014-03-17 22:51:07 +03:00
setBattle ( gs - > curB ) ;
assert ( gs - > curB ) ;
//TODO: pre-tactic stuff, call scripts etc.
2023-03-20 13:02:09 +02:00
//Moat should be initialized here, because only here we can use spellcasting
if ( gs - > curB - > town & & gs - > curB - > town - > fortLevel ( ) > = CGTownInstance : : CITADEL )
{
const auto * h = gs - > curB - > battleGetFightingHero ( BattleSide : : DEFENDER ) ;
const auto * actualCaster = h ? static_cast < const spells : : Caster * > ( h ) : nullptr ;
auto moatCaster = spells : : SilentCaster ( gs - > curB - > getSidePlayer ( BattleSide : : DEFENDER ) , actualCaster ) ;
auto cast = spells : : BattleCast ( gs - > curB , & moatCaster , spells : : Mode : : PASSIVE , gs - > curB - > town - > town - > moatAbility . toSpell ( ) ) ;
auto target = spells : : Target ( ) ;
cast . cast ( spellEnv , target ) ;
}
2014-03-17 22:51:07 +03:00
//tactic round
{
2023-03-25 21:29:33 +02:00
while ( ( lobby - > state ! = EServerState : : SHUTDOWN ) & & gs - > curB - > tacticDistance & & ! battleResult . get ( ) )
2014-03-17 22:51:07 +03:00
boost : : this_thread : : sleep ( boost : : posix_time : : milliseconds ( 50 ) ) ;
}
2016-10-28 23:37:45 +02:00
//initial stacks appearance triggers, e.g. built-in bonus spells
2017-01-29 12:50:37 +02:00
auto initialStacks = gs - > curB - > stacks ; //use temporary variable to outclude summoned stacks added to gs->curB->stacks from processing
2017-01-26 21:53:28 +02:00
for ( CStack * stack : initialStacks )
2016-10-28 23:37:45 +02:00
{
2023-05-01 00:20:01 +02:00
if ( stack - > hasBonusOfType ( BonusType : : SUMMON_GUARDIANS ) )
2017-01-26 21:53:28 +02:00
{
2023-05-01 00:20:01 +02:00
std : : shared_ptr < const Bonus > summonInfo = stack - > getBonus ( Selector : : type ( ) ( BonusType : : SUMMON_GUARDIANS ) ) ;
2017-01-26 21:53:28 +02:00
auto accessibility = getAccesibility ( ) ;
CreatureID creatureData = CreatureID ( summonInfo - > subtype ) ;
std : : vector < BattleHex > targetHexes ;
2023-04-12 00:52:12 +02:00
const bool targetIsBig = stack - > unitType ( ) - > isDoubleWide ( ) ; //target = creature to guard
2017-03-18 12:25:12 +02:00
const bool guardianIsBig = creatureData . toCreature ( ) - > isDoubleWide ( ) ;
2017-01-26 21:53:28 +02:00
2017-01-29 12:50:37 +02:00
/*Chosen idea for two hex units was to cover all possible surrounding hexes of target unit with as small number of stacks as possible.
For one - hex targets there are four guardians - front , back and one per side ( up + down ) .
Two - hex targets are wider and the difference is there are two guardians per side to cover 3 hexes + extra hex in the front
Additionally , there are special cases for starting positions etc . , where guardians would be outside of battlefield if spawned normally */
2017-03-18 12:25:12 +02:00
if ( ! guardianIsBig )
2017-01-26 21:53:28 +02:00
targetHexes = stack - > getSurroundingHexes ( ) ;
else
2023-04-27 19:43:20 +02:00
summonGuardiansHelper ( targetHexes , stack - > getPosition ( ) , stack - > unitSide ( ) , targetIsBig ) ;
2017-03-18 12:25:12 +02:00
2017-07-20 06:08:49 +02:00
for ( auto hex : targetHexes )
2017-01-26 21:53:28 +02:00
{
2023-04-27 19:43:20 +02:00
if ( accessibility . accessible ( hex , guardianIsBig , stack - > unitSide ( ) ) ) //without this multiple creatures can occupy one hex
2017-01-26 21:53:28 +02:00
{
2017-07-20 06:08:49 +02:00
battle : : UnitInfo info ;
info . id = gs - > curB - > battleNextUnitId ( ) ;
info . count = std : : max ( 1 , ( int ) ( stack - > getCount ( ) * 0.01 * summonInfo - > val ) ) ;
info . type = creatureData ;
2023-04-27 19:43:20 +02:00
info . side = stack - > unitSide ( ) ;
2017-07-20 06:08:49 +02:00
info . position = hex ;
info . summoned = true ;
BattleUnitsChanged pack ;
pack . changedStacks . emplace_back ( info . id , UnitChanges : : EOperation : : ADD ) ;
info . save ( pack . changedStacks . back ( ) . data ) ;
sendAndApply ( & pack ) ;
2017-01-26 21:53:28 +02:00
}
}
}
2017-01-26 22:24:01 +02:00
2016-11-02 19:11:01 +02:00
stackEnchantedTrigger ( stack ) ;
2016-10-28 23:37:45 +02:00
}
2014-03-17 22:51:07 +03:00
//spells opening battle
2016-10-12 17:16:26 +02:00
for ( int i = 0 ; i < 2 ; + + i )
2014-03-17 22:51:07 +03:00
{
auto h = gs - > curB - > battleGetFightingHero ( i ) ;
2016-10-12 17:16:26 +02:00
if ( h )
2014-03-17 22:51:07 +03:00
{
2023-05-01 00:20:01 +02:00
TConstBonusListPtr bl = h - > getBonuses ( Selector : : type ( ) ( BonusType : : OPENING_BATTLE_SPELL ) ) ;
2015-10-28 22:53:44 +02:00
2016-09-19 23:36:35 +02:00
for ( auto b : * bl )
2014-03-17 22:51:07 +03:00
{
2018-03-02 12:22:51 +02:00
spells : : BonusCaster caster ( h , b ) ;
2018-02-10 07:37:15 +02:00
2014-11-25 21:00:04 +02:00
const CSpell * spell = SpellID ( b - > subtype ) . toSpell ( ) ;
2016-09-18 16:21:56 +02:00
2018-02-10 07:37:15 +02:00
spells : : BattleCast parameters ( gs - > curB , & caster , spells : : Mode : : PASSIVE , spell ) ;
2017-07-20 06:08:49 +02:00
parameters . setSpellLevel ( 3 ) ;
parameters . setEffectDuration ( b - > val ) ;
2018-02-10 06:31:02 +02:00
parameters . massive = true ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
parameters . castIfPossible ( spellEnv , spells : : Target ( ) ) ;
2014-03-17 22:51:07 +03:00
}
}
}
2022-12-02 21:38:59 +02:00
// it is possible that due to opening spells one side was eliminated -> check for end of battle
checkBattleStateChanges ( ) ;
2014-03-17 22:51:07 +03:00
2017-06-13 14:30:21 +02:00
bool firstRound = true ; //FIXME: why first round is -1?
2014-03-17 22:51:07 +03:00
//main loop
2023-03-25 21:29:33 +02:00
while ( ( lobby - > state ! = EServerState : : SHUTDOWN ) & & ! battleResult . get ( ) ) //till the end of the battle ;]
2014-03-17 22:51:07 +03:00
{
2016-09-29 11:13:06 +02:00
BattleNextRound bnr ;
bnr . round = gs - > curB - > round + 1 ;
2017-06-13 14:30:21 +02:00
logGlobal - > debug ( " Round %d " , bnr . round ) ;
2016-09-29 11:13:06 +02:00
sendAndApply ( & bnr ) ;
2014-03-17 22:51:07 +03:00
auto obstacles = gs - > curB - > obstacles ; //we copy container, because we're going to modify it
2016-10-12 17:16:26 +02:00
for ( auto & obstPtr : obstacles )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( const SpellCreatedObstacle * sco = dynamic_cast < const SpellCreatedObstacle * > ( obstPtr . get ( ) ) )
if ( sco - > turnsRemaining = = 0 )
2014-03-17 22:51:07 +03:00
removeObstacle ( * obstPtr ) ;
}
const BattleInfo & curB = * gs - > curB ;
2016-11-02 19:11:01 +02:00
for ( auto stack : curB . stacks )
{
2017-06-13 14:30:21 +02:00
if ( stack - > alive ( ) & & ! firstRound )
2016-11-02 19:11:01 +02:00
stackEnchantedTrigger ( stack ) ;
}
2014-03-17 22:51:07 +03:00
//stack loop
2017-07-20 06:08:49 +02:00
auto getNextStack = [ this ] ( ) - > const CStack *
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
if ( battleResult . get ( ) )
return nullptr ;
std : : vector < battle : : Units > q ;
gs - > curB - > battleGetTurnOrder ( q , 1 , 0 , - 1 ) ; //todo: get rid of "turn -1"
if ( ! q . empty ( ) )
2016-03-01 10:07:45 +02:00
{
2017-07-20 06:08:49 +02:00
if ( ! q . front ( ) . empty ( ) )
{
auto next = q . front ( ) . front ( ) ;
2022-10-03 14:45:33 +02:00
const auto stack = dynamic_cast < const CStack * > ( next ) ;
// regeneration takes place before everything else but only during first turn attempt in each round
// also works under blind and similar effects
if ( stack & & stack - > alive ( ) & & ! stack - > waiting )
{
BattleTriggerEffect bte ;
2023-04-27 19:43:20 +02:00
bte . stackID = stack - > unitId ( ) ;
2023-05-01 00:20:01 +02:00
bte . effect = vstd : : to_underlying ( BonusType : : HP_REGENERATION ) ;
2022-10-03 14:45:33 +02:00
2023-05-01 19:29:53 +02:00
const int32_t lostHealth = stack - > getMaxHealth ( ) - stack - > getFirstHPleft ( ) ;
2023-05-01 00:20:01 +02:00
if ( stack - > hasBonusOfType ( BonusType : : HP_REGENERATION ) )
bte . val = std : : min ( lostHealth , stack - > valOfBonuses ( BonusType : : HP_REGENERATION ) ) ;
2022-10-03 14:45:33 +02:00
if ( bte . val ) // anything to heal
sendAndApply ( & bte ) ;
}
2017-07-20 06:08:49 +02:00
if ( next - > willMove ( ) )
2022-10-03 14:45:33 +02:00
return stack ;
2017-07-20 06:08:49 +02:00
}
2016-03-01 10:07:45 +02:00
}
2017-07-20 06:08:49 +02:00
return nullptr ;
} ;
const CStack * next = nullptr ;
2023-03-25 21:29:33 +02:00
while ( ( lobby - > state ! = EServerState : : SHUTDOWN ) & & ( next = getNextStack ( ) ) )
2017-07-20 06:08:49 +02:00
{
BattleUnitsChanged removeGhosts ;
for ( auto stack : curB . stacks )
2016-03-01 10:07:45 +02:00
{
2017-07-20 06:08:49 +02:00
if ( stack - > ghostPending )
removeGhosts . changedStacks . emplace_back ( stack - > unitId ( ) , UnitChanges : : EOperation : : REMOVE ) ;
2016-03-01 10:07:45 +02:00
}
2017-07-20 06:08:49 +02:00
if ( ! removeGhosts . changedStacks . empty ( ) )
sendAndApply ( & removeGhosts ) ;
2023-04-20 12:08:55 +02:00
// check for bad morale => freeze
2023-05-02 00:05:59 +02:00
int nextStackMorale = next - > moraleVal ( ) ;
2023-04-20 12:08:55 +02:00
if ( ! next - > hadMorale & & ! next - > waited ( ) & & nextStackMorale < 0 )
2014-03-17 22:51:07 +03:00
{
2023-03-15 23:47:26 +02:00
auto diceSize = VLC - > settings ( ) - > getVector ( EGameSettings : : COMBAT_BAD_MORALE_DICE ) ;
size_t diceIndex = std : : min < size_t > ( diceSize . size ( ) - 1 , - nextStackMorale ) ;
if ( diceSize . size ( ) > 0 & & getRandomGenerator ( ) . nextInt ( 1 , diceSize [ diceIndex ] ) = = 1 )
2014-03-17 22:51:07 +03:00
{
//unit loses its turn - empty freeze action
BattleAction ba ;
2017-07-20 06:08:49 +02:00
ba . actionType = EActionType : : BAD_MORALE ;
2023-04-27 19:43:20 +02:00
ba . side = next - > unitSide ( ) ;
ba . stackNumber = next - > unitId ( ) ;
2014-03-17 22:51:07 +03:00
makeAutomaticAction ( next , ba ) ;
continue ;
}
}
2023-05-01 00:20:01 +02:00
if ( next - > hasBonusOfType ( BonusType : : ATTACKS_NEAREST_CREATURE ) ) //while in berserk
2016-09-29 13:11:15 +02:00
{
2017-07-20 06:08:49 +02:00
logGlobal - > trace ( " Handle Berserk effect " ) ;
std : : pair < const battle : : Unit * , BattleHex > attackInfo = curB . getNearestStack ( next ) ;
2016-10-12 17:16:26 +02:00
if ( attackInfo . first ! = nullptr )
2014-03-17 22:51:07 +03:00
{
BattleAction attack ;
2017-07-20 06:08:49 +02:00
attack . actionType = EActionType : : WALK_AND_ATTACK ;
2023-04-27 19:43:20 +02:00
attack . side = next - > unitSide ( ) ;
attack . stackNumber = next - > unitId ( ) ;
2017-07-20 06:08:49 +02:00
attack . aimToHex ( attackInfo . second ) ;
attack . aimToUnit ( attackInfo . first ) ;
2014-03-17 22:51:07 +03:00
makeAutomaticAction ( next , attack ) ;
2017-07-20 06:08:49 +02:00
logGlobal - > trace ( " Attacked nearest target %s " , attackInfo . first - > getDescription ( ) ) ;
2014-03-17 22:51:07 +03:00
}
else
{
makeStackDoNothing ( next ) ;
2017-07-20 06:08:49 +02:00
logGlobal - > trace ( " No target found " ) ;
2014-03-17 22:51:07 +03:00
}
continue ;
}
2016-09-29 22:14:22 +02:00
const CGHeroInstance * curOwner = battleGetOwnerHero ( next ) ;
2023-04-12 00:52:12 +02:00
const int stackCreatureId = next - > unitType ( ) - > getId ( ) ;
2014-03-17 22:51:07 +03:00
2017-09-04 14:30:43 +02:00
if ( ( stackCreatureId = = CreatureID : : ARROW_TOWERS | | stackCreatureId = = CreatureID : : BALLISTA )
2023-05-01 00:20:01 +02:00
& & ( ! curOwner | | getRandomGenerator ( ) . nextInt ( 99 ) > = curOwner - > valOfBonuses ( BonusType : : MANUAL_CONTROL , stackCreatureId ) ) )
2014-03-17 22:51:07 +03:00
{
BattleAction attack ;
2017-07-20 06:08:49 +02:00
attack . actionType = EActionType : : SHOOT ;
2023-04-27 19:43:20 +02:00
attack . side = next - > unitSide ( ) ;
attack . stackNumber = next - > unitId ( ) ;
2014-03-17 22:51:07 +03:00
2017-07-20 06:08:49 +02:00
//TODO: select target by priority
const battle : : Unit * target = nullptr ;
for ( auto & elem : gs - > curB - > stacks )
2014-03-17 22:51:07 +03:00
{
2023-04-12 00:52:12 +02:00
if ( elem - > unitType ( ) - > getId ( ) ! = CreatureID : : CATAPULT
2023-04-27 19:43:20 +02:00
& & elem - > unitOwner ( ) ! = next - > unitOwner ( )
2020-11-27 20:51:04 +02:00
& & elem - > isValidTarget ( )
& & gs - > curB - > battleCanShoot ( next , elem - > getPosition ( ) ) )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
target = elem ;
2014-03-17 22:51:07 +03:00
break ;
}
}
2017-07-20 06:08:49 +02:00
if ( target = = nullptr )
{
makeStackDoNothing ( next ) ;
}
else
{
attack . aimToUnit ( target ) ;
makeAutomaticAction ( next , attack ) ;
}
2014-03-17 22:51:07 +03:00
continue ;
}
2023-04-12 00:52:12 +02:00
if ( next - > unitType ( ) - > getId ( ) = = CreatureID : : CATAPULT )
2014-03-17 22:51:07 +03:00
{
const auto & attackableBattleHexes = curB . getAttackableBattleHexes ( ) ;
2016-10-12 17:16:26 +02:00
if ( attackableBattleHexes . empty ( ) )
2015-08-27 23:03:48 +02:00
{
makeStackDoNothing ( next ) ;
continue ;
}
2023-05-01 00:20:01 +02:00
if ( ! curOwner | | getRandomGenerator ( ) . nextInt ( 99 ) > = curOwner - > valOfBonuses ( BonusType : : MANUAL_CONTROL , CreatureID : : CATAPULT ) )
2014-03-17 22:51:07 +03:00
{
BattleAction attack ;
2017-07-20 06:08:49 +02:00
attack . actionType = EActionType : : CATAPULT ;
2023-04-27 19:43:20 +02:00
attack . side = next - > unitSide ( ) ;
attack . stackNumber = next - > unitId ( ) ;
2014-03-17 22:51:07 +03:00
makeAutomaticAction ( next , attack ) ;
2015-08-26 20:54:24 +02:00
continue ;
}
}
2023-04-12 00:52:12 +02:00
if ( next - > unitType ( ) - > getId ( ) = = CreatureID : : FIRST_AID_TENT )
2014-03-17 22:51:07 +03:00
{
2016-02-28 04:10:20 +02:00
TStacks possibleStacks = battleGetStacksIf ( [ = ] ( const CStack * s )
{
2023-04-27 19:43:20 +02:00
return s - > unitOwner ( ) = = next - > unitOwner ( ) & & s - > canBeHealed ( ) ;
2014-05-17 13:36:49 +03:00
} ) ;
2014-03-17 22:51:07 +03:00
2016-10-12 17:16:26 +02:00
if ( ! possibleStacks . size ( ) )
2014-03-17 22:51:07 +03:00
{
makeStackDoNothing ( next ) ;
continue ;
}
2023-05-01 00:20:01 +02:00
if ( ! curOwner | | getRandomGenerator ( ) . nextInt ( 99 ) > = curOwner - > valOfBonuses ( BonusType : : MANUAL_CONTROL , CreatureID : : FIRST_AID_TENT ) )
2014-03-17 22:51:07 +03:00
{
2016-09-09 21:11:13 +02:00
RandomGeneratorUtil : : randomShuffle ( possibleStacks , getRandomGenerator ( ) ) ;
2014-03-17 22:51:07 +03:00
const CStack * toBeHealed = possibleStacks . front ( ) ;
BattleAction heal ;
2017-07-20 06:08:49 +02:00
heal . actionType = EActionType : : STACK_HEAL ;
heal . aimToUnit ( toBeHealed ) ;
2023-04-27 19:43:20 +02:00
heal . side = next - > unitSide ( ) ;
heal . stackNumber = next - > unitId ( ) ;
2014-03-17 22:51:07 +03:00
makeAutomaticAction ( next , heal ) ;
continue ;
}
}
int numberOfAsks = 1 ;
bool breakOuter = false ;
do
{ //ask interface and wait for answer
2016-10-12 17:16:26 +02:00
if ( ! battleResult . get ( ) )
2014-03-17 22:51:07 +03:00
{
stackTurnTrigger ( next ) ; //various effects
2017-07-20 06:08:49 +02:00
if ( next - > fear )
2014-03-17 22:51:07 +03:00
{
makeStackDoNothing ( next ) ; //end immediately if stack was affected by fear
}
else
{
2016-08-30 00:11:54 +02:00
logGlobal - > trace ( " Activating %s " , next - > nodeName ( ) ) ;
2023-04-27 19:43:20 +02:00
auto nextId = next - > unitId ( ) ;
2015-10-28 22:53:44 +02:00
BattleSetActiveStack sas ;
2015-10-06 01:04:25 +02:00
sas . stack = nextId ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & sas ) ;
2015-10-05 20:09:00 +02:00
auto actionWasMade = [ & ] ( ) - > bool
{
2016-10-12 17:16:26 +02:00
if ( battleMadeAction . data ) //active stack has made its action
2015-10-05 20:09:00 +02:00
return true ;
2016-10-12 17:16:26 +02:00
if ( battleResult . get ( ) ) // battle is finished
2015-10-05 20:09:00 +02:00
return true ;
2016-10-12 17:16:26 +02:00
if ( next = = nullptr ) //active stack was been removed
2015-10-06 02:46:35 +02:00
return true ;
2015-10-05 20:09:00 +02:00
return ! next - > alive ( ) ; //active stack is dead
} ;
2014-03-17 22:51:07 +03:00
boost : : unique_lock < boost : : mutex > lock ( battleMadeAction . mx ) ;
battleMadeAction . data = false ;
2023-03-25 21:29:33 +02:00
while ( ( lobby - > state ! = EServerState : : SHUTDOWN ) & & ! actionWasMade ( ) )
2015-10-05 20:09:00 +02:00
{
2014-03-17 22:51:07 +03:00
battleMadeAction . cond . wait ( lock ) ;
2016-10-12 17:16:26 +02:00
if ( battleGetStackByID ( nextId , false ) ! = next )
2015-10-06 01:04:25 +02:00
next = nullptr ; //it may be removed, while we wait
2015-10-05 20:09:00 +02:00
}
2014-03-17 22:51:07 +03:00
}
}
2016-10-12 17:16:26 +02:00
if ( battleResult . get ( ) ) //don't touch it, battle could be finished while waiting got action
2014-03-17 22:51:07 +03:00
{
breakOuter = true ;
break ;
}
//we're after action, all results applied
2016-02-09 09:59:33 +02:00
checkBattleStateChanges ( ) ; //check if this action ended the battle
2014-03-17 22:51:07 +03:00
2017-07-20 06:08:49 +02:00
if ( next ! = nullptr )
2014-03-17 22:51:07 +03:00
{
2015-10-06 01:04:25 +02:00
//check for good morale
2023-05-02 00:05:59 +02:00
nextStackMorale = next - > moraleVal ( ) ;
2023-04-20 12:10:03 +02:00
if ( ! battleResult . get ( )
& & ! next - > hadMorale
2017-07-20 06:08:49 +02:00
& & ! next - > defending
2015-10-06 01:04:25 +02:00
& & ! next - > waited ( )
2017-07-20 06:08:49 +02:00
& & ! next - > fear
2023-04-20 12:10:03 +02:00
& & next - > alive ( )
& & nextStackMorale > 0 )
2015-10-06 01:04:25 +02:00
{
2023-03-15 23:47:26 +02:00
auto diceSize = VLC - > settings ( ) - > getVector ( EGameSettings : : COMBAT_GOOD_MORALE_DICE ) ;
size_t diceIndex = std : : min < size_t > ( diceSize . size ( ) - 1 , nextStackMorale ) ;
if ( diceSize . size ( ) > 0 & & getRandomGenerator ( ) . nextInt ( 1 , diceSize [ diceIndex ] ) = = 1 )
2016-10-28 23:37:45 +02:00
{
BattleTriggerEffect bte ;
2023-04-27 19:43:20 +02:00
bte . stackID = next - > unitId ( ) ;
2023-05-01 00:20:01 +02:00
bte . effect = vstd : : to_underlying ( BonusType : : MORALE ) ;
2016-10-28 23:37:45 +02:00
bte . val = 1 ;
bte . additionalInfo = 0 ;
sendAndApply ( & bte ) ; //play animation
+ + numberOfAsks ; //move this stack once more
}
2015-10-06 01:04:25 +02:00
}
2014-03-17 22:51:07 +03:00
}
- - numberOfAsks ;
} while ( numberOfAsks > 0 ) ;
if ( breakOuter )
{
break ;
}
}
2017-06-13 14:30:21 +02:00
firstRound = false ;
2014-03-17 22:51:07 +03:00
}
2023-03-25 21:29:33 +02:00
if ( lobby - > state ! = EServerState : : SHUTDOWN )
endBattle ( gs - > curB - > tile , gs - > curB - > battleGetFightingHero ( 0 ) , gs - > curB - > battleGetFightingHero ( 1 ) ) ;
2014-03-17 22:51:07 +03:00
}
bool CGameHandler : : makeAutomaticAction ( const CStack * stack , BattleAction & ba )
{
BattleSetActiveStack bsa ;
2023-04-27 19:43:20 +02:00
bsa . stack = stack - > unitId ( ) ;
2014-03-17 22:51:07 +03:00
bsa . askPlayerInterface = false ;
sendAndApply ( & bsa ) ;
bool ret = makeBattleAction ( ba ) ;
2016-02-09 09:59:33 +02:00
checkBattleStateChanges ( ) ;
2014-03-17 22:51:07 +03:00
return ret ;
}
2023-03-18 22:58:39 +02:00
bool CGameHandler : : giveHeroArtifact ( const CGHeroInstance * h , const CArtifactInstance * a , ArtifactPosition pos )
2014-03-17 22:51:07 +03:00
{
assert ( a - > artType ) ;
2023-03-18 22:58:39 +02:00
ArtifactLocation al ( h , ArtifactPosition : : PRE_FIRST ) ;
2014-03-17 22:51:07 +03:00
2023-03-18 22:58:39 +02:00
if ( pos = = ArtifactPosition : : FIRST_AVAILABLE )
2014-03-17 22:51:07 +03:00
{
2023-03-21 12:13:53 +02:00
al . slot = ArtifactUtils : : getArtAnyPosition ( h , a - > getTypeId ( ) ) ;
2014-03-17 22:51:07 +03:00
}
2023-03-21 12:13:53 +02:00
else if ( ArtifactUtils : : isSlotBackpack ( pos ) )
2014-03-17 22:51:07 +03:00
{
2023-03-21 12:13:53 +02:00
al . slot = ArtifactUtils : : getArtBackpackPosition ( h , a - > getTypeId ( ) ) ;
2014-03-17 22:51:07 +03:00
}
else
{
2023-03-21 12:13:53 +02:00
al . slot = pos ;
2014-03-17 22:51:07 +03:00
}
2023-03-21 12:13:53 +02:00
if ( a - > canBePutAt ( al ) )
2023-03-18 22:58:39 +02:00
putArtifact ( al , a ) ;
else
return false ;
2023-03-20 01:18:32 +02:00
return true ;
2014-03-17 22:51:07 +03:00
}
2023-03-18 22:58:39 +02:00
2014-03-17 22:51:07 +03:00
void CGameHandler : : putArtifact ( const ArtifactLocation & al , const CArtifactInstance * a )
{
PutArtifact pa ;
pa . art = a ;
pa . al = al ;
sendAndApply ( & pa ) ;
}
2023-03-20 01:18:32 +02:00
bool CGameHandler : : giveHeroNewArtifact ( const CGHeroInstance * h , const CArtifact * artType , ArtifactPosition pos )
2017-05-26 18:51:45 +02:00
{
2023-03-20 01:18:32 +02:00
assert ( artType ) ;
2023-05-09 15:48:52 +02:00
if ( pos = = ArtifactPosition : : FIRST_AVAILABLE )
{
if ( ! artType - > canBePutAt ( h , ArtifactUtils : : getArtAnyPosition ( h , artType - > getId ( ) ) ) )
COMPLAIN_RET ( " Cannot put artifact in that slot! " ) ;
}
else if ( ArtifactUtils : : isSlotBackpack ( pos ) )
{
if ( ! artType - > canBePutAt ( h , ArtifactUtils : : getArtBackpackPosition ( h , artType - > getId ( ) ) ) )
COMPLAIN_RET ( " Cannot put artifact in that slot! " ) ;
}
else
{
2023-03-20 01:18:32 +02:00
COMPLAIN_RET_FALSE_IF ( ! artType - > canBePutAt ( h , pos , false ) , " Cannot put artifact in that slot! " ) ;
2023-05-09 15:48:52 +02:00
}
2017-05-26 18:51:45 +02:00
2023-03-20 01:18:32 +02:00
CArtifactInstance * newArtInst = nullptr ;
if ( artType - > canBeDisassembled ( ) )
newArtInst = new CCombinedArtifactInstance ( ) ;
else
newArtInst = new CArtifactInstance ( ) ;
2017-05-26 18:51:45 +02:00
2023-03-20 01:18:32 +02:00
newArtInst - > artType = artType ; // *NOT* via settype -> all bonus-related stuff must be done by NewArtifact apply
2023-05-09 15:48:52 +02:00
NewArtifact na ;
na . art = newArtInst ;
sendAndApply ( & na ) ; // -> updates a!!!, will create a on other machines
2023-03-20 01:18:32 +02:00
if ( giveHeroArtifact ( h , newArtInst , pos ) )
return true ;
2014-03-17 22:51:07 +03:00
else
2023-03-20 01:18:32 +02:00
return false ;
2014-03-17 22:51:07 +03:00
}
void CGameHandler : : setBattleResult ( BattleResult : : EResult resultType , int victoriusSide )
{
2016-01-25 14:36:34 +02:00
boost : : unique_lock < boost : : mutex > guard ( battleResult . mx ) ;
2016-10-12 17:16:26 +02:00
if ( battleResult . data )
2014-03-17 22:51:07 +03:00
{
2016-01-25 14:36:34 +02:00
complain ( ( boost : : format ( " The battle result has been already set (to %d, asked to %d) " )
% battleResult . data - > result % resultType ) . str ( ) ) ;
2014-03-17 22:51:07 +03:00
return ;
}
2017-07-16 11:58:05 +02:00
auto br = new BattleResult ( ) ;
2014-03-17 22:51:07 +03:00
br - > result = resultType ;
br - > winner = victoriusSide ; //surrendering side loses
gs - > curB - > calculateCasualties ( br - > casualties ) ;
2016-01-25 14:36:34 +02:00
battleResult . data = br ;
2014-03-17 22:51:07 +03:00
}
void CGameHandler : : spawnWanderingMonsters ( CreatureID creatureID )
{
std : : vector < int3 > : : iterator tile ;
std : : vector < int3 > tiles ;
getFreeTiles ( tiles ) ;
2020-10-01 10:38:06 +02:00
ui32 amount = ( ui32 ) tiles . size ( ) / 200 ; //Chance is 0.5% for each tile
2016-09-09 21:11:13 +02:00
RandomGeneratorUtil : : randomShuffle ( tiles , getRandomGenerator ( ) ) ;
2016-08-30 00:11:54 +02:00
logGlobal - > trace ( " Spawning wandering monsters. Found %d free tiles. Creature type: %d " , tiles . size ( ) , creatureID . num ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const CCreature * cre = VLC - > creh - > objects . at ( creatureID ) ;
2020-10-01 10:38:06 +02:00
for ( int i = 0 ; i < ( int ) amount ; + + i )
2014-03-17 22:51:07 +03:00
{
tile = tiles . begin ( ) ;
2017-08-12 13:36:04 +02:00
logGlobal - > trace ( " \t Spawning monster at %s " , tile - > toString ( ) ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
{
auto count = cre - > getRandomAmount ( std : : rand ) ;
auto monsterId = putNewObject ( Obj : : MONSTER , creatureID , * tile ) ;
setObjProperty ( monsterId , ObjProperty : : MONSTER_COUNT , count ) ;
setObjProperty ( monsterId , ObjProperty : : MONSTER_POWER , ( si64 ) 1000 * count ) ;
}
2014-03-17 22:51:07 +03:00
tiles . erase ( tile ) ; //not use it again
}
}
2016-10-02 15:21:46 +02:00
void CGameHandler : : handleCheatCode ( std : : string & cheat , PlayerColor player , const CGHeroInstance * hero , const CGTownInstance * town , bool & cheated )
{
2023-05-20 01:14:06 +02:00
//Make cheat case-insensitive
std : : transform ( cheat . begin ( ) , cheat . end ( ) , cheat . begin ( ) , [ ] ( unsigned char c ) { return std : : tolower ( c ) ; } ) ;
2023-01-23 19:56:00 +02:00
if ( cheat = = " vcmiistari " | | cheat = = " vcmispells " )
2016-10-02 15:21:46 +02:00
{
2022-09-30 21:08:37 +02:00
cheated = true ;
2016-10-12 17:16:26 +02:00
if ( ! hero ) return ;
2016-10-02 15:21:46 +02:00
///Give hero spellbook
2016-10-12 17:16:26 +02:00
if ( ! hero - > hasSpellbook ( ) )
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
giveHeroNewArtifact ( hero , VLC - > arth - > objects [ ArtifactID : : SPELLBOOK ] , ArtifactPosition : : SPELLBOOK ) ;
2016-10-02 15:21:46 +02:00
///Give all spells with bonus (to allow banned spells)
2023-03-28 13:46:53 +02:00
GiveBonus giveBonus ( GiveBonus : : ETarget : : HERO ) ;
2016-10-02 15:21:46 +02:00
giveBonus . id = hero - > id . getNum ( ) ;
2023-05-01 00:20:01 +02:00
giveBonus . bonus = Bonus ( BonusDuration : : PERMANENT , BonusType : : SPELLS_OF_LEVEL , BonusSource : : OTHER , 0 , 0 ) ;
2016-10-02 15:21:46 +02:00
//start with level 0 to skip abilities
2016-10-12 17:16:26 +02:00
for ( int level = 1 ; level < = GameConstants : : SPELL_LEVELS ; level + + )
2016-10-02 15:21:46 +02:00
{
giveBonus . bonus . subtype = level ;
sendAndApply ( & giveBonus ) ;
}
///Give mana
SetMana sm ;
sm . hid = hero - > id ;
sm . val = 999 ;
sm . absolute = true ;
sendAndApply ( & sm ) ;
}
2023-01-23 19:56:00 +02:00
else if ( cheat = = " vcmiarmenelos " | | cheat = = " vcmibuild " )
2016-10-02 15:21:46 +02:00
{
2022-09-30 21:08:37 +02:00
cheated = true ;
2016-10-12 17:16:26 +02:00
if ( ! town ) return ;
2016-10-02 15:21:46 +02:00
///Build all buildings in selected town
2016-10-12 17:16:26 +02:00
for ( auto & build : town - > town - > buildings )
2016-10-02 15:21:46 +02:00
{
2016-10-12 17:16:26 +02:00
if ( ! town - > hasBuilt ( build . first )
2023-01-04 15:17:50 +02:00
& & ! build . second - > getNameTranslated ( ) . empty ( )
2016-10-02 15:21:46 +02:00
& & build . first ! = BuildingID : : SHIP )
{
buildStructure ( town - > id , build . first , true ) ;
}
}
}
2023-01-23 19:56:00 +02:00
else if ( cheat = = " vcmiainur " | | cheat = = " vcmiangband " | | cheat = = " vcmiglaurung " | | cheat = = " vcmiarchangel "
| | cheat = = " vcmiblackknight " | | cheat = = " vcmicrystal " | | cheat = = " vcmiazure " | | cheat = = " vcmifaerie " )
2016-10-02 15:21:46 +02:00
{
2022-09-30 21:08:37 +02:00
cheated = true ;
2016-10-12 17:16:26 +02:00
if ( ! hero ) return ;
2016-10-02 15:21:46 +02:00
///Gives N creatures into each slot
2023-01-25 13:10:48 +02:00
std : : map < std : : string , std : : pair < std : : string , int > > creatures ;
creatures . insert ( std : : make_pair ( " vcmiainur " , std : : make_pair ( " archangel " , 5 ) ) ) ; //5 archangels
creatures . insert ( std : : make_pair ( " vcmiangband " , std : : make_pair ( " blackKnight " , 10 ) ) ) ; //10 black knights
creatures . insert ( std : : make_pair ( " vcmiglaurung " , std : : make_pair ( " crystalDragon " , 5000 ) ) ) ; //5000 crystal dragons
creatures . insert ( std : : make_pair ( " vcmiarchangel " , std : : make_pair ( " archangel " , 5 ) ) ) ; //5 archangels
creatures . insert ( std : : make_pair ( " vcmiblackknight " , std : : make_pair ( " blackKnight " , 10 ) ) ) ; //10 black knights
creatures . insert ( std : : make_pair ( " vcmicrystal " , std : : make_pair ( " crystalDragon " , 5000 ) ) ) ; //5000 crystal dragons
creatures . insert ( std : : make_pair ( " vcmiazure " , std : : make_pair ( " azureDragon " , 5000 ) ) ) ; //5000 azure dragons
creatures . insert ( std : : make_pair ( " vcmifaerie " , std : : make_pair ( " fairieDragon " , 5000 ) ) ) ; //5000 faerie dragons
2023-04-16 19:42:56 +02:00
const int32_t creatureIdentifier = VLC - > modh - > identifiers . getIdentifier ( CModHandler : : scopeGame ( ) , " creature " , creatures [ cheat ] . first , false ) . value ( ) ;
2023-01-25 13:10:48 +02:00
const CCreature * creature = VLC - > creh - > objects . at ( creatureIdentifier ) ;
2016-10-12 17:16:26 +02:00
for ( int i = 0 ; i < GameConstants : : ARMY_SIZE ; i + + )
if ( ! hero - > hasStackAtSlot ( SlotID ( i ) ) )
2016-10-02 15:21:46 +02:00
insertNewStack ( StackLocation ( hero , SlotID ( i ) ) , creature , creatures [ cheat ] . second ) ;
}
2023-03-31 23:08:51 +02:00
else if ( boost : : starts_with ( cheat , " vcmiarmy " ) | | boost : : starts_with ( cheat , " vcminissi " ) )
2023-01-23 22:18:43 +02:00
{
cheated = true ;
if ( ! hero ) return ;
std : : vector < std : : string > words ;
boost : : split ( words , cheat , boost : : is_any_of ( " " ) ) ;
if ( words . size ( ) < 2 )
return ;
std : : string creatureIdentifier = words [ 1 ] ;
2023-04-16 19:42:56 +02:00
std : : optional < int32_t > creatureId = VLC - > modh - > identifiers . getIdentifier ( CModHandler : : scopeGame ( ) , " creature " , creatureIdentifier , false ) ;
2023-01-23 22:18:43 +02:00
2023-04-16 19:42:56 +02:00
if ( creatureId . has_value ( ) )
2023-01-23 22:18:43 +02:00
{
2023-04-16 19:42:56 +02:00
const auto * creature = CreatureID ( creatureId . value ( ) ) . toCreature ( ) ;
2023-01-25 13:50:18 +02:00
2023-01-23 22:18:43 +02:00
for ( int i = 0 ; i < GameConstants : : ARMY_SIZE ; i + + )
if ( ! hero - > hasStackAtSlot ( SlotID ( i ) ) )
2023-01-25 13:50:18 +02:00
insertNewStack ( StackLocation ( hero , SlotID ( i ) ) , creature , 5 * std : : pow ( 10 , i ) ) ;
2023-01-23 22:18:43 +02:00
}
}
2023-01-23 19:56:00 +02:00
else if ( cheat = = " vcminoldor " | | cheat = = " vcmimachines " )
2016-10-02 15:21:46 +02:00
{
2022-09-30 21:08:37 +02:00
cheated = true ;
2016-10-12 17:16:26 +02:00
if ( ! hero ) return ;
2016-10-02 15:21:46 +02:00
///Give all war machines to hero
2016-10-12 17:16:26 +02:00
if ( ! hero - > getArt ( ArtifactPosition : : MACH1 ) )
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
giveHeroNewArtifact ( hero , VLC - > arth - > objects [ ArtifactID : : BALLISTA ] , ArtifactPosition : : MACH1 ) ;
2016-10-12 17:16:26 +02:00
if ( ! hero - > getArt ( ArtifactPosition : : MACH2 ) )
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
giveHeroNewArtifact ( hero , VLC - > arth - > objects [ ArtifactID : : AMMO_CART ] , ArtifactPosition : : MACH2 ) ;
2016-10-12 17:16:26 +02:00
if ( ! hero - > getArt ( ArtifactPosition : : MACH3 ) )
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
giveHeroNewArtifact ( hero , VLC - > arth - > objects [ ArtifactID : : FIRST_AID_TENT ] , ArtifactPosition : : MACH3 ) ;
2016-10-02 15:21:46 +02:00
}
2023-01-23 19:56:00 +02:00
else if ( cheat = = " vcmiforgeofnoldorking " | | cheat = = " vcmiartifacts " )
2016-10-02 15:21:46 +02:00
{
2022-09-30 21:08:37 +02:00
cheated = true ;
2016-10-12 17:16:26 +02:00
if ( ! hero ) return ;
2016-10-02 15:21:46 +02:00
///Give hero all artifacts except war machines, spell scrolls and spell book
2023-03-20 01:18:32 +02:00
for ( int g = 7 ; g < VLC - > arth - > objects . size ( ) ; + + g ) //including artifacts from mods
{
2023-03-21 12:13:53 +02:00
if ( VLC - > arth - > objects [ g ] - > canBePutAt ( hero ) )
2023-03-20 01:18:32 +02:00
giveHeroNewArtifact ( hero , VLC - > arth - > objects [ g ] , ArtifactPosition : : FIRST_AVAILABLE ) ;
}
2016-10-02 15:21:46 +02:00
}
2023-01-23 19:56:00 +02:00
else if ( cheat = = " vcmiglorfindel " | | cheat = = " vcmilevel " )
2016-10-02 15:21:46 +02:00
{
2022-09-30 21:08:37 +02:00
cheated = true ;
2016-10-12 17:16:26 +02:00
if ( ! hero ) return ;
2016-10-02 15:21:46 +02:00
///selected hero gains a new level
changePrimSkill ( hero , PrimarySkill : : EXPERIENCE , VLC - > heroh - > reqExp ( hero - > level + 1 ) - VLC - > heroh - > reqExp ( hero - > level ) ) ;
}
2023-04-01 11:29:49 +02:00
else if ( boost : : starts_with ( cheat , " vcmiexp " ) | | boost : : starts_with ( cheat , " vcmiolorin " ) )
2023-01-24 00:12:41 +02:00
{
cheated = true ;
if ( ! hero ) return ;
std : : vector < std : : string > words ;
boost : : split ( words , cheat , boost : : is_any_of ( " " ) ) ;
if ( words . size ( ) < 2 )
return ;
std : : string expAmount = words [ 1 ] ;
long expAmountProcessed = 0 ;
try
{
expAmountProcessed = std : : stol ( expAmount ) ;
}
catch ( std : : exception & )
{
logGlobal - > error ( " Could not parse experience amount for vcmiexp cheat " ) ;
}
if ( expAmountProcessed > 1 )
{
changePrimSkill ( hero , PrimarySkill : : EXPERIENCE , expAmountProcessed ) ;
}
}
2023-01-23 19:56:00 +02:00
else if ( cheat = = " vcminahar " | | cheat = = " vcmimove " )
2016-10-02 15:21:46 +02:00
{
2022-09-30 21:08:37 +02:00
cheated = true ;
2016-10-12 17:16:26 +02:00
if ( ! hero ) return ;
2016-10-02 15:21:46 +02:00
///Give 1000000 movement points to hero
SetMovePoints smp ;
smp . hid = hero - > id ;
smp . val = 1000000 ;
sendAndApply ( & smp ) ;
2017-06-14 10:41:36 +02:00
2023-03-28 13:46:53 +02:00
GiveBonus gb ( GiveBonus : : ETarget : : HERO ) ;
2023-05-01 00:20:01 +02:00
gb . bonus . type = BonusType : : FREE_SHIP_BOARDING ;
gb . bonus . duration = BonusDuration : : ONE_DAY ;
gb . bonus . source = BonusSource : : OTHER ;
2017-06-14 10:41:36 +02:00
gb . id = hero - > id . getNum ( ) ;
giveHeroBonus ( & gb ) ;
2016-10-02 15:21:46 +02:00
}
2023-01-23 19:56:00 +02:00
else if ( cheat = = " vcmiformenos " | | cheat = = " vcmiresources " )
2016-10-02 15:21:46 +02:00
{
2022-09-30 21:08:37 +02:00
cheated = true ;
2016-10-02 15:21:46 +02:00
///Give resources to player
TResources resources ;
2023-04-05 02:26:29 +02:00
resources [ EGameResID : : GOLD ] = 100000 ;
2023-04-27 23:29:16 +02:00
for ( GameResID i = EGameResID : : WOOD ; i < EGameResID : : GOLD ; + + i )
2016-10-02 15:21:46 +02:00
resources [ i ] = 100 ;
giveResources ( player , resources ) ;
}
2023-01-23 19:56:00 +02:00
else if ( cheat = = " vcmisilmaril " | | cheat = = " vcmiwin " )
2016-10-02 15:21:46 +02:00
{
2022-09-30 21:08:37 +02:00
cheated = true ;
2016-10-02 15:21:46 +02:00
///Player wins
2017-06-02 02:34:50 +02:00
PlayerCheated pc ;
pc . player = player ;
pc . winningCheatCode = true ;
sendAndApply ( & pc ) ;
2016-10-02 15:21:46 +02:00
}
2023-01-23 19:56:00 +02:00
else if ( cheat = = " vcmimelkor " | | cheat = = " vcmilose " )
2016-10-02 15:21:46 +02:00
{
2022-09-30 21:08:37 +02:00
cheated = true ;
2016-10-02 15:21:46 +02:00
///Player looses
2017-06-02 02:34:50 +02:00
PlayerCheated pc ;
pc . player = player ;
pc . losingCheatCode = true ;
sendAndApply ( & pc ) ;
2016-10-02 15:21:46 +02:00
}
2023-01-23 19:56:00 +02:00
else if ( cheat = = " vcmieagles " | | cheat = = " vcmiungoliant " | | cheat = = " vcmimap " | | cheat = = " vcmihidemap " )
2016-10-02 15:21:46 +02:00
{
2022-09-30 21:08:37 +02:00
cheated = true ;
2016-10-02 15:21:46 +02:00
///Reveal or conceal FoW
FoWChange fc ;
2023-01-23 19:56:00 +02:00
fc . mode = ( ( cheat = = " vcmieagles " | | cheat = = " vcmimap " ) ? 1 : 0 ) ;
2016-10-02 15:21:46 +02:00
fc . player = player ;
const auto & fowMap = gs - > getPlayerTeam ( player ) - > fogOfWarMap ;
2022-09-18 16:39:10 +02:00
auto hlp_tab = new int3 [ gs - > map - > width * gs - > map - > height * ( gs - > map - > levels ( ) ) ] ;
2016-10-02 15:21:46 +02:00
int lastUnc = 0 ;
2022-09-18 16:39:10 +02:00
for ( int z = 0 ; z < gs - > map - > levels ( ) ; z + + )
for ( int x = 0 ; x < gs - > map - > width ; x + + )
for ( int y = 0 ; y < gs - > map - > height ; y + + )
if ( ! ( * fowMap ) [ z ] [ x ] [ y ] | | ! fc . mode )
hlp_tab [ lastUnc + + ] = int3 ( x , y , z ) ;
2016-10-02 15:21:46 +02:00
fc . tiles . insert ( hlp_tab , hlp_tab + lastUnc ) ;
delete [ ] hlp_tab ;
sendAndApply ( & fc ) ;
}
}
2017-07-20 06:08:49 +02:00
void CGameHandler : : removeObstacle ( const CObstacleInstance & obstacle )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
BattleObstaclesChanged obsRem ;
2022-12-17 19:37:00 +02:00
obsRem . changes . emplace_back ( obstacle . uniqueID , ObstacleChanges : : EOperation : : REMOVE ) ;
2014-03-17 22:51:07 +03:00
sendAndApply ( & obsRem ) ;
}
void CGameHandler : : synchronizeArtifactHandlerLists ( )
{
UpdateArtHandlerLists uahl ;
uahl . treasures = VLC - > arth - > treasures ;
uahl . minors = VLC - > arth - > minors ;
uahl . majors = VLC - > arth - > majors ;
uahl . relics = VLC - > arth - > relics ;
sendAndApply ( & uahl ) ;
}
bool CGameHandler : : isValidObject ( const CGObjectInstance * obj ) const
{
return vstd : : contains ( gs - > map - > objects , obj ) ;
}
bool CGameHandler : : isBlockedByQueries ( const CPack * pack , PlayerColor player )
{
2016-10-12 17:16:26 +02:00
if ( ! strcmp ( typeid ( * pack ) . name ( ) , typeid ( PlayerMessage ) . name ( ) ) )
2014-03-17 22:51:07 +03:00
return false ;
auto query = queries . topQuery ( player ) ;
2016-10-12 17:16:26 +02:00
if ( query & & query - > blocksPack ( pack ) )
2014-03-17 22:51:07 +03:00
{
2021-11-28 14:57:38 +02:00
complain ( boost : : str ( boost : : format (
" \r \n | Player \" %s \" has to answer queries before attempting any further actions. \r \n | Top Query: \" %s \" \r \n " )
% boost : : to_upper_copy < std : : string > ( player . getStr ( ) )
% query - > toString ( )
) ) ;
2014-03-17 22:51:07 +03:00
return true ;
}
return false ;
}
void CGameHandler : : removeAfterVisit ( const CGObjectInstance * object )
{
//If the object is being visited, there must be a matching query
2016-10-12 17:16:26 +02:00
for ( const auto & query : queries . allQueries ( ) )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( auto someVistQuery = std : : dynamic_pointer_cast < CObjectVisitQuery > ( query ) )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( someVistQuery - > visitedObject = = object )
2014-03-17 22:51:07 +03:00
{
someVistQuery - > removeObjectAfterVisit = true ;
return ;
}
}
2017-07-12 21:01:10 +02:00
}
2014-03-17 22:51:07 +03:00
//If we haven't returned so far, there is no query and no visit, call was wrong
assert ( " This function needs to be called during the object visit! " ) ;
}
2014-06-24 14:50:27 +03:00
void CGameHandler : : changeFogOfWar ( int3 center , ui32 radius , PlayerColor player , bool hide )
{
2023-04-16 00:48:49 +02:00
std : : unordered_set < int3 > tiles ;
2014-06-24 14:50:27 +03:00
getTilesInRange ( tiles , center , radius , player , hide ? - 1 : 1 ) ;
2014-07-01 10:13:19 +03:00
if ( hide )
{
2023-04-16 00:48:49 +02:00
std : : unordered_set < int3 > observedTiles ; //do not hide tiles observed by heroes. May lead to disastrous AI problems
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
auto p = getPlayerState ( player ) ;
2014-07-01 10:13:19 +03:00
for ( auto h : p - > heroes )
{
2016-01-31 17:01:58 +02:00
getTilesInRange ( observedTiles , h - > getSightCenter ( ) , h - > getSightRadius ( ) , h - > tempOwner , - 1 ) ;
2014-07-01 10:13:19 +03:00
}
for ( auto t : p - > towns )
{
2016-01-31 17:01:58 +02:00
getTilesInRange ( observedTiles , t - > getSightCenter ( ) , t - > getSightRadius ( ) , t - > tempOwner , - 1 ) ;
2014-07-01 10:13:19 +03:00
}
for ( auto tile : observedTiles )
vstd : : erase_if_present ( tiles , tile ) ;
}
2014-06-24 14:50:27 +03:00
changeFogOfWar ( tiles , player , hide ) ;
}
2023-04-16 00:48:49 +02:00
void CGameHandler : : changeFogOfWar ( std : : unordered_set < int3 > & tiles , PlayerColor player , bool hide )
2014-06-24 14:50:27 +03:00
{
FoWChange fow ;
fow . tiles = tiles ;
fow . player = player ;
fow . mode = hide ? 0 : 1 ;
sendAndApply ( & fow ) ;
}
2014-03-17 22:51:07 +03:00
bool CGameHandler : : isVisitCoveredByAnotherQuery ( const CGObjectInstance * obj , const CGHeroInstance * hero )
{
2016-10-12 17:16:26 +02:00
if ( auto topQuery = queries . topQuery ( hero - > getOwner ( ) ) )
if ( auto visit = std : : dynamic_pointer_cast < const CObjectVisitQuery > ( topQuery ) )
2014-03-17 22:51:07 +03:00
return ! ( visit - > visitedObject = = obj & & visit - > visitingHero = = hero ) ;
return true ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
void CGameHandler : : setObjProperty ( ObjectInstanceID objid , int prop , si64 val )
{
SetObjectProperty sob ;
sob . id = objid ;
sob . what = prop ;
sob . val = static_cast < ui32 > ( val ) ;
sendAndApply ( & sob ) ;
}
void CGameHandler : : showInfoDialog ( InfoWindow * iw )
{
sendAndApply ( iw ) ;
}
void CGameHandler : : showInfoDialog ( const std : : string & msg , PlayerColor player )
{
InfoWindow iw ;
iw . player = player ;
iw . text < < msg ;
showInfoDialog ( & iw ) ;
}
2023-04-06 17:34:07 +02:00
CasualtiesAfterBattle : : CasualtiesAfterBattle ( const CArmedInstance * _army , const BattleInfo * bat ) :
2015-11-07 10:42:06 +02:00
army ( _army )
2014-03-17 22:51:07 +03:00
{
heroWithDeadCommander = ObjectInstanceID ( ) ;
PlayerColor color = army - > tempOwner ;
2017-07-04 13:24:46 +02:00
if ( color = = PlayerColor : : UNFLAGGABLE )
2014-03-17 22:51:07 +03:00
color = PlayerColor : : NEUTRAL ;
2017-07-04 13:24:46 +02:00
for ( CStack * st : bat - > stacks )
2014-03-17 22:51:07 +03:00
{
2017-07-20 06:08:49 +02:00
if ( st - > summoned ) //don't take into account temporary summoned stacks
2014-03-17 22:51:07 +03:00
continue ;
2023-04-27 19:43:20 +02:00
if ( st - > unitOwner ( ) ! = color ) //remove only our stacks
2014-03-17 22:51:07 +03:00
continue ;
2016-08-30 00:11:54 +02:00
logGlobal - > debug ( " Calculating casualties for %s " , st - > nodeName ( ) ) ;
2016-01-29 23:53:53 +02:00
2017-07-04 13:24:46 +02:00
st - > health . takeResurrected ( ) ;
2014-03-17 22:51:07 +03:00
2023-04-27 19:43:20 +02:00
if ( st - > unitSlot ( ) = = SlotID : : ARROW_TOWERS_SLOT )
2016-01-29 23:53:53 +02:00
{
2016-03-01 03:06:32 +02:00
logGlobal - > debug ( " Ignored arrow towers stack. " ) ;
2016-01-29 23:53:53 +02:00
}
2023-04-27 19:43:20 +02:00
else if ( st - > unitSlot ( ) = = SlotID : : WAR_MACHINES_SLOT )
2014-03-17 22:51:07 +03:00
{
2023-04-27 19:43:20 +02:00
auto warMachine = st - > unitType ( ) - > warMachine ;
2016-01-29 23:53:53 +02:00
2017-07-04 13:24:46 +02:00
if ( warMachine = = ArtifactID : : NONE )
2017-05-26 18:51:45 +02:00
{
2016-08-30 00:11:54 +02:00
logGlobal - > error ( " Invalid creature in war machine virtual slot. Stack: %s " , st - > nodeName ( ) ) ;
2017-05-26 18:51:45 +02:00
}
2015-12-10 12:31:03 +02:00
//catapult artifact remain even if "creature" killed in siege
2017-07-04 13:24:46 +02:00
else if ( warMachine ! = ArtifactID : : CATAPULT & & st - > getCount ( ) < = 0 )
2014-03-17 22:51:07 +03:00
{
2016-03-01 03:06:32 +02:00
logGlobal - > debug ( " War machine has been destroyed " ) ;
2015-10-31 17:04:06 +02:00
auto hero = dynamic_ptr_cast < CGHeroInstance > ( army ) ;
2014-03-17 22:51:07 +03:00
if ( hero )
removedWarMachines . push_back ( ArtifactLocation ( hero , hero - > getArtPos ( warMachine , true ) ) ) ;
2016-01-29 23:53:53 +02:00
else
2016-03-01 03:06:32 +02:00
logGlobal - > error ( " War machine in army without hero " ) ;
2014-03-17 22:51:07 +03:00
}
}
2023-04-27 19:43:20 +02:00
else if ( st - > unitSlot ( ) = = SlotID : : SUMMONED_SLOT_PLACEHOLDER )
2014-03-17 22:51:07 +03:00
{
2017-07-04 13:24:46 +02:00
if ( st - > alive ( ) & & st - > getCount ( ) > 0 )
2015-11-07 10:42:06 +02:00
{
2017-07-04 13:24:46 +02:00
logGlobal - > debug ( " Permanently summoned %d units. " , st - > getCount ( ) ) ;
2023-04-27 19:43:20 +02:00
const CreatureID summonedType = st - > creatureId ( ) ;
2017-07-04 13:24:46 +02:00
summoned [ summonedType ] + = st - > getCount ( ) ;
2016-01-29 23:53:53 +02:00
}
2014-03-17 22:51:07 +03:00
}
2023-04-27 19:43:20 +02:00
else if ( st - > unitSlot ( ) = = SlotID : : COMMANDER_SLOT_PLACEHOLDER )
2016-03-01 03:06:32 +02:00
{
2016-10-12 17:16:26 +02:00
if ( nullptr = = st - > base )
2016-03-01 03:06:32 +02:00
{
2016-08-30 00:11:54 +02:00
logGlobal - > error ( " Stack with no base in commander slot. Stack: %s " , st - > nodeName ( ) ) ;
2016-03-01 03:06:32 +02:00
}
else
{
auto c = dynamic_cast < const CCommanderInstance * > ( st - > base ) ;
2017-07-04 13:24:46 +02:00
if ( c )
2016-03-01 03:06:32 +02:00
{
auto h = dynamic_cast < const CGHeroInstance * > ( army ) ;
2017-07-04 13:24:46 +02:00
if ( h & & h - > commander = = c & & ( st - > getCount ( ) = = 0 | | ! st - > alive ( ) ) )
2016-03-01 03:06:32 +02:00
{
logGlobal - > debug ( " Commander is dead. " ) ;
heroWithDeadCommander = army - > id ; //TODO: unify commander handling
}
}
else
2016-08-30 00:11:54 +02:00
logGlobal - > error ( " Stack with invalid instance in commander slot. Stack: %s " , st - > nodeName ( ) ) ;
2016-03-01 03:06:32 +02:00
}
}
2023-04-27 19:43:20 +02:00
else if ( st - > base & & ! army - > slotEmpty ( st - > unitSlot ( ) ) )
2014-03-17 22:51:07 +03:00
{
2023-04-27 19:43:20 +02:00
logGlobal - > debug ( " Count: %d; base count: %d " , st - > getCount ( ) , army - > getStackCount ( st - > unitSlot ( ) ) ) ;
2017-07-04 13:24:46 +02:00
if ( st - > getCount ( ) = = 0 | | ! st - > alive ( ) )
2014-03-17 22:51:07 +03:00
{
2016-03-01 03:06:32 +02:00
logGlobal - > debug ( " Stack has been destroyed. " ) ;
2023-04-27 19:43:20 +02:00
StackLocation sl ( army , st - > unitSlot ( ) ) ;
2016-03-01 03:06:32 +02:00
newStackCounts . push_back ( TStackAndItsNewCount ( sl , 0 ) ) ;
2014-03-17 22:51:07 +03:00
}
2023-04-27 19:43:20 +02:00
else if ( st - > getCount ( ) < army - > getStackCount ( st - > unitSlot ( ) ) )
2016-01-29 23:53:53 +02:00
{
2023-04-27 19:43:20 +02:00
logGlobal - > debug ( " Stack lost %d units. " , army - > getStackCount ( st - > unitSlot ( ) ) - st - > getCount ( ) ) ;
StackLocation sl ( army , st - > unitSlot ( ) ) ;
2017-07-04 13:24:46 +02:00
newStackCounts . push_back ( TStackAndItsNewCount ( sl , st - > getCount ( ) ) ) ;
2014-03-17 22:51:07 +03:00
}
2023-04-27 19:43:20 +02:00
else if ( st - > getCount ( ) > army - > getStackCount ( st - > unitSlot ( ) ) )
2017-01-20 16:48:45 +02:00
{
2023-04-27 19:43:20 +02:00
logGlobal - > debug ( " Stack gained %d units. " , st - > getCount ( ) - army - > getStackCount ( st - > unitSlot ( ) ) ) ;
StackLocation sl ( army , st - > unitSlot ( ) ) ;
2017-07-04 13:24:46 +02:00
newStackCounts . push_back ( TStackAndItsNewCount ( sl , st - > getCount ( ) ) ) ;
2017-01-20 16:48:45 +02:00
}
2014-03-17 22:51:07 +03:00
}
2016-01-29 23:53:53 +02:00
else
{
2016-08-30 00:11:54 +02:00
logGlobal - > warn ( " Unable to process stack: %s " , st - > nodeName ( ) ) ;
2014-03-17 22:51:07 +03:00
}
}
}
2015-11-07 10:42:06 +02:00
void CasualtiesAfterBattle : : updateArmy ( CGameHandler * gh )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
for ( TStackAndItsNewCount & ncount : newStackCounts )
2014-03-17 22:51:07 +03:00
{
2016-10-12 17:16:26 +02:00
if ( ncount . second > 0 )
2014-03-17 22:51:07 +03:00
gh - > changeStackCount ( ncount . first , ncount . second , true ) ;
else
gh - > eraseStack ( ncount . first , true ) ;
}
2016-10-12 17:16:26 +02:00
for ( auto summoned_iter : summoned )
2015-11-07 10:42:06 +02:00
{
SlotID slot = army - > getSlotFor ( summoned_iter . first ) ;
2016-10-12 17:16:26 +02:00
if ( slot . validSlot ( ) )
2015-11-07 10:42:06 +02:00
{
StackLocation location ( army , slot ) ;
gh - > addToSlot ( location , summoned_iter . first . toCreature ( ) , summoned_iter . second ) ;
}
else
{
//even if it will be possible to summon anything permanently it should be checked for free slot
//necromancy is handled separately
gh - > complain ( " No free slot to put summoned creature " ) ;
}
}
2014-03-17 22:51:07 +03:00
for ( auto al : removedWarMachines )
{
gh - > removeArtifact ( al ) ;
}
if ( heroWithDeadCommander ! = ObjectInstanceID ( ) )
{
SetCommanderProperty scp ;
scp . heroid = heroWithDeadCommander ;
scp . which = SetCommanderProperty : : ALIVE ;
scp . amount = 0 ;
2016-10-29 13:45:08 +02:00
gh - > sendAndApply ( & scp ) ;
2014-03-17 22:51:07 +03:00
}
}
2017-07-01 15:30:13 +02:00
CGameHandler : : FinishingBattleHelper : : FinishingBattleHelper ( std : : shared_ptr < const CBattleQuery > Query , int RemainingBattleQueriesCount )
2014-03-17 22:51:07 +03:00
{
assert ( Query - > result ) ;
assert ( Query - > bi ) ;
auto & result = * Query - > result ;
auto & info = * Query - > bi ;
winnerHero = result . winner ! = 0 ? info . sides [ 1 ] . hero : info . sides [ 0 ] . hero ;
loserHero = result . winner ! = 0 ? info . sides [ 0 ] . hero : info . sides [ 1 ] . hero ;
victor = info . sides [ result . winner ] . color ;
loser = info . sides [ ! result . winner ] . color ;
2023-04-06 17:34:07 +02:00
winnerSide = result . winner ;
2014-03-17 22:51:07 +03:00
remainingBattleQueriesCount = RemainingBattleQueriesCount ;
}
CGameHandler : : FinishingBattleHelper : : FinishingBattleHelper ( )
{
winnerHero = loserHero = nullptr ;
2023-04-06 17:34:07 +02:00
winnerSide = 0 ;
2016-11-27 21:37:41 +02:00
remainingBattleQueriesCount = 0 ;
2014-03-17 22:51:07 +03:00
}
2014-11-25 17:16:14 +02:00
2016-09-09 19:30:36 +02:00
CRandomGenerator & CGameHandler : : getRandomGenerator ( )
{
return CRandomGenerator : : getDefault ( ) ;
}
2022-09-21 18:31:14 +02:00
# if SCRIPTING_ENABLED
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
scripting : : Pool * CGameHandler : : getGlobalContextPool ( ) const
{
return serverScripts . get ( ) ;
}
2022-09-21 18:31:14 +02:00
scripting : : Pool * CGameHandler : : getContextPool ( ) const
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
{
return serverScripts . get ( ) ;
}
2022-09-21 18:31:14 +02:00
# endif
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
const ObjectInstanceID CGameHandler : : putNewObject ( Obj ID , int subID , int3 pos )
{
NewObject no ;
no . ID = ID ; //creature
no . subID = subID ;
no . pos = pos ;
sendAndApply ( & no ) ;
return no . id ; //id field will be filled during applying on gs
2023-04-10 02:34:24 +02:00
}