1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-25 22:42:04 +02:00

Reworked & fixed DARKNESS bonuses and lookout tower / skyship logic

This commit is contained in:
Ivan Savenko
2025-06-09 13:50:45 +03:00
parent a305ed28bb
commit e0de65d56c
19 changed files with 102 additions and 87 deletions

View File

@@ -171,7 +171,12 @@
"special1": { "requires" : [ "marketplace" ], "marketModes" : ["resource-artifact", "artifact-resource"] }, "special1": { "requires" : [ "marketplace" ], "marketModes" : ["resource-artifact", "artifact-resource"] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl2" }, "horde1": { "id" : 18, "upgrades" : "dwellingLvl2" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" }, "horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" },
"special2": { "height" : "high", "requires" : [ "fort" ] }, "special2": {
"requires" : [ "fort" ],
"bonuses": [
{ "type": "SIGHT_RADIUS", "val": 15 }, // 5 base + 15 bonus = 20 tiles range
]
},
"special3": { "type" : "library", "requires" : [ "mageGuild1" ] }, "special3": { "type" : "library", "requires" : [ "mageGuild1" ] },
"special4": { "special4": {
"requires" : [ "mageGuild1" ], "requires" : [ "mageGuild1" ],
@@ -185,7 +190,13 @@
] ]
} }
}, },
"grail": { "height" : "skyship", "produce" : { "gold": 5000 }, "bonuses": [ { "type": "PRIMARY_SKILL", "subtype": "primarySkill.knowledge", "val": 15 } ] }, "grail": {
"produce" : { "gold": 5000 },
"bonuses": [
{ "type": "PRIMARY_SKILL", "subtype": "primarySkill.knowledge", "val": 15 },
{ "type": "FULL_MAP_SCOUTING" }
]
},
"dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] }, "dwellingLvl1": { "id" : 30, "requires" : [ "fort" ] },
"dwellingLvl2": { "id" : 31, "requires" : [ "dwellingLvl1" ] }, "dwellingLvl2": { "id" : 31, "requires" : [ "dwellingLvl1" ] },

View File

