diff --git a/config/artifacts.json b/config/artifacts.json index 3ea52075d..98c71050e 100644 --- a/config/artifacts.json +++ b/config/artifacts.json @@ -1085,6 +1085,7 @@ }, "necklaceOfOceanGuidance": { + "onlyOnWaterMap" : true, "bonuses" : [ { "type" : "MOVEMENT", @@ -1325,6 +1326,7 @@ }, "bootsOfLevitation": { + "onlyOnWaterMap" : true, "bonuses" : [ { "type" : "WATER_WALKING", @@ -1796,6 +1798,7 @@ }, "seaCaptainsHat": { + "onlyOnWaterMap" : true, "bonuses" : [ { "type" : "WHIRLPOOL_PROTECTION", @@ -2159,6 +2162,7 @@ }, "admiralsHat": { + "onlyOnWaterMap" : true, "bonuses" : [ { "type" : "FREE_SHIP_BOARDING", diff --git a/config/spells/adventure.json b/config/spells/adventure.json index 4a707fb08..aa6c63787 100644 --- a/config/spells/adventure.json +++ b/config/spells/adventure.json @@ -13,7 +13,8 @@ }, "flags" : { "indifferent": true - } + }, + "onlyOnWaterMap" : true }, "scuttleBoat" : { "index" : 1, @@ -29,7 +30,8 @@ }, "flags" : { "indifferent": true - } + }, + "onlyOnWaterMap" : true }, "visions" : { "index" : 2, @@ -237,7 +239,8 @@ }, "flags" : { "indifferent": true - } + }, + "onlyOnWaterMap" : true }, "dimensionDoor" : { "index" : 8, diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 3170eda1a..d39030400 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -434,6 +434,7 @@ CArtifact * CArtHandler::loadFromJson(const std::string & scope, const JsonNode art->advMapDef = graphics["map"].String(); art->price = static_cast(node["value"].Float()); + art->onlyOnWaterMap = node["onlyOnWaterMap"].Bool(); loadSlots(art, node); loadClass(art, node); diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index 640de2c6c..dad6e88e5 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -111,6 +111,7 @@ public: enum EartClass {ART_SPECIAL=1, ART_TREASURE=2, ART_MINOR=4, ART_MAJOR=8, ART_RELIC=16}; //artifact classes EartClass aClass = ART_SPECIAL; + bool onlyOnWaterMap; int32_t getIndex() const override; int32_t getIconIndex() const override; @@ -159,6 +160,7 @@ public: h & modScope; h & identifier; h & warMachine; + h & onlyOnWaterMap; } CArtifact(); diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 16b10d9d8..2eddb7372 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -57,7 +57,7 @@ bool CGameInfoCallback::isAllowed(int32_t type, int32_t id) const switch(type) { case 0: - return gs->map->allowedSpell[id]; + return gs->map->allowedSpells[id]; case 1: return gs->map->allowedArtifact[id]; case 2: diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 3e0a9c543..8c5fc9e42 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -174,7 +174,7 @@ void CPrivilegedInfoCallback::pickAllowedArtsSet(std::vector void CPrivilegedInfoCallback::getAllowedSpells(std::vector & out, std::optional level) { - for (ui32 i = 0; i < gs->map->allowedSpell.size(); i++) //spellh size appears to be greater (?) + for (ui32 i = 0; i < gs->map->allowedSpells.size(); i++) //spellh size appears to be greater (?) { const spells::Spell * spell = SpellID(i).toSpell(); diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index b9f88cc07..91a6210a0 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -137,7 +137,7 @@ CMap::CMap() allHeroes.resize(allowedHeroes.size()); allowedAbilities = VLC->skillh->getDefaultAllowed(); allowedArtifact = VLC->arth->getDefaultAllowed(); - allowedSpell = VLC->spellh->getDefaultAllowed(); + allowedSpells = VLC->spellh->getDefaultAllowed(); } CMap::~CMap() @@ -580,6 +580,63 @@ bool CMap::calculateWaterContent() return waterMap; } +void CMap::banWaterContent() +{ + banWaterHeroes(); + banWaterArtifacts(); + banWaterSpells(); +} + +void CMap::banWaterSpells() +{ + for (int j = 0; j < allowedSpells.size(); j++) + { + if (allowedSpells[j]) + { + auto* spell = dynamic_cast(VLC->spells()->getByIndex(j)); + if (spell->onlyOnWaterMap && !isWaterMap()) + { + allowedSpells[j] = false; + } + } + } +} + +void CMap::banWaterArtifacts() +{ + for (int j = 0; j < allowedArtifact.size(); j++) + { + if (allowedArtifact[j]) + { + auto* art = dynamic_cast(VLC->artifacts()->getByIndex(j)); + if (art->onlyOnWaterMap && !isWaterMap()) + { + allowedArtifact[j] = false; + } + } + } +} + +void CMap::banWaterHeroes() +{ + for (int j = 0; j < allowedHeroes.size(); j++) + { + if (allowedHeroes[j]) + { + auto* h = dynamic_cast(VLC->heroTypes()->getByIndex(j)); + if ((h->onlyOnWaterMap && !isWaterMap()) || (h->onlyOnMapWithoutWater && isWaterMap())) + { + banHero(HeroTypeID(j)); + } + } + } +} + +void CMap::banHero(const HeroTypeID & id) +{ + allowedHeroes.at(id) = false; +} + void CMap::initTerrain() { terrain.resize(boost::extents[levels()][width][height]); diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 96e05996d..224361f7c 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -108,6 +108,11 @@ public: bool isWaterMap() const; bool calculateWaterContent(); + void banWaterArtifacts(); + void banWaterHeroes(); + void banHero(const HeroTypeID& id); + void banWaterSpells(); + void banWaterContent(); /// Gets object of specified type on requested position const CGObjectInstance * getObjectiveObjectFrom(const int3 & pos, Obj::EObj type); @@ -122,7 +127,7 @@ public: std::vector rumors; std::vector disposedHeroes; std::vector > predefinedHeroes; - std::vector allowedSpell; + std::vector allowedSpells; std::vector allowedArtifact; std::vector allowedAbilities; std::list events; @@ -163,7 +168,7 @@ public: h & static_cast(*this); h & triggeredEvents; //from CMapHeader h & rumors; - h & allowedSpell; + h & allowedSpells; h & allowedAbilities; h & allowedArtifact; h & events; diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index e2080f40f..5e452d845 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -119,6 +119,7 @@ void CMapLoaderH3M::init() map->calculateGuardingGreaturePositions(); afterRead(); + //map->banWaterContent(); //Not sure if force this for custom scenarios } void CMapLoaderH3M::readHeader() @@ -774,12 +775,12 @@ void CMapLoaderH3M::readAllowedArtifacts() void CMapLoaderH3M::readAllowedSpellsAbilities() { - map->allowedSpell = VLC->spellh->getDefaultAllowed(); + map->allowedSpells = VLC->spellh->getDefaultAllowed(); map->allowedAbilities = VLC->skillh->getDefaultAllowed(); if(features.levelSOD) { - reader->readBitmaskSpells(map->allowedSpell, true); + reader->readBitmaskSpells(map->allowedSpells, true); reader->readBitmaskSkills(map->allowedAbilities, true); } } diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index 82458b0b5..3d59ae8b6 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -834,7 +834,7 @@ void CMapFormatJson::serializeOptions(JsonSerializeFormat & handler) handler.serializeLIC("allowedArtifacts", &ArtifactID::decode, &ArtifactID::encode, VLC->arth->getDefaultAllowed(), map->allowedArtifact); - handler.serializeLIC("allowedSpells", &SpellID::decode, &SpellID::encode, VLC->spellh->getDefaultAllowed(), map->allowedSpell); + handler.serializeLIC("allowedSpells", &SpellID::decode, &SpellID::encode, VLC->spellh->getDefaultAllowed(), map->allowedSpells); //todo:events } diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index decad7466..cca03cef4 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -413,7 +413,7 @@ void CMapGenerator::addHeaderInfo() m.difficulty = 1; addPlayerInfo(); m.waterMap = (mapGenOptions.getWaterContent() != EWaterContent::EWaterContent::NONE); - banWaterHeroes(); + m.banWaterContent(); } int CMapGenerator::getNextMonlithIndex() @@ -453,24 +453,6 @@ const std::vector & CMapGenerator::getAllPossibleQuestArtifacts() co return questArtifacts; } -void CMapGenerator::banWaterHeroes() -{ - //This also bans only-land heroes on wazter maps - auto isWaterMap = map->getMap(this).isWaterMap(); - - for (int j = 0; j < map->getMap(this).allowedHeroes.size(); j++) - { - if (map->getMap(this).allowedHeroes[j]) - { - auto* h = dynamic_cast(VLC->heroTypes()->getByIndex(j)); - if ((h->onlyOnWaterMap && !isWaterMap) || (h->onlyOnMapWithoutWater && isWaterMap)) //TODO: Refactor? Move to hero? - { - banHero(HeroTypeID(j)); - } - } - } -} - const std::vector CMapGenerator::getAllPossibleHeroes() const { auto isWaterMap = map->getMap(this).isWaterMap(); @@ -503,7 +485,7 @@ void CMapGenerator::banQuestArt(const ArtifactID & id) void CMapGenerator::banHero(const HeroTypeID & id) { //TODO: Protect with mutex - map->getMap(this).allowedHeroes[id] = false; + map->getMap(this).banHero(id); } Zone * CMapGenerator::getZoneWater() const diff --git a/lib/rmg/CMapGenerator.h b/lib/rmg/CMapGenerator.h index 831386d88..dd18108c5 100644 --- a/lib/rmg/CMapGenerator.h +++ b/lib/rmg/CMapGenerator.h @@ -63,7 +63,6 @@ public: int getPrisonsRemaning() const; std::shared_ptr getZonePlacer() const; const std::vector & getAllPossibleQuestArtifacts() const; - void banWaterHeroes(); const std::vector getAllPossibleHeroes() const; void banQuestArt(const ArtifactID & id); void banHero(const HeroTypeID& id); diff --git a/lib/rmg/RmgMap.cpp b/lib/rmg/RmgMap.cpp index 73e5868f5..0e6320291 100644 --- a/lib/rmg/RmgMap.cpp +++ b/lib/rmg/RmgMap.cpp @@ -343,9 +343,9 @@ ui32 RmgMap::getTotalZoneCount() const bool RmgMap::isAllowedSpell(const SpellID & sid) const { assert(sid >= 0); - if (sid < mapInstance->allowedSpell.size()) + if (sid < mapInstance->allowedSpells.size()) { - return mapInstance->allowedSpell[sid]; + return mapInstance->allowedSpells[sid]; } else return false; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index aee7303b4..5d470f79b 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -784,6 +784,8 @@ CSpell * CSpellHandler::loadFromJson(const std::string & scope, const JsonNode & spell->special = flags["special"].Bool(); + spell->onlyOnWaterMap = json["onlyOnWaterMap"].Bool(); + auto findBonus = [&](const std::string & name, std::vector & vec) { auto it = bonusNameMap.find(name); diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 884017a60..d27958ec7 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -199,7 +199,7 @@ public: bool combat; //is this spell combat (true) or adventure (false) bool creatureAbility; //if true, only creatures can use this spell si8 positiveness; //1 if spell is positive for influenced stacks, 0 if it is indifferent, -1 if it's negative - + bool onlyOnWaterMap; //Spell will be banned on maps without water std::vector counteredSpells; //spells that are removed when effect of this spell is placed on creature (for bless-curse, haste-slow, and similar pairs) JsonNode targetCondition; //custom condition on what spell can affect @@ -305,6 +305,7 @@ public: h & school; h & animationInfo; h & nonMagical; + h& onlyOnWaterMap; } friend class CSpellHandler; friend class Graphics; diff --git a/mapeditor/inspector/rewardswidget.cpp b/mapeditor/inspector/rewardswidget.cpp index 7f3d335b7..64dcc9a78 100644 --- a/mapeditor/inspector/rewardswidget.cpp +++ b/mapeditor/inspector/rewardswidget.cpp @@ -88,9 +88,9 @@ QList RewardsWidget::getListForType(RewardType typeId) break; case RewardType::SPELL: - for(int i = 0; i < map.allowedSpell.size(); ++i) + for(int i = 0; i < map.allowedSpells.size(); ++i) { - if(map.allowedSpell[i]) + if(map.allowedSpells[i]) result.append(QString::fromStdString(VLC->spells()->getByIndex(i)->getNameTranslated())); } break; diff --git a/mapeditor/mapcontroller.cpp b/mapeditor/mapcontroller.cpp index 4f48b3dba..9cef5a540 100644 --- a/mapeditor/mapcontroller.cpp +++ b/mapeditor/mapcontroller.cpp @@ -94,9 +94,9 @@ void MapController::repairMap() { map()->allowedArtifact.resize(VLC->arth->getDefaultAllowed().size()); } - if(VLC->spellh->getDefaultAllowed().size() > map()->allowedSpell.size()) + if(VLC->spellh->getDefaultAllowed().size() > map()->allowedSpells.size()) { - map()->allowedSpell.resize(VLC->spellh->getDefaultAllowed().size()); + map()->allowedSpells.resize(VLC->spellh->getDefaultAllowed().size()); } if(VLC->heroh->getDefaultAllowed().size() > map()->allowedHeroes.size()) { diff --git a/mapeditor/mapsettings.cpp b/mapeditor/mapsettings.cpp index 145f2013d..42041f755 100644 --- a/mapeditor/mapsettings.cpp +++ b/mapeditor/mapsettings.cpp @@ -116,12 +116,12 @@ MapSettings::MapSettings(MapController & ctrl, QWidget *parent) : item->setCheckState(controller.map()->allowedAbilities[i] ? Qt::Checked : Qt::Unchecked); ui->listAbilities->addItem(item); } - for(int i = 0; i < controller.map()->allowedSpell.size(); ++i) + for(int i = 0; i < controller.map()->allowedSpells.size(); ++i) { auto * item = new QListWidgetItem(QString::fromStdString(VLC->spellh->objects[i]->getNameTranslated())); item->setData(Qt::UserRole, QVariant::fromValue(i)); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); - item->setCheckState(controller.map()->allowedSpell[i] ? Qt::Checked : Qt::Unchecked); + item->setCheckState(controller.map()->allowedSpells[i] ? Qt::Checked : Qt::Unchecked); ui->listSpells->addItem(item); } for(int i = 0; i < controller.map()->allowedArtifact.size(); ++i) @@ -525,10 +525,10 @@ void MapSettings::on_pushButton_clicked() auto * item = ui->listAbilities->item(i); controller.map()->allowedAbilities[i] = item->checkState() == Qt::Checked; } - for(int i = 0; i < controller.map()->allowedSpell.size(); ++i) + for(int i = 0; i < controller.map()->allowedSpells.size(); ++i) { auto * item = ui->listSpells->item(i); - controller.map()->allowedSpell[i] = item->checkState() == Qt::Checked; + controller.map()->allowedSpells[i] = item->checkState() == Qt::Checked; } for(int i = 0; i < controller.map()->allowedArtifact.size(); ++i) { diff --git a/mapeditor/validator.cpp b/mapeditor/validator.cpp index 2f5df53e8..9091da7ef 100644 --- a/mapeditor/validator.cpp +++ b/mapeditor/validator.cpp @@ -140,7 +140,7 @@ std::list Validator::validate(const CMap * map) { if(ins->storedArtifact) { - if(!map->allowedSpell[ins->storedArtifact->getId().getNum()]) + if(!map->allowedSpells[ins->storedArtifact->getId().getNum()]) issues.emplace_back(QString("Spell scroll %1 is prohibited by map settings").arg(ins->getObjectName().c_str()), false); } else