mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +02:00
Merge branch 'develop' of https://github.com/vcmi/vcmi into develop
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include "CAdvmapInterface.h"
|
||||
#include "GUIClasses.h"
|
||||
#include "InfoWindows.h"
|
||||
#include "CCastleInterface.h"
|
||||
|
||||
#include "../CBitmapHandler.h"
|
||||
#include "../CDefHandler.h"
|
||||
@@ -109,7 +110,7 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
|
||||
Uint8 *sitesPerOurTab = s.combatSpell ? sitesPerTabBattle : sitesPerTabAdv;
|
||||
|
||||
++sitesPerOurTab[4];
|
||||
|
||||
|
||||
s.forEachSchool([&sitesPerOurTab](const SpellSchoolInfo & school, bool & stop)
|
||||
{
|
||||
++sitesPerOurTab[(ui8)school.id];
|
||||
@@ -156,9 +157,9 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
|
||||
|
||||
leftCorner = BitmapHandler::loadBitmap("SpelTrnL.bmp", true);
|
||||
rightCorner = BitmapHandler::loadBitmap("SpelTrnR.bmp", true);
|
||||
|
||||
|
||||
spells = new CAnimation("Spells.def");
|
||||
|
||||
|
||||
spellTab = CDefHandler::giveDef("SpelTab.def");
|
||||
schools = CDefHandler::giveDef("Schools.def");
|
||||
schoolBorders[0] = CDefHandler::giveDef("SplevA.def");
|
||||
@@ -380,9 +381,9 @@ public:
|
||||
if(A.level<B.level)
|
||||
return true;
|
||||
if(A.level>B.level)
|
||||
return false;
|
||||
|
||||
|
||||
return false;
|
||||
|
||||
|
||||
for(ui8 schoolId = 0; schoolId < 4; schoolId++)
|
||||
{
|
||||
if(A.school.at((ESpellSchool)schoolId) && !B.school.at((ESpellSchool)schoolId))
|
||||
@@ -390,7 +391,7 @@ public:
|
||||
if(!A.school.at((ESpellSchool)schoolId) && B.school.at((ESpellSchool)schoolId))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return A.name < B.name;
|
||||
}
|
||||
} spellsorter;
|
||||
@@ -400,8 +401,8 @@ void CSpellWindow::computeSpellsPerArea()
|
||||
std::vector<SpellID> spellsCurSite;
|
||||
for(const SpellID & spellID : mySpells)
|
||||
{
|
||||
CSpell * s = spellID.toSpell();
|
||||
|
||||
CSpell * s = spellID.toSpell();
|
||||
|
||||
if(s->combatSpell ^ !battleSpellsOnly
|
||||
&& ((selectedTab == 4) || (s->school[(ESpellSchool)selectedTab]))
|
||||
)
|
||||
@@ -533,7 +534,7 @@ void CSpellWindow::keyPressed(const SDL_KeyboardEvent & key)
|
||||
{
|
||||
fexitb();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(key.state == SDL_PRESSED)
|
||||
{
|
||||
@@ -622,10 +623,9 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
||||
owner->myInt->showInfoDialog(std::string(msgBuf));
|
||||
return;
|
||||
}
|
||||
|
||||
//battle spell on adv map or adventure map spell during combat => display infowindow, not cast
|
||||
if((sp->isCombatSpell() && !owner->myInt->battleInt)
|
||||
|| (sp->isAdventureSpell() && owner->myInt->battleInt))
|
||||
|| (sp->isAdventureSpell() && (owner->myInt->battleInt || owner->myInt->castleInt)))
|
||||
{
|
||||
std::vector<CComponent*> hlp(1, new CComponent(CComponent::spell, mySpell, 0));
|
||||
LOCPLINT->showInfoDialog(sp->getLevelInfo(schoolLevel).description, hlp);
|
||||
@@ -705,26 +705,26 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
const CGHeroInstance *h = owner->myHero;
|
||||
GH.popInt(owner);
|
||||
|
||||
auto guard = vstd::makeScopeGuard([this]
|
||||
|
||||
auto guard = vstd::makeScopeGuard([this]
|
||||
{
|
||||
(LOCPLINT->battleInt ? owner->myInt->spellbookSettings.spellbookLastTabBattle : owner->myInt->spellbookSettings.spellbookLastTabAdvmap) = owner->selectedTab;
|
||||
(LOCPLINT->battleInt ? owner->myInt->spellbookSettings.spellbookLastPageBattle : owner->myInt->spellbookSettings.spellbokLastPageAdvmap) = owner->currentPage;
|
||||
(LOCPLINT->battleInt ? owner->myInt->spellbookSettings.spellbookLastPageBattle : owner->myInt->spellbookSettings.spellbokLastPageAdvmap) = owner->currentPage;
|
||||
delete owner;
|
||||
});
|
||||
});
|
||||
|
||||
if(mySpell == SpellID::TOWN_PORTAL)
|
||||
{
|
||||
//special case
|
||||
//todo: move to mechanics
|
||||
|
||||
|
||||
std::vector <int> availableTowns;
|
||||
std::vector <const CGTownInstance*> Towns = LOCPLINT->cb->getTownsInfo(false);
|
||||
|
||||
vstd::erase_if(Towns, [this](const CGTownInstance * t)
|
||||
{
|
||||
const auto relations = owner->myInt->cb->getPlayerRelations(t->tempOwner, owner->myInt->playerID);
|
||||
return relations == PlayerRelations::ENEMIES;
|
||||
const auto relations = owner->myInt->cb->getPlayerRelations(t->tempOwner, owner->myInt->playerID);
|
||||
return relations == PlayerRelations::ENEMIES;
|
||||
});
|
||||
|
||||
if (Towns.empty())
|
||||
@@ -776,13 +776,13 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
||||
availableTowns.push_back(t->id.getNum());//add to the list
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
auto castTownPortal = [h](int townId)
|
||||
{
|
||||
const CGTownInstance * dest = LOCPLINT->cb->getTown(ObjectInstanceID(townId));
|
||||
LOCPLINT->cb->castSpell(h, SpellID::TOWN_PORTAL, dest->visitablePos());
|
||||
LOCPLINT->cb->castSpell(h, SpellID::TOWN_PORTAL, dest->visitablePos());
|
||||
};
|
||||
|
||||
|
||||
if (availableTowns.empty())
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[124]);
|
||||
else
|
||||
@@ -791,13 +791,13 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
||||
CGI->generaltexth->jktexts[40], CGI->generaltexth->jktexts[41],
|
||||
castTownPortal));
|
||||
}
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(mySpell == SpellID::SUMMON_BOAT)
|
||||
{
|
||||
//special case
|
||||
//todo: move to mechanics
|
||||
//todo: move to mechanics
|
||||
int3 pos = h->bestLocation();
|
||||
if(pos.x < 0)
|
||||
{
|
||||
@@ -805,10 +805,10 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(sp->getTargetType() == CSpell::LOCATION)
|
||||
{
|
||||
adventureInt->enterCastingMode(sp);
|
||||
adventureInt->enterCastingMode(sp);
|
||||
}
|
||||
else if(sp->getTargetType() == CSpell::NO_TARGET)
|
||||
{
|
||||
@@ -817,7 +817,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
||||
else
|
||||
{
|
||||
logGlobal->error("Invalid spell target type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -865,11 +865,11 @@ void CSpellWindow::SpellArea::showAll(SDL_Surface * to)
|
||||
if(mySpell < 0)
|
||||
return;
|
||||
|
||||
const CSpell * spell = mySpell.toSpell();
|
||||
const CSpell * spell = mySpell.toSpell();
|
||||
owner->spells->load(mySpell);
|
||||
|
||||
|
||||
IImage * icon = owner->spells->getImage(mySpell,0,false);
|
||||
|
||||
|
||||
if(icon != nullptr)
|
||||
icon->draw(to, pos.x, pos.y);
|
||||
else
|
||||
|
@@ -187,11 +187,11 @@ TStacks CBattleInfoEssentials::battleGetStacksIf(TStackFilter predicate, bool in
|
||||
{
|
||||
TStacks ret;
|
||||
RETURN_IF_NOT_BATTLE(ret);
|
||||
|
||||
|
||||
vstd::copy_if(getBattle()->stacks, std::back_inserter(ret), [=](const CStack * s){
|
||||
return predicate(s) && (includeTurrets || !(s->type->idNumber == CreatureID::ARROW_TOWERS));
|
||||
});
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -784,23 +784,23 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const CStack
|
||||
bool CBattleInfoCallback::battleCanAttack(const CStack * stack, const CStack * target, BattleHex dest) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(false);
|
||||
|
||||
|
||||
if(battleTacticDist())
|
||||
return false;
|
||||
|
||||
|
||||
if (!stack || !target)
|
||||
return false;
|
||||
|
||||
|
||||
if (stack->owner == target->owner)
|
||||
return false;
|
||||
|
||||
|
||||
auto &id = stack->getCreature()->idNumber;
|
||||
if (id == CreatureID::FIRST_AID_TENT || id == CreatureID::CATAPULT)
|
||||
return false;
|
||||
|
||||
|
||||
if (!target->alive())
|
||||
return false;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -997,7 +997,7 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
|
||||
const bool distPenalty = !info.attackerBonuses->hasBonusOfType(Bonus::NO_DISTANCE_PENALTY) && battleHasDistancePenalty(info.attackerBonuses, info.attackerPosition, info.defenderPosition);
|
||||
const bool obstaclePenalty = battleHasWallPenalty(info.attackerBonuses, info.attackerPosition, info.defenderPosition);
|
||||
|
||||
if (info.shooting)
|
||||
if(info.shooting)
|
||||
{
|
||||
if (distPenalty || info.defenderBonuses->hasBonus(isAdvancedAirShield))
|
||||
{
|
||||
@@ -1013,9 +1013,14 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
|
||||
multBonus *= 0.5;
|
||||
}
|
||||
|
||||
// psychic elementals versus mind immune units 50%
|
||||
if(attackerType->idNumber == CreatureID::PSYCHIC_ELEMENTAL
|
||||
&& info.defenderBonuses->hasBonusOfType(Bonus::MIND_IMMUNITY))
|
||||
{
|
||||
multBonus *= 0.5;
|
||||
}
|
||||
|
||||
// TODO attack on petrified unit 50%
|
||||
// psychic elementals versus mind immune units 50%
|
||||
// blinded unit retaliates
|
||||
|
||||
minDmg *= additiveBonus * multBonus;
|
||||
@@ -1265,8 +1270,8 @@ std::pair<const CStack *, BattleHex> CBattleInfoCallback::getNearestStack(const
|
||||
// I hate std::pairs with their undescriptive member names first / second
|
||||
struct DistStack
|
||||
{
|
||||
int distanceToPred;
|
||||
BattleHex destination;
|
||||
int distanceToPred;
|
||||
BattleHex destination;
|
||||
const CStack *stack;
|
||||
};
|
||||
|
||||
@@ -1276,7 +1281,7 @@ std::pair<const CStack *, BattleHex> CBattleInfoCallback::getNearestStack(const
|
||||
{
|
||||
return s != closest && s->alive() && (boost::logic::indeterminate(attackerOwned) || s->attackerOwned == attackerOwned);
|
||||
}, false);
|
||||
|
||||
|
||||
for(const CStack * st : possibleStacks)
|
||||
for(BattleHex hex : avHexes)
|
||||
if(CStack::isMeleeAttackPossible(closest, st, hex))
|
||||
@@ -1628,9 +1633,9 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
||||
return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
|
||||
|
||||
const ESpellCastProblem::ESpellCastProblem specificProblem = spell->canBeCast(this, player);
|
||||
|
||||
|
||||
if(specificProblem != ESpellCastProblem::OK)
|
||||
return specificProblem;
|
||||
return specificProblem;
|
||||
|
||||
if(spell->isNegative() || spell->hasEffects())
|
||||
{
|
||||
@@ -1667,7 +1672,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
||||
{
|
||||
bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, stack);
|
||||
bool casterStack = stack->owner == caster->getOwner();
|
||||
|
||||
|
||||
if(!immune)
|
||||
{
|
||||
switch (spell->positiveness)
|
||||
@@ -1716,12 +1721,12 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(PlayerColor
|
||||
{
|
||||
const CGHeroInstance * caster = battleGetFightingHero(playerToSide(player)); //TODO
|
||||
const CSpell::TargetInfo ti(spell, caster->getSpellSchoolLevel(spell));
|
||||
|
||||
|
||||
for(const CStack * stack : battleAliveStacks())
|
||||
{
|
||||
bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, stack);
|
||||
bool casterStack = stack->owner == caster->getOwner();
|
||||
|
||||
|
||||
if(!immune)
|
||||
switch (spell->positiveness)
|
||||
{
|
||||
@@ -1784,7 +1789,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
||||
{
|
||||
logGlobal->errorStream() << "CBattleInfoCallback::battleCanCastThisSpellHere: no spellcaster.";
|
||||
return ESpellCastProblem::INVALID;
|
||||
}
|
||||
}
|
||||
const PlayerColor player = caster->getOwner();
|
||||
ESpellCastProblem::ESpellCastProblem moreGeneralProblem = battleCanCastThisSpell(caster, spell, mode);
|
||||
if(moreGeneralProblem != ESpellCastProblem::OK)
|
||||
@@ -1894,19 +1899,19 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co
|
||||
RETURN_IF_NOT_BATTLE(SpellID::NONE);
|
||||
//This is complete list. No spells from mods.
|
||||
//todo: this should be Spellbook of caster Stack
|
||||
static const std::set<SpellID> allPossibleSpells =
|
||||
static const std::set<SpellID> allPossibleSpells =
|
||||
{
|
||||
SpellID::AIR_SHIELD,
|
||||
SpellID::ANTI_MAGIC,
|
||||
SpellID::BLESS,
|
||||
SpellID::BLESS,
|
||||
SpellID::BLOODLUST,
|
||||
SpellID::COUNTERSTRIKE,
|
||||
SpellID::CURE,
|
||||
SpellID::FIRE_SHIELD,
|
||||
SpellID::FIRE_SHIELD,
|
||||
SpellID::FORTUNE,
|
||||
SpellID::HASTE,
|
||||
SpellID::MAGIC_MIRROR,
|
||||
SpellID::MIRTH,
|
||||
SpellID::MIRTH,
|
||||
SpellID::PRAYER,
|
||||
SpellID::PRECISION,
|
||||
SpellID::PROTECTION_FROM_AIR,
|
||||
@@ -1918,7 +1923,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co
|
||||
SpellID::STONE_SKIN
|
||||
};
|
||||
std::vector<SpellID> beneficialSpells;
|
||||
|
||||
|
||||
auto getAliveEnemy = [=](const std::function<bool(const CStack * )> & pred)
|
||||
{
|
||||
return getStackIf([=](const CStack * stack)
|
||||
@@ -1938,7 +1943,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co
|
||||
{
|
||||
case SpellID::SHIELD:
|
||||
case SpellID::FIRE_SHIELD: // not if all enemy units are shooters
|
||||
{
|
||||
{
|
||||
auto walker = getAliveEnemy([&](const CStack * stack) //look for enemy, non-shooting stack
|
||||
{
|
||||
return !stack->shots;
|
||||
@@ -1963,7 +1968,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co
|
||||
case SpellID::PROTECTION_FROM_AIR:
|
||||
case SpellID::PROTECTION_FROM_EARTH:
|
||||
case SpellID::PROTECTION_FROM_FIRE:
|
||||
case SpellID::PROTECTION_FROM_WATER:
|
||||
case SpellID::PROTECTION_FROM_WATER:
|
||||
{
|
||||
const ui8 enemySide = (ui8)subject->attackerOwned;
|
||||
//todo: only if enemy has spellbook
|
||||
@@ -2006,7 +2011,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co
|
||||
}
|
||||
break;
|
||||
}
|
||||
beneficialSpells.push_back(spellID);
|
||||
beneficialSpells.push_back(spellID);
|
||||
}
|
||||
|
||||
if(!beneficialSpells.empty())
|
||||
@@ -2190,13 +2195,13 @@ TStacks CPlayerBattleCallback::battleGetStacks(EStackOwnership whose /*= MINE_AN
|
||||
{
|
||||
ASSERT_IF_CALLED_WITH_PLAYER
|
||||
}
|
||||
|
||||
|
||||
return battleGetStacksIf([=](const CStack * s){
|
||||
const bool ownerMatches = (whose == MINE_AND_ENEMY)
|
||||
|| (whose == ONLY_MINE && s->owner == player)
|
||||
|| (whose == ONLY_ENEMY && s->owner != player);
|
||||
const bool alivenessMatches = s->alive() || !onlyAlive;
|
||||
return ownerMatches && alivenessMatches;
|
||||
return ownerMatches && alivenessMatches;
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -968,6 +968,7 @@ public:
|
||||
WATER_ELEMENTAL = 115,
|
||||
GOLD_GOLEM = 116,
|
||||
DIAMOND_GOLEM = 117,
|
||||
PSYCHIC_ELEMENTAL = 120,
|
||||
CATAPULT = 145,
|
||||
BALLISTA = 146,
|
||||
FIRST_AID_TENT = 147,
|
||||
@@ -1053,7 +1054,3 @@ typedef int TRmgTemplateZoneId;
|
||||
#undef ID_LIKE_OPERATORS_INTERNAL
|
||||
#undef INSTID_LIKE_CLASS_COMMON
|
||||
#undef OP_DECL_INT
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -12,7 +12,7 @@
|
||||
#include "CDrawRoadsOperation.h"
|
||||
#include "CMap.h"
|
||||
|
||||
const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::patterns =
|
||||
const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::patterns =
|
||||
{
|
||||
//single tile. fall-back pattern
|
||||
{
|
||||
@@ -31,7 +31,7 @@ const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::pattern
|
||||
{
|
||||
"?","-","+",
|
||||
"-","+","+",
|
||||
"+","+","?"
|
||||
"+","+","?"
|
||||
},
|
||||
{2,5},
|
||||
{-1,-1},
|
||||
@@ -43,7 +43,7 @@ const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::pattern
|
||||
{
|
||||
"?","-","?",
|
||||
"-","+","+",
|
||||
"?","+","?"
|
||||
"?","+","?"
|
||||
},
|
||||
{0,1},
|
||||
{0,3},
|
||||
@@ -55,7 +55,7 @@ const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::pattern
|
||||
{
|
||||
"?","-","?",
|
||||
"-","+","+",
|
||||
"?","-","?"
|
||||
"?","-","?"
|
||||
},
|
||||
{15,15},{11,12},
|
||||
true,
|
||||
@@ -66,7 +66,7 @@ const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::pattern
|
||||
{
|
||||
"?","-","?",
|
||||
"-","+","-",
|
||||
"?","+","?"
|
||||
"?","+","?"
|
||||
},
|
||||
{14,14},{9,10},
|
||||
false,
|
||||
@@ -77,7 +77,7 @@ const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::pattern
|
||||
{
|
||||
"?","+","?",
|
||||
"-","+","+",
|
||||
"?","+","?"
|
||||
"?","+","?"
|
||||
},
|
||||
{6,7},{7,8},
|
||||
true,
|
||||
@@ -88,46 +88,46 @@ const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::pattern
|
||||
{
|
||||
"?","-","?",
|
||||
"+","+","+",
|
||||
"?","+","?"
|
||||
"?","+","?"
|
||||
},
|
||||
{8,9},{5,6},
|
||||
false,
|
||||
true
|
||||
},
|
||||
//Straight Horizontal
|
||||
//Straight Horizontal
|
||||
{
|
||||
{
|
||||
"?","-","?",
|
||||
"+","+","+",
|
||||
"?","-","?"
|
||||
"?","-","?"
|
||||
},
|
||||
{12,13},{11,12},
|
||||
false,
|
||||
false
|
||||
},
|
||||
//Straight Vertical
|
||||
//Straight Vertical
|
||||
{
|
||||
{
|
||||
"?","+","?",
|
||||
"-","+","-",
|
||||
"?","+","?"
|
||||
"?","+","?"
|
||||
},
|
||||
{10,11},{9,10},
|
||||
false,
|
||||
false
|
||||
},
|
||||
//X-cross
|
||||
//X-cross
|
||||
{
|
||||
{
|
||||
"?","+","?",
|
||||
"+","+","+",
|
||||
"?","+","?"
|
||||
"?","+","?"
|
||||
},
|
||||
{16,16},{4,4},
|
||||
false,
|
||||
false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static bool ruleIsNone(const std::string & rule)
|
||||
@@ -140,45 +140,47 @@ static bool ruleIsSomething(const std::string & rule)
|
||||
return rule == "+";
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
static bool ruleIsAny(const std::string & rule)
|
||||
{
|
||||
return rule == "?";
|
||||
}
|
||||
#endif
|
||||
|
||||
///CDrawRoadsOperation
|
||||
CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, ERoadType::ERoadType roadType, CRandomGenerator * gen):
|
||||
CMapOperation(map),terrainSel(terrainSel), roadType(roadType), gen(gen)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
void CDrawRoadsOperation::execute()
|
||||
{
|
||||
std::set<int3> invalidated;
|
||||
|
||||
|
||||
for(const auto & pos : terrainSel.getSelectedItems())
|
||||
{
|
||||
auto & tile = map->getTile(pos);
|
||||
tile.roadType = roadType;
|
||||
|
||||
|
||||
auto rect = extendTileAroundSafely(pos);
|
||||
rect.forEach([&invalidated](const int3 & pos)
|
||||
{
|
||||
invalidated.insert(pos);
|
||||
});
|
||||
}
|
||||
|
||||
updateTiles(invalidated);
|
||||
|
||||
updateTiles(invalidated);
|
||||
}
|
||||
|
||||
void CDrawRoadsOperation::undo()
|
||||
{
|
||||
//TODO
|
||||
//TODO
|
||||
}
|
||||
|
||||
void CDrawRoadsOperation::redo()
|
||||
{
|
||||
//TODO
|
||||
//TODO
|
||||
}
|
||||
|
||||
std::string CDrawRoadsOperation::getLabel() const
|
||||
@@ -188,14 +190,14 @@ std::string CDrawRoadsOperation::getLabel() const
|
||||
|
||||
bool CDrawRoadsOperation::canApplyPattern(const RoadPattern & pattern) const
|
||||
{
|
||||
//TODO: this method should be virtual for river support
|
||||
//TODO: this method should be virtual for river support
|
||||
return pattern.roadMapping.first >= 0;
|
||||
}
|
||||
|
||||
void CDrawRoadsOperation::flipPattern(RoadPattern& pattern, int flip) const
|
||||
{
|
||||
//todo: use cashing here and also in terrain patterns
|
||||
|
||||
|
||||
if(flip == 0)
|
||||
{
|
||||
return;
|
||||
@@ -217,7 +219,7 @@ void CDrawRoadsOperation::flipPattern(RoadPattern& pattern, int flip) const
|
||||
{
|
||||
std::swap(pattern.data[i], pattern.data[6 + i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -226,98 +228,98 @@ bool CDrawRoadsOperation::needUpdateTile(const TerrainTile & tile) const
|
||||
return tile.roadType != ERoadType::NO_ROAD; //TODO: this method should be virtual for river support
|
||||
}
|
||||
|
||||
void CDrawRoadsOperation::updateTiles(std::set<int3> & invalidated)
|
||||
void CDrawRoadsOperation::updateTiles(std::set<int3> & invalidated)
|
||||
{
|
||||
for(int3 coord : invalidated)
|
||||
{
|
||||
TerrainTile & tile = map->getTile(coord);
|
||||
ValidationResult result(false);
|
||||
|
||||
|
||||
if(!needUpdateTile(tile))
|
||||
continue;
|
||||
|
||||
|
||||
int bestPattern = -1;
|
||||
|
||||
|
||||
for(int k = 0; k < patterns.size(); ++k)
|
||||
{
|
||||
result = validateTile(patterns[k], coord);
|
||||
|
||||
|
||||
if(result.result)
|
||||
{
|
||||
bestPattern = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(bestPattern != -1)
|
||||
{
|
||||
updateTile(tile, patterns[bestPattern], result.flip);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
bool CDrawRoadsOperation::tileHasSomething(const int3& pos) const
|
||||
{
|
||||
//TODO: this method should be virtual for river support
|
||||
//TODO: this method should be virtual for river support
|
||||
|
||||
return map->getTile(pos).roadType != ERoadType::NO_ROAD;
|
||||
return map->getTile(pos).roadType != ERoadType::NO_ROAD;
|
||||
}
|
||||
|
||||
|
||||
void CDrawRoadsOperation::updateTile(TerrainTile & tile, const RoadPattern & pattern, const int flip)
|
||||
{
|
||||
//TODO: this method should be virtual for river support
|
||||
|
||||
//TODO: this method should be virtual for river support
|
||||
|
||||
const std::pair<int, int> & mapping = pattern.roadMapping;
|
||||
|
||||
|
||||
tile.roadDir = gen->nextInt(mapping.first, mapping.second);
|
||||
tile.extTileFlags = (tile.extTileFlags & 0xCF) | (flip << 4);
|
||||
tile.extTileFlags = (tile.extTileFlags & 0xCF) | (flip << 4);
|
||||
}
|
||||
|
||||
CDrawRoadsOperation::ValidationResult CDrawRoadsOperation::validateTile(const RoadPattern & pattern, const int3 & pos)
|
||||
{
|
||||
ValidationResult result(false);
|
||||
|
||||
|
||||
if(!canApplyPattern(pattern))
|
||||
return result;
|
||||
|
||||
|
||||
|
||||
|
||||
for(int flip = 0; flip < 4; ++flip)
|
||||
{
|
||||
if((flip == FLIP_PATTERN_BOTH) && !(pattern.hasHFlip && pattern.hasVFlip))
|
||||
if((flip == FLIP_PATTERN_BOTH) && !(pattern.hasHFlip && pattern.hasVFlip))
|
||||
continue;
|
||||
if((flip == FLIP_PATTERN_HORIZONTAL) && !pattern.hasHFlip)
|
||||
if((flip == FLIP_PATTERN_HORIZONTAL) && !pattern.hasHFlip)
|
||||
continue;
|
||||
if((flip == FLIP_PATTERN_VERTICAL) && !(pattern.hasVFlip))
|
||||
if((flip == FLIP_PATTERN_VERTICAL) && !(pattern.hasVFlip))
|
||||
continue;
|
||||
|
||||
RoadPattern flipped = pattern;
|
||||
|
||||
|
||||
RoadPattern flipped = pattern;
|
||||
|
||||
flipPattern(flipped, flip);
|
||||
|
||||
|
||||
bool validated = true;
|
||||
|
||||
|
||||
for(int i = 0; i < 9; ++i)
|
||||
{
|
||||
if(4 == i)
|
||||
continue;
|
||||
int cx = pos.x + (i % 3) - 1;
|
||||
int cy = pos.y + (i / 3) - 1;
|
||||
|
||||
|
||||
int3 currentPos(cx, cy, pos.z);
|
||||
|
||||
|
||||
bool hasSomething;
|
||||
|
||||
|
||||
if(!map->isInTheMap(currentPos))
|
||||
{
|
||||
hasSomething = true; //road/river can go out of map
|
||||
}
|
||||
else
|
||||
{
|
||||
hasSomething = tileHasSomething(currentPos);
|
||||
hasSomething = tileHasSomething(currentPos);
|
||||
}
|
||||
|
||||
|
||||
if(ruleIsSomething(flipped.data[i]))
|
||||
{
|
||||
if(!hasSomething)
|
||||
@@ -332,23 +334,22 @@ CDrawRoadsOperation::ValidationResult CDrawRoadsOperation::validateTile(const Ro
|
||||
{
|
||||
validated = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(ruleIsAny(flipped.data[i]));
|
||||
}
|
||||
|
||||
assert(ruleIsAny(flipped.data[i]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(validated)
|
||||
{
|
||||
result.result = true;
|
||||
result.flip = flip;
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@@ -154,6 +154,13 @@ bool DefaultSpellMechanics::adventureCast(const SpellCastEnvironment * env, Adve
|
||||
}
|
||||
|
||||
const CGHeroInstance * caster = parameters.caster;
|
||||
|
||||
if(caster->inTownGarrison)
|
||||
{
|
||||
env->complain("Attempt to cast an adventure spell in town garrison");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int cost = caster->getSpellCost(owner);
|
||||
|
||||
if(!caster->canCastThisSpell(owner))
|
||||
@@ -174,7 +181,7 @@ bool DefaultSpellMechanics::adventureCast(const SpellCastEnvironment * env, Adve
|
||||
asc.spellID = owner->id;
|
||||
env->sendAndApply(&asc);
|
||||
}
|
||||
|
||||
|
||||
switch(applyAdventureEffects(env, parameters))
|
||||
{
|
||||
case ESpellCastResult::OK:
|
||||
@@ -184,7 +191,7 @@ bool DefaultSpellMechanics::adventureCast(const SpellCastEnvironment * env, Adve
|
||||
sm.absolute = false;
|
||||
sm.val = -cost;
|
||||
env->sendAndApply(&sm);
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case ESpellCastResult::CANCEL:
|
||||
@@ -224,16 +231,16 @@ ESpellCastResult DefaultSpellMechanics::applyAdventureEffects(const SpellCastEnv
|
||||
void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const
|
||||
{
|
||||
logGlobal->debugStream() << "Started spell cast. Spell: "<<owner->name<<"; mode:"<<parameters.mode;
|
||||
|
||||
|
||||
if(nullptr == parameters.caster)
|
||||
{
|
||||
env->complain("No spell-caster provided.");
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
BattleSpellCast sc;
|
||||
prepareBattleCast(parameters, sc);
|
||||
|
||||
|
||||
//check it there is opponent hero
|
||||
const ui8 otherSide = 1-parameters.casterSide;
|
||||
const CGHeroInstance * otherHero = nullptr;
|
||||
@@ -247,7 +254,7 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
|
||||
spellCost = parameters.cb->battleGetSpellCost(owner, parameters.casterHero);
|
||||
|
||||
if(nullptr != otherHero) //handle mana channel
|
||||
{
|
||||
{
|
||||
int manaChannel = 0;
|
||||
for(const CStack * stack : parameters.cb->battleGetAllStacks(true)) //TODO: shouldn't bonus system handle it somehow?
|
||||
{
|
||||
@@ -271,10 +278,10 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
|
||||
logGlobal->debugStream() << "will affect: " << attackedCres.size() << " stacks";
|
||||
|
||||
std::vector <const CStack*> reflected;//for magic mirror
|
||||
//checking if creatures resist
|
||||
//checking if creatures resist
|
||||
handleResistance(env, attackedCres, sc);
|
||||
//it is actual spell and can be reflected to single target, no recurrence
|
||||
const bool tryMagicMirror = owner->isNegative() && owner->level && owner->getLevelInfo(0).range == "0";
|
||||
const bool tryMagicMirror = owner->isNegative() && owner->level && owner->getLevelInfo(0).range == "0";
|
||||
if(tryMagicMirror)
|
||||
{
|
||||
for(auto s : attackedCres)
|
||||
@@ -295,7 +302,7 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
|
||||
effect.effect = 3;
|
||||
effect.stack = s->ID;
|
||||
sc.customEffects.push_back(effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto cre : attackedCres)
|
||||
@@ -376,13 +383,13 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultSpellMechanics::battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
|
||||
void DefaultSpellMechanics::battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
|
||||
const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const
|
||||
{
|
||||
const std::string attackedName = attackedStack->getName();
|
||||
const std::string attackedNameSing = attackedStack->getCreature()->nameSing;
|
||||
const std::string attackedNamePl = attackedStack->getCreature()->namePl;
|
||||
|
||||
|
||||
auto getPluralFormat = [attackedStack](const int baseTextID) -> boost::format
|
||||
{
|
||||
return boost::format(VLC->generaltexth->allTexts[(attackedStack->count > 1 ? baseTextID + 1 : baseTextID)]);
|
||||
@@ -470,7 +477,7 @@ void DefaultSpellMechanics::battleLogSingleTarget(std::vector<std::string> & log
|
||||
logLines.push_back(text.str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
@@ -479,7 +486,7 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
|
||||
if(owner->isOffensiveSpell())
|
||||
{
|
||||
int spellDamage = parameters.effectValue;
|
||||
|
||||
|
||||
int chainLightningModifier = 0;
|
||||
for(auto & attackedCre : ctx.attackedCres)
|
||||
{
|
||||
@@ -522,13 +529,13 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
|
||||
vstd::amax(maxDuration, b.turnsRemain);
|
||||
sse.effect.push_back(b);
|
||||
}
|
||||
//if all spell effects have special duration, use it
|
||||
duration = maxDuration;
|
||||
//if all spell effects have special duration, use it
|
||||
duration = maxDuration;
|
||||
}
|
||||
//fix to original config: shield should display damage reduction
|
||||
if(owner->id == SpellID::SHIELD || owner->id == SpellID::AIR_SHIELD)
|
||||
{
|
||||
sse.effect.back().val = (100 - sse.effect.back().val);
|
||||
sse.effect.back().val = (100 - sse.effect.back().val);
|
||||
}
|
||||
//we need to know who cast Bind
|
||||
if(owner->id == SpellID::BIND && parameters.casterStack)
|
||||
@@ -593,7 +600,7 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
|
||||
if(!sse.stacks.empty())
|
||||
env->sendAndApply(&sse);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<BattleHex> DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
|
||||
@@ -738,7 +745,7 @@ std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargeting
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::canBeCast(const CBattleInfoCallback * cb, PlayerColor player) const
|
||||
{
|
||||
//no problems by default, this method is for spell-specific problems
|
||||
//no problems by default, this method is for spell-specific problems
|
||||
return ESpellCastProblem::OK;
|
||||
}
|
||||
|
||||
@@ -756,7 +763,7 @@ void DefaultSpellMechanics::doDispell(BattleInfo * battle, const BattleSpellCast
|
||||
if(sourceSpell != nullptr)
|
||||
{
|
||||
//Special case: DISRUPTING_RAY is "immune" to dispell
|
||||
//Other even PERMANENT effects can be removed (f.e. BIND)
|
||||
//Other even PERMANENT effects can be removed (f.e. BIND)
|
||||
if(sourceSpell->id == SpellID::DISRUPTING_RAY)
|
||||
return false;
|
||||
}
|
||||
@@ -766,7 +773,7 @@ void DefaultSpellMechanics::doDispell(BattleInfo * battle, const BattleSpellCast
|
||||
{
|
||||
CStack *s = battle->getStack(stackID);
|
||||
s->popBonuses(CSelector(localSelector).And(selector));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment* env, BattleSpellCastParameters& parameters) const
|
||||
@@ -781,12 +788,12 @@ void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment* env, Bat
|
||||
if(!destination.isValid())
|
||||
{
|
||||
env->complain("MagicMirror: invalid destination");
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
BattleSpellCast sc;
|
||||
prepareBattleCast(parameters, sc);
|
||||
|
||||
|
||||
//calculating affected creatures for all spells
|
||||
//must be vector, as in Chain Lightning order matters
|
||||
std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector
|
||||
@@ -802,14 +809,14 @@ void DefaultSpellMechanics::castMagicMirror(const SpellCastEnvironment* env, Bat
|
||||
{
|
||||
sc.affectedCres.insert(cre->ID);
|
||||
}
|
||||
|
||||
|
||||
StacksInjured si;
|
||||
SpellCastContext ctx(attackedCres, sc, si);
|
||||
applyBattleEffects(env, parameters, ctx);
|
||||
|
||||
env->sendAndApply(&sc);
|
||||
if(!si.stacks.empty()) //after spellcast info shows
|
||||
env->sendAndApply(&si);
|
||||
env->sendAndApply(&si);
|
||||
logGlobal->debugStream() << "Finished spell cast. Spell: "<<owner->name<<"; mode: MAGIC_MIRROR";
|
||||
}
|
||||
|
||||
@@ -842,8 +849,8 @@ void DefaultSpellMechanics::handleResistance(const SpellCastEnvironment * env, s
|
||||
effect.effect = 78;
|
||||
effect.stack = s->ID;
|
||||
sc.customEffects.push_back(effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultSpellMechanics::prepareBattleCast(const BattleSpellCastParameters& parameters, BattleSpellCast& sc) const
|
||||
@@ -855,6 +862,5 @@ void DefaultSpellMechanics::prepareBattleCast(const BattleSpellCastParameters& p
|
||||
sc.dmgToDisplay = 0;
|
||||
sc.castByHero = parameters.mode == ECastingMode::HERO_CASTING;
|
||||
sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1);
|
||||
sc.manaGained = 0;
|
||||
sc.manaGained = 0;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user