@@ -314,6 +314,9 @@
"movementPointsLand" : [ 1500, 1500, 1500, 1500, 1560, 1630, 1700, 1760, 1830, 1900, 1960, 2000 ], "movementPointsLand" : [ 1500, 1500, 1500, 1500, 1560, 1630, 1700, 1760, 1830, 1900, 1960, 2000 ],
/// movement points hero can get on start of the turn when on sea, depending on speed of slowest creature (0-based list) /// movement points hero can get on start of the turn when on sea, depending on speed of slowest creature (0-based list)
"movementPointsSea" : [ 1500 ] "movementPointsSea" : [ 1500 ]
/// Base scouting range for hero without any range modifiers
"baseScoutingRange" : 5,
}, },
"towns": "towns":
@@ -335,7 +338,10 @@
// How much researchs/skips per day are possible? (array index is spell tier) // How much researchs/skips per day are possible? (array index is spell tier)
"spellResearchPerDay": [ 2, 2, 2, 2, 1 ], "spellResearchPerDay": [ 2, 2, 2, 2, 1 ],
// Exponent for increasing cost for each research (factor 1 disables this; array index is spell tier) // Exponent for increasing cost for each research (factor 1 disables this; array index is spell tier)
"spellResearchCostExponentPerResearch": [ 1.25, 1.25, 1.25, 1.25, 1.25 ] "spellResearchCostExponentPerResearch": [ 1.25, 1.25, 1.25, 1.25, 1.25 ],
// Base scouting range for town without any range modifiers
"baseScoutingRange" : 5,
}, },
"combat": "combat":
@@ -581,12 +587,6 @@
"val" : 1, "val" : 1,
"valueType" : "BASE_NUMBER" "valueType" : "BASE_NUMBER"
}, },
"sightRadius" :
{
"type" : "SIGHT_RADIUS", //default sight radius
"val" : 5,
"valueType" : "BASE_NUMBER"
},
"experienceGain" : "experienceGain" :
{ {
"type" : "HERO_EXPERIENCE_GAIN_PERCENT", //default hero xp "type" : "HERO_EXPERIENCE_GAIN_PERCENT", //default hero xp

View File

@@ -44,11 +44,6 @@
"enum" : [ "normal", "auto", "special", "grail" ], "enum" : [ "normal", "auto", "special", "grail" ],
"description" : "Mode in which this building will be built" "description" : "Mode in which this building will be built"
}, },
"height" : {
"type" : "string",
"enum" : [ "skyship", "high", "average", "low"],
"description" : "Height for lookout towers and some grails"
},
"requires" : { "requires" : {
"$ref" : "#/definitions/buildingRequirement", "$ref" : "#/definitions/buildingRequirement",
"description" : "List of town buildings that must be built before this one" "description" : "List of town buildings that must be built before this one"

View File

@@ -27,12 +27,6 @@ Changes mastery level of spells of affected heroes and units. Examples are magic
- subtype: school of magic - subtype: school of magic
- val: level - val: level
### DARKNESS
On each turn, hides area in fog of war around affected town for all players other than town owner. Currently does not work for any entities other than towns.
- val: radius in tiles
## Player bonuses ## Player bonuses
Intended to be setup as global effect, AI cheat etc. Intended to be setup as global effect, AI cheat etc.
@@ -99,6 +93,27 @@ Reveal area of fog of war around affected heroes when hero is recruited or moves
- val: radius in tiles - val: radius in tiles
### DARKNESS
On each turn, hides area in fog of war around affected objects for all players other than town owner. Areas within scouting range of owned objects are not affected
NOTE: when used by heroes, effect would still activate only on new turn, and not on every hero movement
- val: radius in tiles
- addInfo: optional, activation period (e.g. 7 = weekly, 28 = monthly)
### FULL_MAP_SCOUTING
On each turn, reveals entire map for owner of the bonus
- addInfo: optional, activation period (e.g. 7 = weekly, 28 = monthly)
### FULL_MAP_DARKNESS
On each turn, hides entire map in fog of war for all players other than town owner. Areas within scouting range of owned objects are not affected
- addInfo: optional, activation period (e.g. 7 = weekly, 28 = monthly)
### MANA_REGENERATION ### MANA_REGENERATION
Restores specific amount of mana points for affected heroes on new turn Restores specific amount of mana points for affected heroes on new turn

View File

@@ -150,16 +150,7 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config
// Generally only needs to be specified for "special" buildings // Generally only needs to be specified for "special" buildings
// See 'List of unique town buildings' section below for detailed description of this field // See 'List of unique town buildings' section below for detailed description of this field
"type" : "", "type" : "",
// If set, building will have Lookout Tower logic - extend sight radius of a town.
// Possible values:
// low - increases town sight radius by 5 tiles
// average - sight radius extended by 15 tiles
// high - sight radius extended by 20 tiles
// skyship - entire map will be revealed
// If not set, building will not affect sight radius of a town
"height" : "average"
// Resources produced each day by this building // Resources produced each day by this building
"produce" : { "produce" : {
"sulfur" : 1, "sulfur" : 1,

View File

@@ -77,6 +77,7 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED, "dwellings", "accumulateWhenOwned" }, {EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED, "dwellings", "accumulateWhenOwned" },
{EGameSettings::DWELLINGS_MERGE_ON_RECRUIT, "dwellings", "mergeOnRecruit" }, {EGameSettings::DWELLINGS_MERGE_ON_RECRUIT, "dwellings", "mergeOnRecruit" },
{EGameSettings::HEROES_BACKPACK_CAP, "heroes", "backpackSize" }, {EGameSettings::HEROES_BACKPACK_CAP, "heroes", "backpackSize" },
{EGameSettings::HEROES_BASE_SCOUNTING_RANGE, "heroes", "baseScoutingRange" },
{EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, "heroes", "minimalPrimarySkills" }, {EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, "heroes", "minimalPrimarySkills" },
{EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP, "heroes", "perPlayerOnMapCap" }, {EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP, "heroes", "perPlayerOnMapCap" },
{EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP, "heroes", "perPlayerTotalCap" }, {EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP, "heroes", "perPlayerTotalCap" },
@@ -116,6 +117,7 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
{EGameSettings::TEXTS_ROAD, "textData", "road" }, {EGameSettings::TEXTS_ROAD, "textData", "road" },
{EGameSettings::TEXTS_SPELL, "textData", "spell" }, {EGameSettings::TEXTS_SPELL, "textData", "spell" },
{EGameSettings::TEXTS_TERRAIN, "textData", "terrain" }, {EGameSettings::TEXTS_TERRAIN, "textData", "terrain" },
{EGameSettings::TOWNS_BASE_SCOUNTING_RANGE, "towns", "baseScoutingRange" },
{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" },
{EGameSettings::TOWNS_SPELL_RESEARCH, "towns", "spellResearch" }, {EGameSettings::TOWNS_SPELL_RESEARCH, "towns", "spellResearch" },

View File

@@ -50,6 +50,7 @@ enum class EGameSettings
DWELLINGS_ACCUMULATE_WHEN_OWNED, DWELLINGS_ACCUMULATE_WHEN_OWNED,
DWELLINGS_MERGE_ON_RECRUIT, DWELLINGS_MERGE_ON_RECRUIT,
HEROES_BACKPACK_CAP, HEROES_BACKPACK_CAP,
HEROES_BASE_SCOUNTING_RANGE,
HEROES_MINIMAL_PRIMARY_SKILLS, HEROES_MINIMAL_PRIMARY_SKILLS,
HEROES_PER_PLAYER_ON_MAP_CAP, HEROES_PER_PLAYER_ON_MAP_CAP,
HEROES_PER_PLAYER_TOTAL_CAP, HEROES_PER_PLAYER_TOTAL_CAP,
@@ -59,6 +60,7 @@ enum class EGameSettings
HEROES_MOVEMENT_COST_BASE, HEROES_MOVEMENT_COST_BASE,
HEROES_MOVEMENT_POINTS_LAND, HEROES_MOVEMENT_POINTS_LAND,
HEROES_MOVEMENT_POINTS_SEA, HEROES_MOVEMENT_POINTS_SEA,
INTERFACE_PLAYER_COLORED_BACKGROUND,
MAP_FORMAT_ARMAGEDDONS_BLADE, MAP_FORMAT_ARMAGEDDONS_BLADE,
MAP_FORMAT_CHRONICLES, MAP_FORMAT_CHRONICLES,
MAP_FORMAT_HORN_OF_THE_ABYSS, MAP_FORMAT_HORN_OF_THE_ABYSS,
@@ -91,14 +93,13 @@ enum class EGameSettings
TEXTS_TERRAIN, TEXTS_TERRAIN,
TOWNS_BUILDINGS_PER_TURN_CAP, TOWNS_BUILDINGS_PER_TURN_CAP,
TOWNS_STARTING_DWELLING_CHANCES, TOWNS_STARTING_DWELLING_CHANCES,
INTERFACE_PLAYER_COLORED_BACKGROUND, TOWNS_BASE_SCOUNTING_RANGE,
TOWNS_SPELL_RESEARCH, TOWNS_SPELL_RESEARCH,
TOWNS_SPELL_RESEARCH_COST, TOWNS_SPELL_RESEARCH_COST,
TOWNS_SPELL_RESEARCH_PER_DAY, TOWNS_SPELL_RESEARCH_PER_DAY,
TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH, TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH,
OPTIONS_COUNT, OPTIONS_COUNT
OPTIONS_BEGIN = BONUSES_GLOBAL
}; };
class DLL_LINKAGE IGameSettings class DLL_LINKAGE IGameSettings

View File

@@ -190,6 +190,8 @@ class JsonNode;
BONUS_NAME(MULTIHEX_ANIMATION) /*eg. dragons*/ \ BONUS_NAME(MULTIHEX_ANIMATION) /*eg. dragons*/ \
BONUS_NAME(STACK_EXPERIENCE_GAIN_PERCENT) /*modifies all stack experience gains*/\ BONUS_NAME(STACK_EXPERIENCE_GAIN_PERCENT) /*modifies all stack experience gains*/\
BONUS_NAME(VULNERABLE_FROM_BACK) /*bonus damage for attacks from behind*/\ BONUS_NAME(VULNERABLE_FROM_BACK) /*bonus damage for attacks from behind*/\
BONUS_NAME(FULL_MAP_SCOUTING) /*Skyship*/\
BONUS_NAME(FULL_MAP_DARKNESS) /*opposite to Skyship*/\
/* end of list */ /* end of list */

View File

@@ -15,10 +15,10 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
int IBonusBearer::valOfBonuses(const CSelector &selector, const std::string &cachingStr) const int IBonusBearer::valOfBonuses(const CSelector &selector, const std::string &cachingStr, int baseValue) const
{ {
TConstBonusListPtr hlp = getAllBonuses(selector, nullptr, cachingStr); TConstBonusListPtr hlp = getAllBonuses(selector, nullptr, cachingStr);
return hlp->totalValue(); return hlp->totalValue(baseValue);
} }
bool IBonusBearer::hasBonus(const CSelector &selector, const std::string &cachingStr) const bool IBonusBearer::hasBonus(const CSelector &selector, const std::string &cachingStr) const
@@ -63,14 +63,17 @@ TConstBonusListPtr IBonusBearer::getBonusesOfType(BonusType type, BonusSubtypeID
return getBonuses(s, cachingStr); return getBonuses(s, cachingStr);
} }
int IBonusBearer::valOfBonuses(BonusType type) const int IBonusBearer::applyBonuses(BonusType type, int baseValue) const
{ {
//This part is performance-critical //This part is performance-critical
std::string cachingStr = "type_" + std::to_string(static_cast<int>(type)); std::string cachingStr = "type_" + std::to_string(static_cast<int>(type));
CSelector s = Selector::type()(type); CSelector s = Selector::type()(type);
return valOfBonuses(s, cachingStr, baseValue);
}
return valOfBonuses(s, cachingStr); int IBonusBearer::valOfBonuses(BonusType type) const
{
return applyBonuses(type, 0);
} }
bool IBonusBearer::hasBonusOfType(BonusType type) const bool IBonusBearer::hasBonusOfType(BonusType type) const

View File

@@ -21,7 +21,7 @@ public:
IBonusBearer() = default; IBonusBearer() = default;
virtual ~IBonusBearer() = default; virtual ~IBonusBearer() = default;
virtual TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = {}) const = 0; virtual TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = {}) const = 0;
int valOfBonuses(const CSelector &selector, const std::string &cachingStr = {}) const; int valOfBonuses(const CSelector &selector, const std::string &cachingStr = {}, int baseValue = 0) const;
bool hasBonus(const CSelector &selector, const std::string &cachingStr = {}) const; bool hasBonus(const CSelector &selector, const std::string &cachingStr = {}) const;
bool hasBonus(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = {}) const; bool hasBonus(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = {}) const;
TConstBonusListPtr getBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = {}) const; TConstBonusListPtr getBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = {}) const;
@@ -30,6 +30,7 @@ public:
std::shared_ptr<const Bonus> getBonus(const CSelector &selector) const; //returns any bonus visible on node that matches (or nullptr if none matches) std::shared_ptr<const Bonus> getBonus(const CSelector &selector) const; //returns any bonus visible on node that matches (or nullptr if none matches)
//Optimized interface (with auto-caching) //Optimized interface (with auto-caching)
int applyBonuses(BonusType type, int baseValue) const; //subtype -> subtype of bonus;
int valOfBonuses(BonusType type) const; //subtype -> subtype of bonus; int valOfBonuses(BonusType type) const; //subtype -> subtype of bonus;
bool hasBonusOfType(BonusType type) const;//determines if hero has a bonus of given type (and optionally subtype) bool hasBonusOfType(BonusType type) const;//determines if hero has a bonus of given type (and optionally subtype)
int valOfBonuses(BonusType type, BonusSubtypeID subtype) const; //subtype -> subtype of bonus; int valOfBonuses(BonusType type, BonusSubtypeID subtype) const; //subtype -> subtype of bonus;

