mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-29 21:56:54 +02:00
Extract adventure spell mechanics
This commit is contained in:
parent
c5e772eb78
commit
08d4f7991e
@ -328,6 +328,11 @@
|
|||||||
"icon": "zvs/Lib1.res/E_MIND"
|
"icon": "zvs/Lib1.res/E_MIND"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"NONE":
|
||||||
|
{
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
|
||||||
"NO_DISTANCE_PENALTY":
|
"NO_DISTANCE_PENALTY":
|
||||||
{
|
{
|
||||||
|
@ -127,7 +127,7 @@ CBonusTypeHandler::~CBonusTypeHandler()
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string CBonusTypeHandler::bonusToString(const Bonus *bonus, const IBonusBearer *bearer, bool description) const
|
std::string CBonusTypeHandler::bonusToString(const Bonus *bonus, const IBonusBearer *bearer, bool description) const
|
||||||
{
|
{
|
||||||
auto getValue = [=](const std::string &name) -> std::string
|
auto getValue = [=](const std::string &name) -> std::string
|
||||||
{
|
{
|
||||||
if (name == "val")
|
if (name == "val")
|
||||||
@ -156,6 +156,8 @@ std::string CBonusTypeHandler::bonusToString(const Bonus *bonus, const IBonusBea
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CBonusType& bt = bonusTypes[bonus->type];
|
const CBonusType& bt = bonusTypes[bonus->type];
|
||||||
|
if(bt.hidden)
|
||||||
|
return "";
|
||||||
const MacroString& macro = description ? bt.description : bt.name;
|
const MacroString& macro = description ? bt.description : bt.name;
|
||||||
|
|
||||||
return macro.build(getValue);
|
return macro.build(getValue);
|
||||||
|
@ -113,6 +113,12 @@ void CSpell::afterCast(BattleInfo * battle, const BattleSpellCast * packet) cons
|
|||||||
mechanics->afterCast(battle, packet);
|
mechanics->afterCast(battle, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CSpell::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
|
||||||
|
{
|
||||||
|
assert(env);
|
||||||
|
|
||||||
|
return mechanics->adventureCast(env, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
void CSpell::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const
|
void CSpell::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
class CGObjectInstance;
|
||||||
|
|
||||||
class CSpell;
|
class CSpell;
|
||||||
class ISpellMechanics;
|
class ISpellMechanics;
|
||||||
|
|
||||||
@ -32,7 +34,9 @@ class BattleInfo;
|
|||||||
struct CPackForClient;
|
struct CPackForClient;
|
||||||
struct BattleSpellCast;
|
struct BattleSpellCast;
|
||||||
|
|
||||||
|
class CGameInfoCallback;
|
||||||
class CRandomGenerator;
|
class CRandomGenerator;
|
||||||
|
class CMap;
|
||||||
|
|
||||||
struct SpellSchoolInfo
|
struct SpellSchoolInfo
|
||||||
{
|
{
|
||||||
@ -53,6 +57,11 @@ public:
|
|||||||
|
|
||||||
virtual CRandomGenerator & getRandomGenerator() const = 0;
|
virtual CRandomGenerator & getRandomGenerator() const = 0;
|
||||||
virtual void complain(const std::string & problem) const = 0;
|
virtual void complain(const std::string & problem) const = 0;
|
||||||
|
|
||||||
|
virtual const CMap * getMap() const = 0;
|
||||||
|
virtual const CGameInfoCallback * getCb() const = 0;
|
||||||
|
|
||||||
|
virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) const =0; //TODO: remove
|
||||||
};
|
};
|
||||||
|
|
||||||
///helper struct
|
///helper struct
|
||||||
@ -73,6 +82,12 @@ public:
|
|||||||
const BattleInfo * cb;
|
const BattleInfo * cb;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DLL_LINKAGE AdventureSpellCastParameters
|
||||||
|
{
|
||||||
|
const CGHeroInstance * caster;
|
||||||
|
int3 pos;
|
||||||
|
};
|
||||||
|
|
||||||
enum class VerticalPosition : ui8{TOP, CENTER, BOTTOM};
|
enum class VerticalPosition : ui8{TOP, CENTER, BOTTOM};
|
||||||
|
|
||||||
class DLL_LINKAGE CSpell
|
class DLL_LINKAGE CSpell
|
||||||
@ -314,7 +329,7 @@ public:
|
|||||||
///Server logic. Has write access to GameState via packets.
|
///Server logic. Has write access to GameState via packets.
|
||||||
///May be executed on client side by (future) non-cheat-proof scripts.
|
///May be executed on client side by (future) non-cheat-proof scripts.
|
||||||
|
|
||||||
//void adventureCast() const;
|
bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const;
|
||||||
void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const;
|
void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -18,6 +18,10 @@
|
|||||||
|
|
||||||
#include "NetPacks.h"
|
#include "NetPacks.h"
|
||||||
|
|
||||||
|
#include "mapping/CMap.h"
|
||||||
|
#include "CGameInfoCallback.h"
|
||||||
|
#include "CGameState.h"
|
||||||
|
|
||||||
namespace SRSLPraserHelpers
|
namespace SRSLPraserHelpers
|
||||||
{
|
{
|
||||||
static int XYToHex(int x, int y)
|
static int XYToHex(int x, int y)
|
||||||
@ -139,7 +143,7 @@ public:
|
|||||||
|
|
||||||
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
|
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
|
||||||
|
|
||||||
//bool adventureCast(const SpellCastContext & context) const override;
|
bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override final;
|
||||||
void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const override;
|
void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const override;
|
||||||
|
|
||||||
void afterCast(BattleInfo * battle, const BattleSpellCast * packet) const override;
|
void afterCast(BattleInfo * battle, const BattleSpellCast * packet) const override;
|
||||||
@ -149,8 +153,58 @@ protected:
|
|||||||
|
|
||||||
virtual int calculateDuration(const CGHeroInstance * caster, int usedSpellPower) const;
|
virtual int calculateDuration(const CGHeroInstance * caster, int usedSpellPower) const;
|
||||||
|
|
||||||
|
///actual adventure cast implementation
|
||||||
|
virtual bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
///ADVENTURE SPELLS
|
||||||
|
|
||||||
|
//todo: make configyrable
|
||||||
|
class AdventureBonusingMechanics: public DefaultSpellMechanics
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AdventureBonusingMechanics(CSpell * s, Bonus::BonusType _bonusTypeID): DefaultSpellMechanics(s), bonusTypeID(_bonusTypeID){};
|
||||||
|
protected:
|
||||||
|
bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;
|
||||||
|
private:
|
||||||
|
Bonus::BonusType bonusTypeID;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SummonBoatMechanics: public DefaultSpellMechanics
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SummonBoatMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||||
|
protected:
|
||||||
|
bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScuttleBoatMechanics: public DefaultSpellMechanics
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScuttleBoatMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||||
|
protected:
|
||||||
|
bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DimensionDoorMechanics: public DefaultSpellMechanics
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DimensionDoorMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||||
|
protected:
|
||||||
|
bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TownPortalMechanics: public DefaultSpellMechanics
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TownPortalMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||||
|
protected:
|
||||||
|
bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///BATTLE SPELLS
|
||||||
|
|
||||||
class AcidBreathDamageMechnics: public DefaultSpellMechanics
|
class AcidBreathDamageMechnics: public DefaultSpellMechanics
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -323,7 +377,23 @@ ISpellMechanics * ISpellMechanics::createMechanics(CSpell * s)
|
|||||||
case SpellID::SUMMON_AIR_ELEMENTAL:
|
case SpellID::SUMMON_AIR_ELEMENTAL:
|
||||||
return new SummonMechanics(s);
|
return new SummonMechanics(s);
|
||||||
case SpellID::TELEPORT:
|
case SpellID::TELEPORT:
|
||||||
return new TeleportMechanics(s);
|
return new TeleportMechanics(s);
|
||||||
|
case SpellID::SUMMON_BOAT:
|
||||||
|
return new SummonBoatMechanics(s);
|
||||||
|
case SpellID::SCUTTLE_BOAT:
|
||||||
|
return new ScuttleBoatMechanics(s);
|
||||||
|
case SpellID::DIMENSION_DOOR:
|
||||||
|
return new DimensionDoorMechanics(s);
|
||||||
|
case SpellID::FLY:
|
||||||
|
return new AdventureBonusingMechanics(s, Bonus::FLYING_MOVEMENT);
|
||||||
|
case SpellID::WATER_WALK:
|
||||||
|
return new AdventureBonusingMechanics(s, Bonus::WATER_WALKING);
|
||||||
|
case SpellID::TOWN_PORTAL:
|
||||||
|
|
||||||
|
case SpellID::VISIONS:
|
||||||
|
case SpellID::VIEW_EARTH:
|
||||||
|
case SpellID::DISGUISE:
|
||||||
|
case SpellID::VIEW_AIR:
|
||||||
default:
|
default:
|
||||||
if(s->isRisingSpell())
|
if(s->isRisingSpell())
|
||||||
return new SpecialRisingSpellMechanics(s);
|
return new SpecialRisingSpellMechanics(s);
|
||||||
@ -362,6 +432,55 @@ void DefaultSpellMechanics::afterCast(BattleInfo * battle, const BattleSpellCast
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DefaultSpellMechanics::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
|
||||||
|
{
|
||||||
|
if(!owner->isAdventureSpell())
|
||||||
|
{
|
||||||
|
env->complain("Attempt to cast non adventure spell in adventure mode");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CGHeroInstance * caster = parameters.caster;
|
||||||
|
const int cost = caster->getSpellCost(owner);
|
||||||
|
|
||||||
|
if(!caster->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
AdvmapSpellCast asc;
|
||||||
|
asc.caster = caster;
|
||||||
|
asc.spellID = owner->id;
|
||||||
|
env->sendAndApply(&asc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(applyAdventureEffects(env, parameters))
|
||||||
|
{
|
||||||
|
SetMana sm;
|
||||||
|
sm.hid = caster->id;
|
||||||
|
sm.absolute = false;
|
||||||
|
sm.val = -cost;
|
||||||
|
env->sendAndApply(&sm);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DefaultSpellMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
|
||||||
|
{
|
||||||
|
//There is no generic algorithm of adventure cast
|
||||||
|
env->complain("Unimplemented adventure spell");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const
|
void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const
|
||||||
{
|
{
|
||||||
@ -847,6 +966,245 @@ ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(cons
|
|||||||
return owner->isImmuneBy(obj);
|
return owner->isImmuneBy(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///ADVENTURE SPELLS
|
||||||
|
|
||||||
|
///AdventureBonusingMechanics
|
||||||
|
bool AdventureBonusingMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
|
||||||
|
{
|
||||||
|
const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||||
|
const int subtype = schoolLevel >= 2 ? 1 : 2; //adv or expert
|
||||||
|
|
||||||
|
GiveBonus gb;
|
||||||
|
gb.id = parameters.caster->id.getNum();
|
||||||
|
gb.bonus = Bonus(Bonus::ONE_DAY, bonusTypeID, Bonus::SPELL_EFFECT, 0, owner->id, subtype);
|
||||||
|
env->sendAndApply(&gb);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///SummonBoatMechanics
|
||||||
|
bool SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
|
||||||
|
{
|
||||||
|
const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||||
|
//check if spell works at all
|
||||||
|
if(env->getRandomGenerator().nextInt(99) >= owner->getPower(schoolLevel)) //power is % chance of success
|
||||||
|
{
|
||||||
|
InfoWindow iw;
|
||||||
|
iw.player = parameters.caster->tempOwner;
|
||||||
|
iw.text.addTxt(MetaString::GENERAL_TXT, 336); //%s tried to summon a boat, but failed.
|
||||||
|
iw.text.addReplacement(parameters.caster->name);
|
||||||
|
env->sendAndApply(&iw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//try to find unoccupied boat to summon
|
||||||
|
const CGBoat * nearest = nullptr;
|
||||||
|
double dist = 0;
|
||||||
|
int3 summonPos = parameters.caster->bestLocation();
|
||||||
|
if(summonPos.x < 0)
|
||||||
|
{
|
||||||
|
env->complain("There is no water tile available!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const CGObjectInstance * obj : env->getMap()->objects)
|
||||||
|
{
|
||||||
|
if(obj && obj->ID == Obj::BOAT)
|
||||||
|
{
|
||||||
|
const CGBoat *b = static_cast<const CGBoat*>(obj);
|
||||||
|
if(b->hero)
|
||||||
|
continue; //we're looking for unoccupied boat
|
||||||
|
|
||||||
|
double nDist = b->pos.dist2d(parameters.caster->getPosition());
|
||||||
|
if(!nearest || nDist < dist) //it's first boat or closer than previous
|
||||||
|
{
|
||||||
|
nearest = b;
|
||||||
|
dist = nDist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nullptr != nearest) //we found boat to summon
|
||||||
|
{
|
||||||
|
ChangeObjPos cop;
|
||||||
|
cop.objid = nearest->id;
|
||||||
|
cop.nPos = summonPos + int3(1,0,0);;
|
||||||
|
cop.flags = 1;
|
||||||
|
env->sendAndApply(&cop);
|
||||||
|
}
|
||||||
|
else if(schoolLevel < 2) //none or basic level -> cannot create boat :(
|
||||||
|
{
|
||||||
|
InfoWindow iw;
|
||||||
|
iw.player = parameters.caster->tempOwner;
|
||||||
|
iw.text.addTxt(MetaString::GENERAL_TXT, 335); //There are no boats to summon.
|
||||||
|
env->sendAndApply(&iw);
|
||||||
|
}
|
||||||
|
else //create boat
|
||||||
|
{
|
||||||
|
NewObject no;
|
||||||
|
no.ID = Obj::BOAT;
|
||||||
|
no.subID = parameters.caster->getBoatType();
|
||||||
|
no.pos = summonPos + int3(1,0,0);;
|
||||||
|
env->sendAndApply(&no);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///ScuttleBoatMechanics
|
||||||
|
bool ScuttleBoatMechanics::applyAdventureEffects(const SpellCastEnvironment* env, AdventureSpellCastParameters& parameters) const
|
||||||
|
{
|
||||||
|
const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||||
|
//check if spell works at all
|
||||||
|
if(env->getRandomGenerator().nextInt(99) >= owner->getPower(schoolLevel)) //power is % chance of success
|
||||||
|
{
|
||||||
|
InfoWindow iw;
|
||||||
|
iw.player = parameters.caster->tempOwner;
|
||||||
|
iw.text.addTxt(MetaString::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed
|
||||||
|
iw.text.addReplacement(parameters.caster->name);
|
||||||
|
env->sendAndApply(&iw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!env->getMap()->isInTheMap(parameters.pos))
|
||||||
|
{
|
||||||
|
env->complain("Invalid dst tile for scuttle!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: test range, visibility
|
||||||
|
const TerrainTile *t = &env->getMap()->getTile(parameters.pos);
|
||||||
|
if(!t->visitableObjects.size() || t->visitableObjects.back()->ID != Obj::BOAT)
|
||||||
|
{
|
||||||
|
env->complain("There is no boat to scuttle!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RemoveObject ro;
|
||||||
|
ro.id = t->visitableObjects.back()->id;
|
||||||
|
env->sendAndApply(&ro);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///DimensionDoorMechanics
|
||||||
|
bool DimensionDoorMechanics::applyAdventureEffects(const SpellCastEnvironment* env, AdventureSpellCastParameters& parameters) const
|
||||||
|
{
|
||||||
|
if(!env->getMap()->isInTheMap(parameters.pos))
|
||||||
|
{
|
||||||
|
env->complain("Destination is out of map!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TerrainTile * dest = env->getCb()->getTile(parameters.pos);
|
||||||
|
const TerrainTile * curr = env->getCb()->getTile(parameters.caster->getSightCenter());
|
||||||
|
|
||||||
|
if(nullptr == dest)
|
||||||
|
{
|
||||||
|
env->complain("Destination tile doesn't exist!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nullptr == curr)
|
||||||
|
{
|
||||||
|
env->complain("Source tile doesn't exist!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parameters.caster->movement <= 0)
|
||||||
|
{
|
||||||
|
env->complain("Hero needs movement points to cast Dimension Door!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||||
|
|
||||||
|
if(parameters.caster->getBonusesCount(Bonus::SPELL_EFFECT, SpellID::DIMENSION_DOOR) >= owner->getPower(schoolLevel)) //limit casts per turn
|
||||||
|
{
|
||||||
|
InfoWindow iw;
|
||||||
|
iw.player = parameters.caster->tempOwner;
|
||||||
|
iw.text.addTxt(MetaString::GENERAL_TXT, 338); //%s is not skilled enough to cast this spell again today.
|
||||||
|
iw.text.addReplacement(parameters.caster->name);
|
||||||
|
env->sendAndApply(&iw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
GiveBonus gb;
|
||||||
|
gb.id = parameters.caster->id.getNum();
|
||||||
|
gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::NONE, Bonus::SPELL_EFFECT, 0, owner->id);
|
||||||
|
env->sendAndApply(&gb);
|
||||||
|
|
||||||
|
if(!dest->isClear(curr)) //wrong dest tile
|
||||||
|
{
|
||||||
|
InfoWindow iw;
|
||||||
|
iw.player = parameters.caster->tempOwner;
|
||||||
|
iw.text.addTxt(MetaString::GENERAL_TXT, 70); //Dimension Door failed!
|
||||||
|
env->sendAndApply(&iw);
|
||||||
|
}
|
||||||
|
else if(env->moveHero(parameters.caster->id, parameters.pos + parameters.caster->getVisitableOffset(), true))
|
||||||
|
{
|
||||||
|
SetMovePoints smp;
|
||||||
|
smp.hid = parameters.caster->id;
|
||||||
|
smp.val = std::max<ui32>(0, parameters.caster->movement - 300);
|
||||||
|
env->sendAndApply(&smp);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///TownPortalMechanics
|
||||||
|
bool TownPortalMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters& parameters) const
|
||||||
|
{
|
||||||
|
if (!env->getMap()->isInTheMap(parameters.pos))
|
||||||
|
{
|
||||||
|
env->complain("Destination tile not present!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerrainTile tile = env->getMap()->getTile(parameters.pos);
|
||||||
|
if (tile.visitableObjects.empty() || tile.visitableObjects.back()->ID != Obj::TOWN)
|
||||||
|
{
|
||||||
|
env->complain("Town not found for Town Portal!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGTownInstance * town = static_cast<CGTownInstance*>(tile.visitableObjects.back());
|
||||||
|
if (town->tempOwner != parameters.caster->tempOwner)
|
||||||
|
{
|
||||||
|
env->complain("Can't teleport to another player!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (town->visitingHero)
|
||||||
|
{
|
||||||
|
env->complain("Can't teleport to occupied town!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters.caster->getSpellSchoolLevel(owner) < 2)
|
||||||
|
{
|
||||||
|
si32 dist = town->pos.dist2dSQ(parameters.caster->pos);
|
||||||
|
ObjectInstanceID nearest = town->id; //nearest town's ID
|
||||||
|
for(const CGTownInstance * currTown : env->getCb()->getPlayer(parameters.caster->tempOwner)->towns)
|
||||||
|
{
|
||||||
|
si32 currDist = currTown->pos.dist2dSQ(parameters.caster->pos);
|
||||||
|
if (currDist < dist)
|
||||||
|
{
|
||||||
|
nearest = currTown->id;
|
||||||
|
dist = currDist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (town->id != nearest)
|
||||||
|
{
|
||||||
|
env->complain("This hero can only teleport to nearest town!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
env->moveHero(parameters.caster->id, town->visitablePos() + parameters.caster->getVisitableOffset() ,1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///BATTLE SPELLS
|
||||||
|
|
||||||
///AcidBreathDamageMechnics
|
///AcidBreathDamageMechnics
|
||||||
void AcidBreathDamageMechnics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
void AcidBreathDamageMechnics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||||
{
|
{
|
||||||
|
@ -41,7 +41,7 @@ public:
|
|||||||
|
|
||||||
virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0;
|
virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0;
|
||||||
|
|
||||||
//virtual bool adventureCast(const SpellCastContext & context) const = 0;
|
virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0;
|
||||||
virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0;
|
virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0;
|
||||||
|
|
||||||
static ISpellMechanics * createMechanics(CSpell * s);
|
static ISpellMechanics * createMechanics(CSpell * s);
|
||||||
|
@ -67,8 +67,11 @@ public:
|
|||||||
void sendAndApply(CPackForClient * info) const override;
|
void sendAndApply(CPackForClient * info) const override;
|
||||||
CRandomGenerator & getRandomGenerator() const override;
|
CRandomGenerator & getRandomGenerator() const override;
|
||||||
void complain(const std::string & problem) const override;
|
void complain(const std::string & problem) const override;
|
||||||
|
const CMap * getMap() const override;
|
||||||
|
const CGameInfoCallback * getCb() const override;
|
||||||
|
bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) const override;
|
||||||
private:
|
private:
|
||||||
CGameHandler * gh;
|
mutable CGameHandler * gh;
|
||||||
};
|
};
|
||||||
|
|
||||||
CondSh<bool> battleMadeAction;
|
CondSh<bool> battleMadeAction;
|
||||||
@ -4970,222 +4973,12 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
|
|||||||
bool CGameHandler::castSpell(const CGHeroInstance *h, SpellID spellID, const int3 &pos)
|
bool CGameHandler::castSpell(const CGHeroInstance *h, SpellID spellID, const int3 &pos)
|
||||||
{
|
{
|
||||||
const CSpell *s = spellID.toSpell();
|
const CSpell *s = spellID.toSpell();
|
||||||
int cost = h->getSpellCost(s);
|
|
||||||
int schoolLevel = h->getSpellSchoolLevel(s);
|
AdventureSpellCastParameters p;
|
||||||
|
p.caster = h;
|
||||||
if(!h->canCastThisSpell(s))
|
p.pos = pos;
|
||||||
COMPLAIN_RET("Hero cannot cast this spell!");
|
|
||||||
if(h->mana < cost)
|
return s->adventureCast(spellEnv, p);
|
||||||
COMPLAIN_RET("Hero doesn't have enough spell points to cast this spell!");
|
|
||||||
if(s->combatSpell)
|
|
||||||
COMPLAIN_RET("This function can be used only for adventure map spells!");
|
|
||||||
|
|
||||||
AdvmapSpellCast asc;
|
|
||||||
asc.caster = h;
|
|
||||||
asc.spellID = spellID;
|
|
||||||
sendAndApply(&asc);
|
|
||||||
|
|
||||||
switch(spellID)
|
|
||||||
{
|
|
||||||
case SpellID::SUMMON_BOAT:
|
|
||||||
{
|
|
||||||
//check if spell works at all
|
|
||||||
if(gs->getRandomGenerator().nextInt(99) >= s->getPower(schoolLevel)) //power is % chance of success
|
|
||||||
{
|
|
||||||
InfoWindow iw;
|
|
||||||
iw.player = h->tempOwner;
|
|
||||||
iw.text.addTxt(MetaString::GENERAL_TXT, 336); //%s tried to summon a boat, but failed.
|
|
||||||
iw.text.addReplacement(h->name);
|
|
||||||
sendAndApply(&iw);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//try to find unoccupied boat to summon
|
|
||||||
const CGBoat *nearest = nullptr;
|
|
||||||
double dist = 0;
|
|
||||||
int3 summonPos = h->bestLocation();
|
|
||||||
if(summonPos.x < 0)
|
|
||||||
COMPLAIN_RET("There is no water tile available!");
|
|
||||||
|
|
||||||
for(const CGObjectInstance *obj : gs->map->objects)
|
|
||||||
{
|
|
||||||
if(obj && obj->ID == Obj::BOAT)
|
|
||||||
{
|
|
||||||
const CGBoat *b = static_cast<const CGBoat*>(obj);
|
|
||||||
if(b->hero) continue; //we're looking for unoccupied boat
|
|
||||||
|
|
||||||
double nDist = distance(b->pos, h->getPosition());
|
|
||||||
if(!nearest || nDist < dist) //it's first boat or closer than previous
|
|
||||||
{
|
|
||||||
nearest = b;
|
|
||||||
dist = nDist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nearest) //we found boat to summon
|
|
||||||
{
|
|
||||||
ChangeObjPos cop;
|
|
||||||
cop.objid = nearest->id;
|
|
||||||
cop.nPos = summonPos + int3(1,0,0);;
|
|
||||||
cop.flags = 1;
|
|
||||||
sendAndApply(&cop);
|
|
||||||
}
|
|
||||||
else if(schoolLevel < 2) //none or basic level -> cannot create boat :(
|
|
||||||
{
|
|
||||||
InfoWindow iw;
|
|
||||||
iw.player = h->tempOwner;
|
|
||||||
iw.text.addTxt(MetaString::GENERAL_TXT, 335); //There are no boats to summon.
|
|
||||||
sendAndApply(&iw);
|
|
||||||
}
|
|
||||||
else //create boat
|
|
||||||
{
|
|
||||||
NewObject no;
|
|
||||||
no.ID = Obj::BOAT;
|
|
||||||
no.subID = h->getBoatType();
|
|
||||||
no.pos = summonPos + int3(1,0,0);;
|
|
||||||
sendAndApply(&no);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SpellID::SCUTTLE_BOAT:
|
|
||||||
{
|
|
||||||
//check if spell works at all
|
|
||||||
if(gs->getRandomGenerator().nextInt(99) >= s->getPower(schoolLevel)) //power is % chance of success
|
|
||||||
{
|
|
||||||
InfoWindow iw;
|
|
||||||
iw.player = h->tempOwner;
|
|
||||||
iw.text.addTxt(MetaString::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed
|
|
||||||
iw.text.addReplacement(h->name);
|
|
||||||
sendAndApply(&iw);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(!gs->map->isInTheMap(pos))
|
|
||||||
COMPLAIN_RET("Invalid dst tile for scuttle!");
|
|
||||||
|
|
||||||
//TODO: test range, visibility
|
|
||||||
const TerrainTile *t = &gs->map->getTile(pos);
|
|
||||||
if(!t->visitableObjects.size() || t->visitableObjects.back()->ID != Obj::BOAT)
|
|
||||||
COMPLAIN_RET("There is no boat to scuttle!");
|
|
||||||
|
|
||||||
RemoveObject ro;
|
|
||||||
ro.id = t->visitableObjects.back()->id;
|
|
||||||
sendAndApply(&ro);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SpellID::DIMENSION_DOOR:
|
|
||||||
{
|
|
||||||
const TerrainTile *dest = getTile(pos);
|
|
||||||
const TerrainTile *curr = getTile(h->getSightCenter());
|
|
||||||
|
|
||||||
if(!dest)
|
|
||||||
COMPLAIN_RET("Destination tile doesn't exist!");
|
|
||||||
if(!h->movement)
|
|
||||||
COMPLAIN_RET("Hero needs movement points to cast Dimension Door!");
|
|
||||||
if(h->getBonusesCount(Bonus::SPELL_EFFECT, SpellID::DIMENSION_DOOR) >= s->getPower(schoolLevel)) //limit casts per turn
|
|
||||||
{
|
|
||||||
InfoWindow iw;
|
|
||||||
iw.player = h->tempOwner;
|
|
||||||
iw.text.addTxt(MetaString::GENERAL_TXT, 338); //%s is not skilled enough to cast this spell again today.
|
|
||||||
iw.text.addReplacement(h->name);
|
|
||||||
sendAndApply(&iw);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
GiveBonus gb;
|
|
||||||
gb.id = h->id.getNum();
|
|
||||||
gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::NONE, Bonus::SPELL_EFFECT, 0, SpellID::DIMENSION_DOOR);
|
|
||||||
sendAndApply(&gb);
|
|
||||||
|
|
||||||
if(!dest->isClear(curr)) //wrong dest tile
|
|
||||||
{
|
|
||||||
InfoWindow iw;
|
|
||||||
iw.player = h->tempOwner;
|
|
||||||
iw.text.addTxt(MetaString::GENERAL_TXT, 70); //Dimension Door failed!
|
|
||||||
sendAndApply(&iw);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (moveHero(h->id, pos + h->getVisitableOffset(), true))
|
|
||||||
{
|
|
||||||
SetMovePoints smp;
|
|
||||||
smp.hid = h->id;
|
|
||||||
smp.val = std::max<ui32>(0, h->movement - 300);
|
|
||||||
sendAndApply(&smp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SpellID::FLY:
|
|
||||||
{
|
|
||||||
int subtype = schoolLevel >= 2 ? 1 : 2; //adv or expert
|
|
||||||
|
|
||||||
GiveBonus gb;
|
|
||||||
gb.id = h->id.getNum();
|
|
||||||
gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::FLYING_MOVEMENT, Bonus::SPELL_EFFECT, 0, SpellID::FLY, subtype);
|
|
||||||
sendAndApply(&gb);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SpellID::WATER_WALK:
|
|
||||||
{
|
|
||||||
int subtype = schoolLevel >= 2 ? 1 : 2; //adv or expert
|
|
||||||
|
|
||||||
GiveBonus gb;
|
|
||||||
gb.id = h->id.getNum();
|
|
||||||
gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::WATER_WALKING, Bonus::SPELL_EFFECT, 0, SpellID::WATER_WALK, subtype);
|
|
||||||
sendAndApply(&gb);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SpellID::TOWN_PORTAL:
|
|
||||||
{
|
|
||||||
if (!gs->map->isInTheMap(pos))
|
|
||||||
COMPLAIN_RET("Destination tile not present!")
|
|
||||||
TerrainTile tile = gs->map->getTile(pos);
|
|
||||||
if (tile.visitableObjects.empty() || tile.visitableObjects.back()->ID != Obj::TOWN )
|
|
||||||
COMPLAIN_RET("Town not found for Town Portal!");
|
|
||||||
|
|
||||||
CGTownInstance * town = static_cast<CGTownInstance*>(tile.visitableObjects.back());
|
|
||||||
if (town->tempOwner != h->tempOwner)
|
|
||||||
COMPLAIN_RET("Can't teleport to another player!");
|
|
||||||
if (town->visitingHero)
|
|
||||||
COMPLAIN_RET("Can't teleport to occupied town!");
|
|
||||||
|
|
||||||
if (h->getSpellSchoolLevel(s) < 2)
|
|
||||||
{
|
|
||||||
si32 dist = town->pos.dist2dSQ(h->pos);
|
|
||||||
ObjectInstanceID nearest = town->id; //nearest town's ID
|
|
||||||
for(const CGTownInstance * currTown : gs->getPlayer(h->tempOwner)->towns)
|
|
||||||
{
|
|
||||||
si32 currDist = currTown->pos.dist2dSQ(h->pos);
|
|
||||||
if (currDist < dist)
|
|
||||||
{
|
|
||||||
nearest = currTown->id;
|
|
||||||
dist = currDist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (town->id != nearest)
|
|
||||||
COMPLAIN_RET("This hero can only teleport to nearest town!")
|
|
||||||
}
|
|
||||||
moveHero(h->id, town->visitablePos() + h->getVisitableOffset() ,1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SpellID::VISIONS:
|
|
||||||
case SpellID::VIEW_EARTH:
|
|
||||||
case SpellID::DISGUISE:
|
|
||||||
case SpellID::VIEW_AIR:
|
|
||||||
default:
|
|
||||||
COMPLAIN_RET("This spell is not implemented yet!");
|
|
||||||
}
|
|
||||||
|
|
||||||
SetMana sm;
|
|
||||||
sm.hid = h->id;
|
|
||||||
sm.absolute = false;
|
|
||||||
sm.val = -cost;
|
|
||||||
sendAndApply(&sm);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h)
|
void CGameHandler::visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h)
|
||||||
@ -6010,7 +5803,7 @@ CGameHandler::FinishingBattleHelper::FinishingBattleHelper()
|
|||||||
winnerHero = loserHero = nullptr;
|
winnerHero = loserHero = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///ServerSpellCastEnvironment
|
||||||
ServerSpellCastEnvironment::ServerSpellCastEnvironment(CGameHandler * gh): gh(gh)
|
ServerSpellCastEnvironment::ServerSpellCastEnvironment(CGameHandler * gh): gh(gh)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -6030,3 +5823,20 @@ void ServerSpellCastEnvironment::complain(const std::string& problem) const
|
|||||||
{
|
{
|
||||||
gh->complain(problem);
|
gh->complain(problem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CGameInfoCallback * ServerSpellCastEnvironment::getCb() const
|
||||||
|
{
|
||||||
|
return gh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const CMap * ServerSpellCastEnvironment::getMap() const
|
||||||
|
{
|
||||||
|
return gh->gameState()->map;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServerSpellCastEnvironment::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker) const
|
||||||
|
{
|
||||||
|
return gh->moveHero(hid, dst, teleporting, asker);
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user