2015-12-02 21:39:53 +02:00
|
|
|
/*
|
2014-06-05 20:26:50 +03:00
|
|
|
* CArmedInstance.cpp, part of VCMI engine
|
2014-06-05 19:52:14 +03:00
|
|
|
*
|
|
|
|
* Authors: listed in file AUTHORS in main folder
|
|
|
|
*
|
|
|
|
* License: GNU General Public License v2.0 or later
|
|
|
|
* Full text of license available in license.txt file, in main folder
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "StdInc.h"
|
2014-06-05 20:26:50 +03:00
|
|
|
#include "CArmedInstance.h"
|
2014-06-05 19:52:14 +03:00
|
|
|
|
|
|
|
#include "../CTownHandler.h"
|
|
|
|
#include "../CCreatureHandler.h"
|
|
|
|
#include "../CGeneralTextHandler.h"
|
2023-06-23 17:02:48 +02:00
|
|
|
#include "../gameState/CGameState.h"
|
2015-12-02 21:39:53 +02:00
|
|
|
#include "../CPlayerState.h"
|
2023-12-13 16:53:32 +02:00
|
|
|
#include "../MetaString.h"
|
2014-06-05 19:52:14 +03:00
|
|
|
|
2022-07-26 15:07:42 +02:00
|
|
|
VCMI_LIB_NAMESPACE_BEGIN
|
|
|
|
|
2023-11-05 18:58:07 +02:00
|
|
|
void CArmedInstance::randomizeArmy(FactionID type)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
|
|
|
for (auto & elem : stacks)
|
|
|
|
{
|
2023-04-02 18:56:10 +02:00
|
|
|
if(elem.second->randomStack)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2023-04-02 18:56:10 +02:00
|
|
|
int level = elem.second->randomStack->level;
|
|
|
|
int upgrade = elem.second->randomStack->upgrade;
|
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
|
|
|
elem.second->setType((*VLC->townh)[type]->town->creatures[level][upgrade]);
|
2014-06-05 19:52:14 +03:00
|
|
|
|
2023-04-16 19:42:56 +02:00
|
|
|
elem.second->randomStack = std::nullopt;
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
assert(elem.second->valid(false));
|
|
|
|
assert(elem.second->armyObj == this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-28 15:19:27 +02:00
|
|
|
// Take Angelic Alliance troop-mixing freedom of non-evil units into account.
|
2023-05-01 00:20:01 +02:00
|
|
|
CSelector CArmedInstance::nonEvilAlignmentMixSelector = Selector::type()(BonusType::NONEVIL_ALIGNMENT_MIX);
|
2020-06-28 15:19:27 +02:00
|
|
|
|
2024-01-01 16:37:48 +02:00
|
|
|
CArmedInstance::CArmedInstance(IGameCallback *cb)
|
|
|
|
:CArmedInstance(cb, false)
|
2021-05-23 13:28:43 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-01-01 16:37:48 +02:00
|
|
|
CArmedInstance::CArmedInstance(IGameCallback *cb, bool isHypothetic):
|
|
|
|
CGObjectInstance(cb),
|
2023-12-21 19:09:33 +02:00
|
|
|
CBonusSystemNode(isHypothetic),
|
2023-02-12 22:39:17 +02:00
|
|
|
nonEvilAlignmentMix(this, nonEvilAlignmentMixSelector),
|
|
|
|
battle(nullptr)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void CArmedInstance::updateMoraleBonusFromArmy()
|
|
|
|
{
|
|
|
|
if(!validTypes(false)) //object not randomized, don't bother
|
|
|
|
return;
|
|
|
|
|
2023-05-01 00:20:01 +02:00
|
|
|
auto b = getExportedBonusList().getFirst(Selector::sourceType()(BonusSource::ARMY).And(Selector::type()(BonusType::MORALE)));
|
2016-09-19 23:36:35 +02:00
|
|
|
if(!b)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2023-10-21 13:50:42 +02:00
|
|
|
b = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::MORALE, BonusSource::ARMY, 0, BonusSourceID());
|
2014-06-05 19:52:14 +03:00
|
|
|
addNewBonus(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
//number of alignments and presence of undead
|
2023-04-09 17:26:32 +02:00
|
|
|
std::set<FactionID> factions;
|
2014-06-05 19:52:14 +03:00
|
|
|
bool hasUndead = false;
|
|
|
|
|
2020-06-28 15:19:27 +02:00
|
|
|
const std::string undeadCacheKey = "type_UNDEAD";
|
2023-05-01 00:20:01 +02:00
|
|
|
static const CSelector undeadSelector = Selector::type()(BonusType::UNDEAD);
|
2020-06-28 15:19:27 +02:00
|
|
|
|
2023-02-12 22:39:17 +02:00
|
|
|
for(const auto & slot : Slots())
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
|
|
|
const CStackInstance * inst = slot.second;
|
2023-11-04 17:04:53 +02:00
|
|
|
const auto * creature = inst->getCreatureID().toEntity(VLC);
|
2014-06-05 19:52:14 +03:00
|
|
|
|
2023-04-09 17:26:32 +02:00
|
|
|
factions.insert(creature->getFaction());
|
2014-06-05 19:52:14 +03:00
|
|
|
// Check for undead flag instead of faction (undead mummies are neutral)
|
2022-09-14 11:00:40 +02:00
|
|
|
if (!hasUndead)
|
|
|
|
{
|
|
|
|
//this is costly check, let's skip it at first undead
|
|
|
|
hasUndead |= inst->hasBonus(undeadSelector, undeadCacheKey);
|
|
|
|
}
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t factionsInArmy = factions.size(); //town garrison seems to take both sets into account
|
|
|
|
|
2020-06-28 15:19:27 +02:00
|
|
|
if (nonEvilAlignmentMix.getHasBonus())
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
|
|
|
size_t mixableFactions = 0;
|
|
|
|
|
2023-04-09 17:26:32 +02:00
|
|
|
for(auto f : factions)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2023-11-02 16:56:02 +02:00
|
|
|
if (VLC->factions()->getById(f)->getAlignment() != EAlignment::EVIL)
|
2014-06-05 19:52:14 +03:00
|
|
|
mixableFactions++;
|
|
|
|
}
|
|
|
|
if (mixableFactions > 0)
|
|
|
|
factionsInArmy -= mixableFactions - 1;
|
|
|
|
}
|
|
|
|
|
2021-05-16 19:53:11 +02:00
|
|
|
std::string description;
|
|
|
|
|
2014-06-05 19:52:14 +03:00
|
|
|
if(factionsInArmy == 1)
|
|
|
|
{
|
|
|
|
b->val = +1;
|
2021-05-16 19:53:11 +02:00
|
|
|
description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1
|
|
|
|
description = description.substr(0, description.size()-3);//trim "+1"
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
else if (!factions.empty()) // no bonus from empty garrison
|
|
|
|
{
|
2023-02-12 22:39:17 +02:00
|
|
|
b->val = 2 - static_cast<si32>(factionsInArmy);
|
2023-12-13 16:53:32 +02:00
|
|
|
MetaString formatter;
|
|
|
|
formatter.appendTextID("core.arraytxt.114"); //Troops of %d alignments %d
|
|
|
|
formatter.replaceNumber(factionsInArmy);
|
|
|
|
formatter.replaceNumber(b->val);
|
|
|
|
|
|
|
|
description = formatter.toString();
|
2023-11-10 16:11:35 +02:00
|
|
|
description = description.substr(0, description.size()-3);//trim value
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
2021-05-16 19:53:11 +02:00
|
|
|
|
|
|
|
boost::algorithm::trim(description);
|
|
|
|
b->description = description;
|
|
|
|
|
2015-12-11 15:13:18 +02:00
|
|
|
CBonusSystemNode::treeHasChanged();
|
2014-06-05 19:52:14 +03:00
|
|
|
|
|
|
|
//-1 modifier for any Undead unit in army
|
2023-10-21 13:50:42 +02:00
|
|
|
auto undeadModifier = getExportedBonusList().getFirst(Selector::source(BonusSource::ARMY, BonusCustomSource::undeadMoraleDebuff));
|
2014-06-05 19:52:14 +03:00
|
|
|
if(hasUndead)
|
|
|
|
{
|
|
|
|
if(!undeadModifier)
|
2015-09-04 18:38:42 +02:00
|
|
|
{
|
2023-10-21 13:50:42 +02:00
|
|
|
undeadModifier = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::MORALE, BonusSource::ARMY, -1, BonusCustomSource::undeadMoraleDebuff, VLC->generaltexth->arraytxt[116]);
|
2015-09-04 18:38:42 +02:00
|
|
|
undeadModifier->description = undeadModifier->description.substr(0, undeadModifier->description.size()-2);//trim value
|
|
|
|
addNewBonus(undeadModifier);
|
2015-11-16 15:30:40 +02:00
|
|
|
}
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
else if(undeadModifier)
|
|
|
|
removeBonus(undeadModifier);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void CArmedInstance::armyChanged()
|
|
|
|
{
|
|
|
|
updateMoraleBonusFromArmy();
|
|
|
|
}
|
|
|
|
|
2022-11-06 01:26:13 +02:00
|
|
|
CBonusSystemNode & CArmedInstance::whereShouldBeAttached(CGameState * gs)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2023-08-27 00:35:38 +02:00
|
|
|
if(tempOwner.isValidPlayer())
|
2022-11-06 01:26:13 +02:00
|
|
|
if(auto * where = gs->getPlayerState(tempOwner))
|
|
|
|
return *where;
|
|
|
|
|
|
|
|
return gs->globalEffects;
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
|
2022-11-06 01:26:13 +02:00
|
|
|
CBonusSystemNode & CArmedInstance::whatShouldBeAttached()
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2022-11-06 01:26:13 +02:00
|
|
|
return *this;
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
2022-07-26 15:07:42 +02:00
|
|
|
|
2023-04-09 17:26:32 +02:00
|
|
|
const IBonusBearer* CArmedInstance::getBonusBearer() const
|
|
|
|
{
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2023-09-17 22:19:45 +02:00
|
|
|
void CArmedInstance::serializeJsonOptions(JsonSerializeFormat & handler)
|
|
|
|
{
|
|
|
|
CGObjectInstance::serializeJsonOptions(handler);
|
|
|
|
CCreatureSet::serializeJson(handler, "army", 7);
|
|
|
|
}
|
|
|
|
|
2022-07-26 15:07:42 +02:00
|
|
|
VCMI_LIB_NAMESPACE_END
|