| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 | /* * Summon.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */#include "StdInc.h"#include "Summon.h"#include "Registry.h"#include "../ISpellMechanics.h"#include "../../MetaString.h"#include "../../battle/CBattleInfoCallback.h"#include "../../battle/BattleInfo.h"#include "../../battle/Unit.h"#include "../../serializer/JsonSerializeFormat.h"#include "../../CCreatureHandler.h"#include "../../CHeroHandler.h"#include "../../mapObjects/CGHeroInstance.h"#include "../../networkPacks/PacksForClientBattle.h"VCMI_LIB_NAMESPACE_BEGINnamespace spells{namespace effects{void Summon::adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const{	//no hexes affected}void Summon::adjustTargetTypes(std::vector<TargetType> & types) const{	//any target type allowed}bool Summon::applicable(Problem & problem, const Mechanics * m) const{	if (creature == CreatureID::NONE)	{		logMod->error("Attempt to summon non-existing creature!");		return m->adaptGenericProblem(problem);	}	if(exclusive)	{		//check if there are summoned creatures of other type		auto otherSummoned = m->battle()->battleGetUnitsIf([m, this](const battle::Unit * unit)		{			return (unit->unitOwner() == m->getCasterColor())				&& (unit->unitSlot() == SlotID::SUMMONED_SLOT_PLACEHOLDER)				&& (!unit->isClone())				&& (unit->creatureId() != creature);		});		if(!otherSummoned.empty())		{			const auto *elemental = otherSummoned.front();			MetaString text;			text.appendLocalString(EMetaText::GENERAL_TXT, 538);			const auto *caster = dynamic_cast<const CGHeroInstance *>(m->caster);			if(caster)			{				text.replaceRawString(caster->getNameTranslated());				text.replaceNamePlural(elemental->creatureId());				if(caster->type->gender == EHeroGender::FEMALE)					text.replaceLocalString(EMetaText::GENERAL_TXT, 540);				else					text.replaceLocalString(EMetaText::GENERAL_TXT, 539);			}			problem.add(std::move(text), Problem::NORMAL);			return false;		}	}	return true;}void Summon::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const{	//new feature - percentage bonus	auto valueWithBonus = m->applySpecificSpellBonus(m->calculateRawEffectValue(0, m->getEffectPower()));//TODO: consider use base power too	BattleUnitsChanged pack;	pack.battleID = m->battle()->getBattle()->getBattleID();	for(const auto & dest : target)	{		if(dest.unitValue)		{			const battle::Unit * summoned = dest.unitValue;			std::shared_ptr<battle::Unit> state = summoned->acquire();			int64_t healthValue = (summonByHealth ? valueWithBonus : (valueWithBonus * summoned->getMaxHealth()));			state->heal(healthValue, EHealLevel::OVERHEAL, (permanent ? EHealPower::PERMANENT : EHealPower::ONE_BATTLE));			pack.changedStacks.emplace_back(summoned->unitId(), UnitChanges::EOperation::RESET_STATE);			state->save(pack.changedStacks.back().data);		}		else		{			int32_t amount = 0;			if(summonByHealth)			{				const auto *creatureType = creature.toEntity(m->creatures());				auto creatureMaxHealth = creatureType->getMaxHealth();				amount = static_cast<int32_t>(valueWithBonus / creatureMaxHealth);			}			else			{				amount = static_cast<int32_t>(valueWithBonus);			}			if(amount < 1)			{				server->complain("Summoning didn't summon any!");				continue;			}			battle::UnitInfo info;			info.id = m->battle()->battleNextUnitId();			info.count = amount;			info.type = creature;			info.side = m->casterSide;			info.position = dest.hexValue;			info.summoned = !permanent;			pack.changedStacks.emplace_back(info.id, UnitChanges::EOperation::ADD);			info.save(pack.changedStacks.back().data);		}	}	if(!pack.changedStacks.empty())		server->apply(&pack);}EffectTarget Summon::filterTarget(const Mechanics * m, const EffectTarget & target) const{	return target;}void Summon::serializeJsonEffect(JsonSerializeFormat & handler){	handler.serializeId("id", creature, CreatureID());	handler.serializeBool("permanent", permanent, false);	handler.serializeBool("exclusive", exclusive, true);	handler.serializeBool("summonByHealth", summonByHealth, false);	handler.serializeBool("summonSameUnit", summonSameUnit, false);}EffectTarget Summon::transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const{	auto sameSummoned = m->battle()->battleGetUnitsIf([m, this](const battle::Unit * unit)	{		return (unit->unitOwner() == m->getCasterColor())			&& (unit->unitSlot() == SlotID::SUMMONED_SLOT_PLACEHOLDER)			&& (!unit->isClone())			&& (unit->creatureId() == creature)			&& (unit->alive());	});	EffectTarget effectTarget;	if(sameSummoned.empty() || !summonSameUnit)	{		BattleHex hex = m->battle()->getAvaliableHex(creature, m->casterSide);		if(!hex.isValid())			logGlobal->error("No free space to summon creature!");		else			effectTarget.emplace_back(hex);	}	else	{		effectTarget.emplace_back(sameSummoned.front());	}	return effectTarget;}}}VCMI_LIB_NAMESPACE_END
 |