|
|
@@ -37,27 +37,28 @@ bool AdventureSpellMechanics::adventureCast(SpellCastEnvironment * env, const Ad
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- const CGHeroInstance * caster = parameters.caster;
|
|
|
-
|
|
|
- if(caster->inTownGarrison)
|
|
|
+ if(const CGHeroInstance * heroCaster = dynamic_cast<const CGHeroInstance *>(parameters.caster))
|
|
|
{
|
|
|
- env->complain("Attempt to cast an adventure spell in town garrison");
|
|
|
- return false;
|
|
|
- }
|
|
|
+ if(heroCaster->inTownGarrison)
|
|
|
+ {
|
|
|
+ env->complain("Attempt to cast an adventure spell in town garrison");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- const auto level = caster->getSpellSchoolLevel(owner);
|
|
|
- const auto cost = owner->getCost(level);
|
|
|
+ const auto level = heroCaster->getSpellSchoolLevel(owner);
|
|
|
+ const auto cost = owner->getCost(level);
|
|
|
|
|
|
- if(!caster->canCastThisSpell(owner))
|
|
|
- {
|
|
|
- env->complain("Hero cannot cast this spell!");
|
|
|
- return false;
|
|
|
- }
|
|
|
+ if(!heroCaster->canCastThisSpell(owner))
|
|
|
+ {
|
|
|
+ env->complain("Hero cannot cast this spell!");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- if(caster->mana < cost)
|
|
|
- {
|
|
|
- env->complain("Hero doesn't have enough spell points to cast this spell!");
|
|
|
- return false;
|
|
|
+ if(heroCaster->mana < cost)
|
|
|
+ {
|
|
|
+ env->complain("Hero doesn't have enough spell points to cast this spell!");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
ESpellCastResult result = beginCast(env, parameters);
|
|
|
@@ -82,7 +83,7 @@ ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(SpellCastEnviron
|
|
|
for(const Bonus & b : bonuses)
|
|
|
{
|
|
|
GiveBonus gb;
|
|
|
- gb.id = parameters.caster->id.getNum();
|
|
|
+ gb.id = parameters.caster->getCasterUnitId();
|
|
|
gb.bonus = b;
|
|
|
env->apply(&gb);
|
|
|
}
|
|
|
@@ -105,7 +106,7 @@ ESpellCastResult AdventureSpellMechanics::beginCast(SpellCastEnvironment * env,
|
|
|
void AdventureSpellMechanics::performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
|
|
|
{
|
|
|
AdvmapSpellCast asc;
|
|
|
- asc.casterID = parameters.caster->id;
|
|
|
+ asc.casterID = ObjectInstanceID(parameters.caster->getCasterUnitId());
|
|
|
asc.spellID = owner->id;
|
|
|
env->apply(&asc);
|
|
|
|
|
|
@@ -121,13 +122,7 @@ void AdventureSpellMechanics::endCast(SpellCastEnvironment * env, const Adventur
|
|
|
switch(result)
|
|
|
{
|
|
|
case ESpellCastResult::OK:
|
|
|
- {
|
|
|
- SetMana sm;
|
|
|
- sm.hid = parameters.caster->id;
|
|
|
- sm.absolute = false;
|
|
|
- sm.val = -cost;
|
|
|
- env->apply(&sm);
|
|
|
- }
|
|
|
+ parameters.caster->spendMana(env, cost);
|
|
|
break;
|
|
|
default:
|
|
|
break;
|
|
|
@@ -142,21 +137,28 @@ SummonBoatMechanics::SummonBoatMechanics(const CSpell * s):
|
|
|
|
|
|
ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
|
|
|
{
|
|
|
- if(parameters.caster->boat)
|
|
|
+ if(!parameters.caster->getHeroCaster())
|
|
|
+ {
|
|
|
+ env->complain("Not a hero caster!");
|
|
|
+ return ESpellCastResult::ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(parameters.caster->getHeroCaster()->boat)
|
|
|
{
|
|
|
InfoWindow iw;
|
|
|
- iw.player = parameters.caster->tempOwner;
|
|
|
+ iw.player = parameters.caster->getCasterOwner();
|
|
|
iw.text.addTxt(MetaString::GENERAL_TXT, 333);//%s is already in boat
|
|
|
- iw.text.addReplacement(parameters.caster->getNameTranslated());
|
|
|
+ parameters.caster->getCasterName(iw.text);
|
|
|
env->apply(&iw);
|
|
|
return ESpellCastResult::CANCEL;
|
|
|
}
|
|
|
|
|
|
- int3 summonPos = parameters.caster->bestLocation();
|
|
|
+ int3 summonPos = parameters.caster->getHeroCaster()->bestLocation();
|
|
|
+
|
|
|
if(summonPos.x < 0)
|
|
|
{
|
|
|
InfoWindow iw;
|
|
|
- iw.player = parameters.caster->tempOwner;
|
|
|
+ iw.player = parameters.caster->getCasterOwner();
|
|
|
iw.text.addTxt(MetaString::GENERAL_TXT, 334);//There is no place to put the boat.
|
|
|
env->apply(&iw);
|
|
|
return ESpellCastResult::CANCEL;
|
|
|
@@ -168,9 +170,9 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
|
|
|
if(env->getRNG()->getInt64Range(0, 99)() >= owner->getLevelPower(schoolLevel)) //power is % chance of success
|
|
|
{
|
|
|
InfoWindow iw;
|
|
|
- iw.player = parameters.caster->tempOwner;
|
|
|
+ iw.player = parameters.caster->getCasterOwner();
|
|
|
iw.text.addTxt(MetaString::GENERAL_TXT, 336); //%s tried to summon a boat, but failed.
|
|
|
- iw.text.addReplacement(parameters.caster->getNameTranslated());
|
|
|
+ parameters.caster->getCasterName(iw.text);
|
|
|
env->apply(&iw);
|
|
|
return ESpellCastResult::OK;
|
|
|
}
|
|
|
@@ -186,7 +188,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
|
|
|
if(b->hero)
|
|
|
continue; //we're looking for unoccupied boat
|
|
|
|
|
|
- double nDist = b->pos.dist2d(parameters.caster->visitablePos());
|
|
|
+ double nDist = b->pos.dist2d(parameters.caster->getHeroCaster()->visitablePos());
|
|
|
if(!nearest || nDist < dist) //it's first boat or closer than previous
|
|
|
{
|
|
|
nearest = b;
|
|
|
@@ -205,7 +207,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
|
|
|
else if(schoolLevel < 2) //none or basic level -> cannot create boat :(
|
|
|
{
|
|
|
InfoWindow iw;
|
|
|
- iw.player = parameters.caster->tempOwner;
|
|
|
+ iw.player = parameters.caster->getCasterOwner();
|
|
|
iw.text.addTxt(MetaString::GENERAL_TXT, 335); //There are no boats to summon.
|
|
|
env->apply(&iw);
|
|
|
}
|
|
|
@@ -213,7 +215,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
|
|
|
{
|
|
|
NewObject no;
|
|
|
no.ID = Obj::BOAT;
|
|
|
- no.subID = parameters.caster->getBoatType();
|
|
|
+ no.subID = parameters.caster->getHeroCaster()->getBoatType();
|
|
|
no.pos = summonPos + int3(1,0,0);
|
|
|
env->apply(&no);
|
|
|
}
|
|
|
@@ -233,9 +235,9 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironmen
|
|
|
if(env->getRNG()->getInt64Range(0, 99)() >= owner->getLevelPower(schoolLevel)) //power is % chance of success
|
|
|
{
|
|
|
InfoWindow iw;
|
|
|
- iw.player = parameters.caster->tempOwner;
|
|
|
+ iw.player = parameters.caster->getCasterOwner();
|
|
|
iw.text.addTxt(MetaString::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed
|
|
|
- iw.text.addReplacement(parameters.caster->getNameTranslated());
|
|
|
+ parameters.caster->getCasterName(iw.text);
|
|
|
env->apply(&iw);
|
|
|
return ESpellCastResult::OK;
|
|
|
}
|
|
|
@@ -273,9 +275,15 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
|
|
|
env->complain("Destination is out of map!");
|
|
|
return ESpellCastResult::ERROR;
|
|
|
}
|
|
|
+
|
|
|
+ if(!parameters.caster->getHeroCaster())
|
|
|
+ {
|
|
|
+ env->complain("Not a hero caster!");
|
|
|
+ return ESpellCastResult::ERROR;
|
|
|
+ }
|
|
|
|
|
|
const TerrainTile * dest = env->getCb()->getTile(parameters.pos);
|
|
|
- const TerrainTile * curr = env->getCb()->getTile(parameters.caster->getSightCenter());
|
|
|
+ const TerrainTile * curr = env->getCb()->getTile(parameters.caster->getHeroCaster()->getSightCenter());
|
|
|
|
|
|
if(nullptr == dest)
|
|
|
{
|
|
|
@@ -289,7 +297,7 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
|
|
|
return ESpellCastResult::ERROR;
|
|
|
}
|
|
|
|
|
|
- if(parameters.caster->movement <= 0) //unlike town portal non-zero MP is enough
|
|
|
+ if(parameters.caster->getHeroCaster()->movement <= 0) //unlike town portal non-zero MP is enough
|
|
|
{
|
|
|
env->complain("Hero needs movement points to cast Dimension Door!");
|
|
|
return ESpellCastResult::ERROR;
|
|
|
@@ -301,34 +309,34 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
|
|
|
std::stringstream cachingStr;
|
|
|
cachingStr << "source_" << Bonus::SPELL_EFFECT << "id_" << owner->id.num;
|
|
|
|
|
|
- if(parameters.caster->getBonuses(Selector::source(Bonus::SPELL_EFFECT, owner->id), Selector::all, cachingStr.str())->size() >= owner->getLevelPower(schoolLevel)) //limit casts per turn
|
|
|
+ if(parameters.caster->getHeroCaster()->getBonuses(Selector::source(Bonus::SPELL_EFFECT, owner->id), Selector::all, cachingStr.str())->size() >= owner->getLevelPower(schoolLevel)) //limit casts per turn
|
|
|
{
|
|
|
InfoWindow iw;
|
|
|
- iw.player = parameters.caster->tempOwner;
|
|
|
+ iw.player = parameters.caster->getCasterOwner();
|
|
|
iw.text.addTxt(MetaString::GENERAL_TXT, 338); //%s is not skilled enough to cast this spell again today.
|
|
|
- iw.text.addReplacement(parameters.caster->getNameTranslated());
|
|
|
+ parameters.caster->getCasterName(iw.text);
|
|
|
env->apply(&iw);
|
|
|
return ESpellCastResult::CANCEL;
|
|
|
}
|
|
|
|
|
|
GiveBonus gb;
|
|
|
- gb.id = parameters.caster->id.getNum();
|
|
|
+ gb.id = parameters.caster->getCasterUnitId();
|
|
|
gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::NONE, Bonus::SPELL_EFFECT, 0, owner->id);
|
|
|
env->apply(&gb);
|
|
|
|
|
|
if(!dest->isClear(curr)) //wrong dest tile
|
|
|
{
|
|
|
InfoWindow iw;
|
|
|
- iw.player = parameters.caster->tempOwner;
|
|
|
+ iw.player = parameters.caster->getCasterOwner();
|
|
|
iw.text.addTxt(MetaString::GENERAL_TXT, 70); //Dimension Door failed!
|
|
|
env->apply(&iw);
|
|
|
}
|
|
|
- else if(env->moveHero(parameters.caster->id, parameters.caster->convertFromVisitablePos(parameters.pos), true))
|
|
|
+ else if(env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(parameters.pos), true))
|
|
|
{
|
|
|
SetMovePoints smp;
|
|
|
- smp.hid = parameters.caster->id;
|
|
|
- if(movementCost < static_cast<int>(parameters.caster->movement))
|
|
|
- smp.val = parameters.caster->movement - movementCost;
|
|
|
+ smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId());
|
|
|
+ if(movementCost < static_cast<int>(parameters.caster->getHeroCaster()->movement))
|
|
|
+ smp.val = parameters.caster->getHeroCaster()->movement - movementCost;
|
|
|
else
|
|
|
smp.val = 0;
|
|
|
env->apply(&smp);
|
|
|
@@ -346,6 +354,12 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
|
|
|
{
|
|
|
const CGTownInstance * destination = nullptr;
|
|
|
const int moveCost = movementCost(parameters);
|
|
|
+
|
|
|
+ if(!parameters.caster->getHeroCaster())
|
|
|
+ {
|
|
|
+ env->complain("Not a hero caster!");
|
|
|
+ return ESpellCastResult::ERROR;
|
|
|
+ }
|
|
|
|
|
|
if(parameters.caster->getSpellSchoolLevel(owner) < 2)
|
|
|
{
|
|
|
@@ -355,13 +369,13 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
|
|
|
if(nullptr == destination)
|
|
|
return ESpellCastResult::ERROR;
|
|
|
|
|
|
- if(static_cast<int>(parameters.caster->movement) < moveCost)
|
|
|
+ if(static_cast<int>(parameters.caster->getHeroCaster()->movement) < moveCost)
|
|
|
return ESpellCastResult::ERROR;
|
|
|
|
|
|
if(destination->visitingHero)
|
|
|
{
|
|
|
InfoWindow iw;
|
|
|
- iw.player = parameters.caster->tempOwner;
|
|
|
+ iw.player = parameters.caster->getCasterOwner();
|
|
|
iw.text.addTxt(MetaString::GENERAL_TXT, 123);
|
|
|
env->apply(&iw);
|
|
|
return ESpellCastResult::CANCEL;
|
|
|
@@ -397,7 +411,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
|
|
|
return ESpellCastResult::ERROR;
|
|
|
}
|
|
|
|
|
|
- const auto relations = env->getCb()->getPlayerRelations(destination->tempOwner, parameters.caster->tempOwner);
|
|
|
+ const auto relations = env->getCb()->getPlayerRelations(destination->tempOwner, parameters.caster->getCasterOwner());
|
|
|
|
|
|
if(relations == PlayerRelations::ENEMIES)
|
|
|
{
|
|
|
@@ -405,7 +419,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
|
|
|
return ESpellCastResult::ERROR;
|
|
|
}
|
|
|
|
|
|
- if(static_cast<int>(parameters.caster->movement) < moveCost)
|
|
|
+ if(static_cast<int>(parameters.caster->getHeroCaster()->movement) < moveCost)
|
|
|
{
|
|
|
env->complain("This hero has not enough movement points!");
|
|
|
return ESpellCastResult::ERROR;
|
|
|
@@ -423,11 +437,11 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
|
|
|
return ESpellCastResult::ERROR;
|
|
|
}
|
|
|
|
|
|
- if(env->moveHero(parameters.caster->id, parameters.caster->convertFromVisitablePos(destination->visitablePos()), true))
|
|
|
+ if(env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(destination->visitablePos()), true))
|
|
|
{
|
|
|
SetMovePoints smp;
|
|
|
- smp.hid = parameters.caster->id;
|
|
|
- smp.val = std::max<ui32>(0, parameters.caster->movement - moveCost);
|
|
|
+ smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId());
|
|
|
+ smp.val = std::max<ui32>(0, parameters.caster->getHeroCaster()->movement - moveCost);
|
|
|
env->apply(&smp);
|
|
|
}
|
|
|
return ESpellCastResult::OK;
|
|
|
@@ -436,11 +450,17 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
|
|
|
ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
|
|
|
{
|
|
|
std::vector<const CGTownInstance *> towns = getPossibleTowns(env, parameters);
|
|
|
+
|
|
|
+ if(!parameters.caster->getHeroCaster())
|
|
|
+ {
|
|
|
+ env->complain("Not a hero caster!");
|
|
|
+ return ESpellCastResult::ERROR;
|
|
|
+ }
|
|
|
|
|
|
if(towns.empty())
|
|
|
{
|
|
|
InfoWindow iw;
|
|
|
- iw.player = parameters.caster->tempOwner;
|
|
|
+ iw.player = parameters.caster->getCasterOwner();
|
|
|
iw.text.addTxt(MetaString::GENERAL_TXT, 124);
|
|
|
env->apply(&iw);
|
|
|
return ESpellCastResult::CANCEL;
|
|
|
@@ -448,10 +468,10 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
|
|
|
|
|
|
const int moveCost = movementCost(parameters);
|
|
|
|
|
|
- if(static_cast<int>(parameters.caster->movement) < moveCost)
|
|
|
+ if(static_cast<int>(parameters.caster->getHeroCaster()->movement) < moveCost)
|
|
|
{
|
|
|
InfoWindow iw;
|
|
|
- iw.player = parameters.caster->tempOwner;
|
|
|
+ iw.player = parameters.caster->getCasterOwner();
|
|
|
iw.text.addTxt(MetaString::GENERAL_TXT, 125);
|
|
|
env->apply(&iw);
|
|
|
return ESpellCastResult::CANCEL;
|
|
|
@@ -496,13 +516,13 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
|
|
|
if(request.objects.empty())
|
|
|
{
|
|
|
InfoWindow iw;
|
|
|
- iw.player = parameters.caster->tempOwner;
|
|
|
+ iw.player = parameters.caster->getCasterOwner();
|
|
|
iw.text.addTxt(MetaString::GENERAL_TXT, 124);
|
|
|
env->apply(&iw);
|
|
|
return ESpellCastResult::CANCEL;
|
|
|
}
|
|
|
|
|
|
- request.player = parameters.caster->getOwner();
|
|
|
+ request.player = parameters.caster->getCasterOwner();
|
|
|
request.title.addTxt(MetaString::JK_TXT, 40);
|
|
|
request.description.addTxt(MetaString::JK_TXT, 41);
|
|
|
request.icon.id = Component::EComponentType::SPELL;
|
|
|
@@ -520,13 +540,16 @@ const CGTownInstance * TownPortalMechanics::findNearestTown(SpellCastEnvironment
|
|
|
{
|
|
|
if(pool.empty())
|
|
|
return nullptr;
|
|
|
+
|
|
|
+ if(!parameters.caster->getHeroCaster())
|
|
|
+ return nullptr;
|
|
|
|
|
|
auto nearest = pool.cbegin(); //nearest town's iterator
|
|
|
- si32 dist = (*nearest)->pos.dist2dSQ(parameters.caster->pos);
|
|
|
+ si32 dist = (*nearest)->pos.dist2dSQ(parameters.caster->getHeroCaster()->pos);
|
|
|
|
|
|
for(auto i = nearest + 1; i != pool.cend(); ++i)
|
|
|
{
|
|
|
- si32 curDist = (*i)->pos.dist2dSQ(parameters.caster->pos);
|
|
|
+ si32 curDist = (*i)->pos.dist2dSQ(parameters.caster->getHeroCaster()->pos);
|
|
|
|
|
|
if(curDist < dist)
|
|
|
{
|
|
|
@@ -541,7 +564,7 @@ std::vector <const CGTownInstance*> TownPortalMechanics::getPossibleTowns(SpellC
|
|
|
{
|
|
|
std::vector <const CGTownInstance*> ret;
|
|
|
|
|
|
- const TeamState * team = env->getCb()->getPlayerTeam(parameters.caster->getOwner());
|
|
|
+ const TeamState * team = env->getCb()->getPlayerTeam(parameters.caster->getCasterOwner());
|
|
|
|
|
|
for(const auto & color : team->players)
|
|
|
{
|
|
|
@@ -555,6 +578,9 @@ std::vector <const CGTownInstance*> TownPortalMechanics::getPossibleTowns(SpellC
|
|
|
|
|
|
int32_t TownPortalMechanics::movementCost(const AdventureSpellCastParameters & parameters) const
|
|
|
{
|
|
|
+ if(parameters.caster != parameters.caster->getHeroCaster()) //if caster is not hero
|
|
|
+ return 0;
|
|
|
+
|
|
|
return GameConstants::BASE_MOVEMENT_COST * ((parameters.caster->getSpellSchoolLevel(owner) >= 3) ? 2 : 3);
|
|
|
}
|
|
|
|
|
|
@@ -568,11 +594,11 @@ ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env
|
|
|
{
|
|
|
ShowWorldViewEx pack;
|
|
|
|
|
|
- pack.player = parameters.caster->getOwner();
|
|
|
+ pack.player = parameters.caster->getCasterOwner();
|
|
|
|
|
|
const auto spellLevel = parameters.caster->getSpellSchoolLevel(owner);
|
|
|
|
|
|
- const auto fowMap = env->getCb()->getPlayerTeam(parameters.caster->getOwner())->fogOfWarMap;
|
|
|
+ const auto fowMap = env->getCb()->getPlayerTeam(parameters.caster->getCasterOwner())->fogOfWarMap;
|
|
|
|
|
|
for(const CGObjectInstance * obj : env->getMap()->objects)
|
|
|
{
|