View File

@@ -903,7 +903,7 @@ void CGameInfoCallback::getTilesInRange(std::unordered_set<int3> & tiles,
logGlobal->error("Illegal call to getTilesInRange!"); logGlobal->error("Illegal call to getTilesInRange!");
return; return;
} }
if(radious == CBuilding::HEIGHT_SKYSHIP) //reveal entire map if(radious == GameConstants::FULL_MAP_RANGE)
getAllTiles (tiles, player, -1, [](auto * tile){return true;}); getAllTiles (tiles, player, -1, [](auto * tile){return true;});
else else
{ {

View File

@@ -54,6 +54,8 @@ namespace GameConstants
constexpr int TOURNAMENT_RULES_DD_MAP_TILES_THRESHOLD = 144*144*2; //map tiles count threshold for 2 dimension door casts with tournament rules constexpr int TOURNAMENT_RULES_DD_MAP_TILES_THRESHOLD = 144*144*2; //map tiles count threshold for 2 dimension door casts with tournament rules
constexpr int KINGDOM_WINDOW_HEROES_SLOTS = 4; constexpr int KINGDOM_WINDOW_HEROES_SLOTS = 4;
constexpr int INFO_WINDOW_ARTIFACTS_MAX_ITEMS = 14; constexpr int INFO_WINDOW_ARTIFACTS_MAX_ITEMS = 14;
constexpr int FULL_MAP_RANGE = std::numeric_limits<int>::max();
} }
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@@ -25,14 +25,6 @@ const std::map<std::string, CBuilding::EBuildMode> CBuilding::MODES =
{ "grail", CBuilding::BUILD_GRAIL } { "grail", CBuilding::BUILD_GRAIL }
}; };
const std::map<std::string, CBuilding::ETowerHeight> CBuilding::TOWER_TYPES =
{
{ "low", CBuilding::HEIGHT_LOW },
{ "average", CBuilding::HEIGHT_AVERAGE },
{ "high", CBuilding::HEIGHT_HIGH },
{ "skyship", CBuilding::HEIGHT_SKYSHIP }
};
BuildingTypeUniqueID CBuilding::getUniqueTypeID() const BuildingTypeUniqueID CBuilding::getUniqueTypeID() const
{ {
return BuildingTypeUniqueID(town->faction->getId(), bid); return BuildingTypeUniqueID(town->faction->getId(), bid);

View File

@@ -60,17 +60,7 @@ public:
BUILD_GRAIL // 3 - grail - building requires grail to be built BUILD_GRAIL // 3 - grail - building requires grail to be built
} mode; } mode;
enum ETowerHeight // for lookup towers and some grails
{
HEIGHT_NO_TOWER = 5, // building has not 'lookout tower' ability
HEIGHT_LOW = 10, // low lookout tower, but castle without lookout tower gives radius 5
HEIGHT_AVERAGE = 15,
HEIGHT_HIGH = 20, // such tower is in the Tower town
HEIGHT_SKYSHIP = std::numeric_limits<int>::max() // grail, open entire map
} height;
static const std::map<std::string, CBuilding::EBuildMode> MODES; static const std::map<std::string, CBuilding::EBuildMode> MODES;
static const std::map<std::string, CBuilding::ETowerHeight> TOWER_TYPES;
CBuilding() : town(nullptr), mode(BUILD_NORMAL) {}; CBuilding() : town(nullptr), mode(BUILD_NORMAL) {};

