mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-26 22:57:00 +02:00
Serverside validation, setting for terrain compatibility before cast etc
This commit is contained in:
parent
e69c096f94
commit
3bb66de551
@ -516,29 +516,24 @@ void AdventureMapInterface::onTileLeftClicked(const int3 &targetPosition)
|
|||||||
|
|
||||||
const CGObjectInstance *topBlocking = LOCPLINT->cb->isVisible(targetPosition) ? getActiveObject(targetPosition) : nullptr;
|
const CGObjectInstance *topBlocking = LOCPLINT->cb->isVisible(targetPosition) ? getActiveObject(targetPosition) : nullptr;
|
||||||
|
|
||||||
int3 selPos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
|
|
||||||
if(spellBeingCasted)
|
if(spellBeingCasted)
|
||||||
{
|
{
|
||||||
assert(shortcuts->optionSpellcasting());
|
assert(shortcuts->optionSpellcasting());
|
||||||
|
|
||||||
if (!isInScreenRange(selPos, targetPosition))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const TerrainTile *heroTile = LOCPLINT->cb->getTile(selPos);
|
|
||||||
|
|
||||||
switch(spellBeingCasted->id)
|
switch(spellBeingCasted->id)
|
||||||
{
|
{
|
||||||
case SpellID::SCUTTLE_BOAT: //Scuttle Boat
|
case SpellID::SCUTTLE_BOAT:
|
||||||
if(topBlocking && topBlocking->ID == Obj::BOAT)
|
if(isValidAdventureSpellTarget(targetPosition, topBlocking, SpellID::SCUTTLE_BOAT))
|
||||||
performSpellcasting(targetPosition);
|
performSpellcasting(targetPosition);
|
||||||
break;
|
break;
|
||||||
case SpellID::DIMENSION_DOOR:
|
case SpellID::DIMENSION_DOOR:
|
||||||
const TerrainTile * targetTile = LOCPLINT->cb->getTileForDimensionDoor(targetPosition, LOCPLINT->localState->getCurrentHero());
|
if(isValidAdventureSpellTarget(targetPosition, topBlocking, SpellID::DIMENSION_DOOR))
|
||||||
|
|
||||||
if(targetTile && targetTile->isClear(heroTile))
|
|
||||||
performSpellcasting(targetPosition);
|
performSpellcasting(targetPosition);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//check if we can select this object
|
//check if we can select this object
|
||||||
@ -610,7 +605,7 @@ void AdventureMapInterface::onTileHovered(const int3 &targetPosition)
|
|||||||
if(!shortcuts->optionMapViewActive())
|
if(!shortcuts->optionMapViewActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//may occur just at the start of game (fake move before full intiialization)
|
//may occur just at the start of game (fake move before full initialization)
|
||||||
if(!LOCPLINT->localState->getCurrentArmy())
|
if(!LOCPLINT->localState->getCurrentArmy())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -645,35 +640,23 @@ void AdventureMapInterface::onTileHovered(const int3 &targetPosition)
|
|||||||
|
|
||||||
if(spellBeingCasted)
|
if(spellBeingCasted)
|
||||||
{
|
{
|
||||||
int3 heroPosition = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
|
|
||||||
if (!isInScreenRange(heroPosition, targetPosition))
|
|
||||||
{
|
|
||||||
CCS->curh->set(Cursor::Map::POINTER);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(spellBeingCasted->id)
|
switch(spellBeingCasted->id)
|
||||||
{
|
{
|
||||||
case SpellID::SCUTTLE_BOAT:
|
case SpellID::SCUTTLE_BOAT:
|
||||||
{
|
if(isValidAdventureSpellTarget(targetPosition, objAtTile, SpellID::SCUTTLE_BOAT))
|
||||||
if(objAtTile && objAtTile->ID == Obj::BOAT)
|
|
||||||
CCS->curh->set(Cursor::Map::SCUTTLE_BOAT);
|
CCS->curh->set(Cursor::Map::SCUTTLE_BOAT);
|
||||||
else
|
else
|
||||||
CCS->curh->set(Cursor::Map::POINTER);
|
CCS->curh->set(Cursor::Map::POINTER);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
case SpellID::DIMENSION_DOOR:
|
case SpellID::DIMENSION_DOOR:
|
||||||
{
|
if(isValidAdventureSpellTarget(targetPosition, objAtTile, SpellID::DIMENSION_DOOR))
|
||||||
const TerrainTile * t = LOCPLINT->cb->getTileForDimensionDoor(targetPosition, LOCPLINT->localState->getCurrentHero());
|
CCS->curh->set(Cursor::Map::TELEPORT);
|
||||||
|
|
||||||
if(t && t->isClear(LOCPLINT->cb->getTile(heroPosition))/* && isInScreenRange(hpos, mapPos)*/)
|
|
||||||
CCS->curh->set(Cursor::Map::TELEPORT); //TODO: something wrong with beyond east spell range border cursor on arrogance after TP-ing near underground portal on previous day
|
|
||||||
else
|
else
|
||||||
CCS->curh->set(Cursor::Map::POINTER);
|
CCS->curh->set(Cursor::Map::POINTER);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break;
|
CCS->curh->set(Cursor::Map::POINTER);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -949,3 +932,35 @@ void AdventureMapInterface::onScreenResize()
|
|||||||
if (widgetActive)
|
if (widgetActive)
|
||||||
activate();
|
activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AdventureMapInterface::isValidAdventureSpellTarget(int3 targetPosition, const CGObjectInstance * topObjectAtTarget, SpellID spellId)
|
||||||
|
{
|
||||||
|
int3 heroPosition = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
|
||||||
|
if (!isInScreenRange(heroPosition, targetPosition))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(spellId)
|
||||||
|
{
|
||||||
|
case SpellID::SCUTTLE_BOAT:
|
||||||
|
{
|
||||||
|
if(topObjectAtTarget && topObjectAtTarget->ID == Obj::BOAT)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case SpellID::DIMENSION_DOOR:
|
||||||
|
{
|
||||||
|
const TerrainTile * t = LOCPLINT->cb->getTileForDimensionDoor(targetPosition, LOCPLINT->localState->getCurrentHero());
|
||||||
|
|
||||||
|
if(t && t->isClear(LOCPLINT->cb->getTile(heroPosition)))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
logGlobal->warn("Called AdventureMapInterface::isValidAdventureSpellTarget with unknown Spell ID!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -92,6 +92,9 @@ private:
|
|||||||
/// casts current spell at specified location
|
/// casts current spell at specified location
|
||||||
void performSpellcasting(const int3 & castTarget);
|
void performSpellcasting(const int3 & castTarget);
|
||||||
|
|
||||||
|
/// performs clientside validation of valid targets for adventure spells
|
||||||
|
bool isValidAdventureSpellTarget(int3 targetPosition, const CGObjectInstance * topObjectAtTarget, SpellID spellId);
|
||||||
|
|
||||||
/// dim interface if some windows opened
|
/// dim interface if some windows opened
|
||||||
void dim(Canvas & to);
|
void dim(Canvas & to);
|
||||||
|
|
||||||
|
@ -313,7 +313,7 @@
|
|||||||
"goodLuckDice" : [ 24, 12, 8 ],
|
"goodLuckDice" : [ 24, 12, 8 ],
|
||||||
"badLuckDice" : [],
|
"badLuckDice" : [],
|
||||||
|
|
||||||
// every 1 attack point damage influence in battle when attack points > defense points during creature attack
|
// every 1 attack point damage influence in battle whe //TODO: test range, visibilityn attack points > defense points during creature attack
|
||||||
"attackPointDamageFactor": 0.05,
|
"attackPointDamageFactor": 0.05,
|
||||||
// limit of damage increase that can be achieved by overpowering attack points
|
// limit of damage increase that can be achieved by overpowering attack points
|
||||||
"attackPointDamageFactorCap": 4.0,
|
"attackPointDamageFactorCap": 4.0,
|
||||||
@ -391,7 +391,10 @@
|
|||||||
|
|
||||||
"spells":
|
"spells":
|
||||||
{
|
{
|
||||||
"dimensionDoorOnlyToUncoveredTiles" : false
|
// if enabled, dimension work doesn't work into tiles under Fog of War
|
||||||
|
"dimensionDoorOnlyToUncoveredTiles" : false,
|
||||||
|
// if enabled, dimension door will hint regarding tile being incompatible terrain type, unlike H3 (water/land)
|
||||||
|
"dimensionDoorExposesTerrainType" : false
|
||||||
},
|
},
|
||||||
|
|
||||||
"bonuses" :
|
"bonuses" :
|
||||||
|
@ -522,16 +522,20 @@ const TerrainTile * CGameInfoCallback::getTileForDimensionDoor(int3 tile, const
|
|||||||
if(!allowOnlyToUncoveredTiles)
|
if(!allowOnlyToUncoveredTiles)
|
||||||
{
|
{
|
||||||
if(castingHero->canCastThisSpell(static_cast<SpellID>(SpellID::DIMENSION_DOOR).toSpell())
|
if(castingHero->canCastThisSpell(static_cast<SpellID>(SpellID::DIMENSION_DOOR).toSpell())
|
||||||
&& isInScreenRange(castingHero->pos, tile)) //TODO: check if > 0 casts left
|
&& isInScreenRange(castingHero->getSightCenter(), tile)) //TODO: check if > 0 casts left
|
||||||
{
|
{
|
||||||
//we are allowed to get basic blocked/water invisible nearby tile date when casting DD spell
|
//we are allowed to get basic blocked/water invisible nearby tile date when casting DD spell
|
||||||
TerrainTile targetTile = gs->map->getTile(tile);
|
TerrainTile targetTile = gs->map->getTile(tile);
|
||||||
auto obfuscatedTile = std::make_shared<TerrainTile>();
|
auto obfuscatedTile = std::make_shared<TerrainTile>();
|
||||||
obfuscatedTile->visitable = false;
|
obfuscatedTile->visitable = false;
|
||||||
obfuscatedTile->blocked = targetTile.blocked || targetTile.visitable;
|
obfuscatedTile->blocked = targetTile.blocked || targetTile.visitable;
|
||||||
obfuscatedTile->terType = (targetTile.blocked || targetTile.visitable)
|
|
||||||
? VLC->terrainTypeHandler->getById(TerrainId::ROCK)
|
if(targetTile.blocked || targetTile.visitable)
|
||||||
: targetTile.isWater()
|
obfuscatedTile->terType = VLC->terrainTypeHandler->getById(TerrainId::ROCK);
|
||||||
|
else if(!VLC->settings()->getBoolean(EGameSettings::DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE))
|
||||||
|
obfuscatedTile->terType = gs->map->getTile(castingHero->getSightCenter()).terType;
|
||||||
|
else
|
||||||
|
obfuscatedTile->terType = targetTile.isWater()
|
||||||
? VLC->terrainTypeHandler->getById(TerrainId::WATER)
|
? VLC->terrainTypeHandler->getById(TerrainId::WATER)
|
||||||
: VLC->terrainTypeHandler->getById(TerrainId::GRASS);
|
: VLC->terrainTypeHandler->getById(TerrainId::GRASS);
|
||||||
|
|
||||||
|
@ -104,6 +104,7 @@ void GameSettings::load(const JsonNode & input)
|
|||||||
{EGameSettings::PATHFINDER_USE_WHIRLPOOL, "pathfinder", "useWhirlpool" },
|
{EGameSettings::PATHFINDER_USE_WHIRLPOOL, "pathfinder", "useWhirlpool" },
|
||||||
{EGameSettings::PATHFINDER_ORIGINAL_FLY_RULES, "pathfinder", "originalFlyRules" },
|
{EGameSettings::PATHFINDER_ORIGINAL_FLY_RULES, "pathfinder", "originalFlyRules" },
|
||||||
{EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES, "spells", "dimensionDoorOnlyToUncoveredTiles"},
|
{EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES, "spells", "dimensionDoorOnlyToUncoveredTiles"},
|
||||||
|
{EGameSettings::DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE, "spells", "dimensionDoorExposesTerrainType" },
|
||||||
{EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP, "towns", "buildingsPerTurnCap" },
|
{EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP, "towns", "buildingsPerTurnCap" },
|
||||||
{EGameSettings::TOWNS_STARTING_DWELLING_CHANCES, "towns", "startingDwellingChances" },
|
{EGameSettings::TOWNS_STARTING_DWELLING_CHANCES, "towns", "startingDwellingChances" },
|
||||||
};
|
};
|
||||||
|
@ -71,6 +71,7 @@ enum class EGameSettings
|
|||||||
TOWNS_STARTING_DWELLING_CHANCES,
|
TOWNS_STARTING_DWELLING_CHANCES,
|
||||||
COMBAT_ONE_HEX_TRIGGERS_OBSTACLES,
|
COMBAT_ONE_HEX_TRIGGERS_OBSTACLES,
|
||||||
DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES,
|
DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES,
|
||||||
|
DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE,
|
||||||
|
|
||||||
OPTIONS_COUNT
|
OPTIONS_COUNT
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "../CGameInfoCallback.h"
|
#include "../CGameInfoCallback.h"
|
||||||
#include "../CPlayerState.h"
|
#include "../CPlayerState.h"
|
||||||
#include "../CRandomGenerator.h"
|
#include "../CRandomGenerator.h"
|
||||||
|
#include "../GameSettings.h"
|
||||||
#include "../mapObjects/CGHeroInstance.h"
|
#include "../mapObjects/CGHeroInstance.h"
|
||||||
#include "../mapObjects/CGTownInstance.h"
|
#include "../mapObjects/CGTownInstance.h"
|
||||||
#include "../mapObjects/MiscObjects.h"
|
#include "../mapObjects/MiscObjects.h"
|
||||||
@ -252,7 +253,20 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironmen
|
|||||||
return ESpellCastResult::ERROR;
|
return ESpellCastResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: test range, visibility
|
int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter();
|
||||||
|
|
||||||
|
if(!isInScreenRange(casterPosition, parameters.pos))
|
||||||
|
{
|
||||||
|
env->complain("Attempting to cast Scuttle Boat outside screen range!");
|
||||||
|
return ESpellCastResult::ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!env->getCb()->isVisible(parameters.pos, parameters.caster->getCasterOwner()))
|
||||||
|
{
|
||||||
|
env->complain("Attempting to cast Scuttle Boat at invisible tile!");
|
||||||
|
return ESpellCastResult::ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
const TerrainTile *t = &env->getMap()->getTile(parameters.pos);
|
const TerrainTile *t = &env->getMap()->getTile(parameters.pos);
|
||||||
if(t->visitableObjects.empty() || t->visitableObjects.back()->ID != Obj::BOAT)
|
if(t->visitableObjects.empty() || t->visitableObjects.back()->ID != Obj::BOAT)
|
||||||
{
|
{
|
||||||
@ -287,8 +301,10 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
|
|||||||
return ESpellCastResult::ERROR;
|
return ESpellCastResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter();
|
||||||
|
|
||||||
const TerrainTile * dest = env->getCb()->getTile(parameters.pos);
|
const TerrainTile * dest = env->getCb()->getTile(parameters.pos);
|
||||||
const TerrainTile * curr = env->getCb()->getTile(parameters.caster->getHeroCaster()->getSightCenter());
|
const TerrainTile * curr = env->getCb()->getTile(casterPosition);
|
||||||
|
|
||||||
if(nullptr == dest)
|
if(nullptr == dest)
|
||||||
{
|
{
|
||||||
@ -308,6 +324,21 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
|
|||||||
return ESpellCastResult::ERROR;
|
return ESpellCastResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!isInScreenRange(casterPosition, parameters.pos))
|
||||||
|
{
|
||||||
|
env->complain("Attempting to cast Dimension Door outside screen range!");
|
||||||
|
return ESpellCastResult::ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(VLC->settings()->getBoolean(EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES))
|
||||||
|
{
|
||||||
|
if(!env->getCb()->isVisible(parameters.pos, parameters.caster->getCasterOwner()))
|
||||||
|
{
|
||||||
|
env->complain("Attempting to cast Dimension Door inside Fog of War with limitation toggled on!");
|
||||||
|
return ESpellCastResult::ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||||
const int movementCost = GameConstants::BASE_MOVEMENT_COST * ((schoolLevel >= 3) ? 2 : 3);
|
const int movementCost = GameConstants::BASE_MOVEMENT_COST * ((schoolLevel >= 3) ? 2 : 3);
|
||||||
|
|
||||||
@ -324,19 +355,22 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
|
|||||||
return ESpellCastResult::CANCEL;
|
return ESpellCastResult::CANCEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
GiveBonus gb;
|
|
||||||
gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId());
|
|
||||||
gb.bonus = Bonus(BonusDuration::ONE_DAY, BonusType::NONE, BonusSource::SPELL_EFFECT, 0, BonusSourceID(owner->id));
|
|
||||||
env->apply(&gb);
|
|
||||||
|
|
||||||
if(!dest->isClear(curr)) //wrong dest tile
|
if(!dest->isClear(curr)) //wrong dest tile
|
||||||
{
|
{
|
||||||
InfoWindow iw;
|
InfoWindow iw;
|
||||||
iw.player = parameters.caster->getCasterOwner();
|
iw.player = parameters.caster->getCasterOwner();
|
||||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 70); //Dimension Door failed!
|
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 70); //Dimension Door failed!
|
||||||
env->apply(&iw);
|
env->apply(&iw);
|
||||||
|
return ESpellCastResult::CANCEL;
|
||||||
}
|
}
|
||||||
else if(env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(parameters.pos), true))
|
|
||||||
|
GiveBonus gb;
|
||||||
|
gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId());
|
||||||
|
gb.bonus = Bonus(BonusDuration::ONE_DAY, BonusType::NONE, BonusSource::SPELL_EFFECT, 0, BonusSourceID(owner->id));
|
||||||
|
env->apply(&gb);
|
||||||
|
|
||||||
|
|
||||||
|
if(env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(parameters.pos), true))
|
||||||
{
|
{
|
||||||
SetMovePoints smp;
|
SetMovePoints smp;
|
||||||
smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId());
|
smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId());
|
||||||
|
Loading…
Reference in New Issue
Block a user