View File

@@ -283,8 +283,6 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
? CBuilding::BUILD_GRAIL ? CBuilding::BUILD_GRAIL
: vstd::find_or(CBuilding::MODES, source["mode"].String(), CBuilding::BUILD_NORMAL); : vstd::find_or(CBuilding::MODES, source["mode"].String(), CBuilding::BUILD_NORMAL);
ret->height = vstd::find_or(CBuilding::TOWER_TYPES, source["height"].String(), CBuilding::HEIGHT_NO_TOWER);
ret->identifier = stringID; ret->identifier = stringID;
ret->modScope = source.getModScope(); ret->modScope = source.getModScope();
ret->town = town; ret->town = town;

View File

@@ -220,6 +220,9 @@ static void loadBonusAddInfo(CAddInfo & var, BonusType type, const JsonNode & no
case BonusType::PRIMARY_SKILL: case BonusType::PRIMARY_SKILL:
case BonusType::ENCHANTER: case BonusType::ENCHANTER:
case BonusType::SPECIAL_PECULIAR_ENCHANT: case BonusType::SPECIAL_PECULIAR_ENCHANT:
case BonusType::DARKNESS:
case BonusType::FULL_MAP_SCOUTING:
case BonusType::FULL_MAP_DARKNESS:
// 1 number // 1 number
var = getFirstValue(value).Integer(); var = getFirstValue(value).Integer();
break; break;

View File

@@ -1062,7 +1062,8 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b
int CGHeroInstance::getSightRadius() const int CGHeroInstance::getSightRadius() const
{ {
return valOfBonuses(BonusType::SIGHT_RADIUS); // scouting gives SIGHT_RADIUS bonus int baseValue = LIBRARY->engineSettings()->getInteger(EGameSettings::HEROES_BASE_SCOUNTING_RANGE);
return applyBonuses(BonusType::SIGHT_RADIUS, baseValue);
} }
si32 CGHeroInstance::manaRegain() const si32 CGHeroInstance::manaRegain() const

View File

@@ -11,6 +11,7 @@
#include "StdInc.h" #include "StdInc.h"
#include "CGTownInstance.h" #include "CGTownInstance.h"
#include "IGameSettings.h"
#include "TownBuildingInstance.h" #include "TownBuildingInstance.h"
#include "../spells/CSpellHandler.h" #include "../spells/CSpellHandler.h"
#include "../bonuses/Bonus.h" #include "../bonuses/Bonus.h"
@@ -42,17 +43,10 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
int CGTownInstance::getSightRadius() const //returns sight distance int CGTownInstance::getSightRadius() const
{ {
auto ret = CBuilding::HEIGHT_NO_TOWER; int baseValue = LIBRARY->engineSettings()->getInteger(EGameSettings::TOWNS_BASE_SCOUNTING_RANGE);
return applyBonuses(BonusType::SIGHT_RADIUS, baseValue);
for(const auto & bid : builtBuildings)
{
auto height = getTown()->buildings.at(bid)->height;
if(ret < height)
ret = height;
}
return ret;
} }
void CGTownInstance::setPropertyDer(ObjProperty what, ObjPropertyID identifier) void CGTownInstance::setPropertyDer(ObjProperty what, ObjPropertyID identifier)

View File

@@ -654,6 +654,19 @@ void CGameHandler::onNewTurn()
addStatistics(*statistics); // write at end of turn addStatistics(*statistics); // write at end of turn
} }
const auto & currentDaySelector = [day = gameState().day+1](const Bonus * bonus)
{
if (bonus->additionalInfo[0] <= 0)
return true;
if ((day % bonus->additionalInfo[0]) == 0)
return true;
return false;
};
const auto & fullMapScoutingSelector = Selector::type()(BonusType::FULL_MAP_SCOUTING).And(currentDaySelector);
const auto & fullMapDarknessSelector = Selector::type()(BonusType::FULL_MAP_DARKNESS).And(currentDaySelector);
const auto & darknessSelector = Selector::type()(BonusType::DARKNESS).And(currentDaySelector);
for (const auto & townID : gameState().getMap().getAllTowns()) for (const auto & townID : gameState().getMap().getAllTowns())
{ {
const auto * t = gameState().getTown(townID); const auto * t = gameState().getTown(townID);
@@ -661,25 +674,27 @@ void CGameHandler::onNewTurn()
// Skyship, probably easier to handle same as Veil of darkness // Skyship, probably easier to handle same as Veil of darkness
// do it every new day before veils // do it every new day before veils
if(t->hasBuilt(BuildingID::GRAIL) if(t->hasBonus(fullMapScoutingSelector) && player.isValidPlayer())
&& player.isValidPlayer() changeFogOfWar(t->getSightCenter(), GameConstants::FULL_MAP_RANGE, player, ETileVisibility::REVEALED);
&& t->getTown()->buildings.at(BuildingID::GRAIL)->height == CBuilding::HEIGHT_SKYSHIP)
{
changeFogOfWar(t->getSightCenter(), t->getSightRadius(), player, ETileVisibility::REVEALED);
}
} }
for (const auto & townID : gameState().getMap().getAllTowns()) for (const auto & object : gameState().getMap().getObjects<CArmedInstance>())
{ {
const auto * t = gameState().getTown(townID); if(!object->hasBonus(darknessSelector) && !object->hasBonus(fullMapDarknessSelector))
if(t->hasBonusOfType(BonusType::DARKNESS)) continue;
for(const auto & player : gameState().players)
{ {
for(const auto & player : gameState().players) if (gameInfo().getPlayerStatus(player.first) != EPlayerStatus::INGAME)
{ continue;
if (gameInfo().getPlayerStatus(player.first) == EPlayerStatus::INGAME &&
gameInfo().getPlayerRelations(player.first, t->tempOwner) == PlayerRelations::ENEMIES) if (gameInfo().getPlayerRelations(player.first, object->getOwner()) != PlayerRelations::ENEMIES)
changeFogOfWar(t->getSightCenter(), t->valOfBonuses(BonusType::DARKNESS), player.first, ETileVisibility::HIDDEN); continue;
}
if (object->hasBonus(fullMapDarknessSelector))
changeFogOfWar(object->getSightCenter(), GameConstants::FULL_MAP_RANGE, player.first, ETileVisibility::HIDDEN);
else
changeFogOfWar(object->getSightCenter(), object->valOfBonuses(darknessSelector), player.first, ETileVisibility::HIDDEN);
} }
} }
@@ -4146,7 +4161,6 @@ void CGameHandler::changeFogOfWar(const std::unordered_set<int3> &tiles, PlayerC
if (mode == ETileVisibility::HIDDEN) if (mode == ETileVisibility::HIDDEN)
{ {
// do not hide tiles observed by owned objects. May lead to disastrous AI problems // do not hide tiles observed by owned objects. May lead to disastrous AI problems
// FIXME: this leads to a bug - shroud of darkness from Necropolis does can not override Skyship from Tower
std::unordered_set<int3> observedTiles; std::unordered_set<int3> observedTiles;
const auto * p = gameInfo().getPlayerState(player); const auto * p = gameInfo().getPlayerState(player);
for (const auto * obj : p->getOwnedObjects()) for (const auto * obj : p->getOwnedObjects())