diff --git a/AUTHORS b/AUTHORS index c8a5bb872..c58bbfd79 100644 --- a/AUTHORS +++ b/AUTHORS @@ -80,4 +80,4 @@ Dmitry Orlov, * special buildings support in fan towns, new features and bug fixes Andrey Cherkas aka nordsoft, - * random map generator features and bug fixes \ No newline at end of file + * new terrain support, random map generator features and various bug fixes \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index a8930ae30..9ae843236 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,8 +37,8 @@ if(NOT CMAKE_BUILD_TYPE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo) endif() -set(VCMI_VERSION_MAJOR 0) -set(VCMI_VERSION_MINOR 99) +set(VCMI_VERSION_MAJOR 1) +set(VCMI_VERSION_MINOR 0) set(VCMI_VERSION_PATCH 0) option(ENABLE_ERM "Enable compilation of ERM scripting module" ON) diff --git a/ChangeLog b/ChangeLog index b0c3d0597..57c7f4e37 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,12 @@ -0.99 -> 1.00 +0.99 -> 1.0 GENERAL: * Spectator mode was implemented through command-line options * Some main menu settings get saved after returning to main menu - last selected map, save etc. * Restart scenario button should work correctly now +* Skyship Grail works now immediately after capturing without battle +* Lodestar Grail implemented +* Fixed Gargoyles immunity * New bonuses: - SOUL_STEAL - "WoG ghost" ability, should work somewhat same as in H3 - TRANSMUTATION - "WoG werewolf"-like ability @@ -35,17 +38,53 @@ MODS: * Added bonus updaters for hero specialties * Added allOf, anyOf and noneOf qualifiers for bonus limiters * Added bonus limiters: alignment, faction and terrain +* Supported new terrains, new battlefields, custom water and rock terrains +* Following special buildings becomes available in the fan towns: +- attackVisitingBonus +- defenceVisitingBonus +- spellPowerVisitingBonus +- knowledgeVisitingBonus +- experienceVisitingBonus +- lighthouse +- treasury SOUND: * Fixed many mising or wrong pickup and visit sounds for map objects -* All map objects now have ambient sounds identical to OH3 - -RANDOM MAP GENERATOR: -* Random map generator supports water modes (normal, islands) -* Added config randomMap.json with settings for map generator -* Added parameter for template allowedWaterContent -* Extra resource packs appear nearby mines +* All map objects now have ambient sounds identical to OH3 + +RANDOM MAP GENERATOR: +* Random map generator supports water modes (normal, islands) +* Added config randomMap.json with settings for map generator +* Added parameter for template allowedWaterContent +* Extra resource packs appear nearby mines * Underground can be player starting place for factions allowed to be placed underground +* Improved obstacles placement aesthetics +* Rivers are generated on the random maps +* RMG works more stable, various crashes have been fixed +* Treasures requiring guards are guaranteed to be protected + +VCAI: +* Reworked goal decomposition engine, fixing many loopholes. AI will now pick correct goals faster. +* AI will now use universal pathfinding globally +* AI can use Summon Boat and Town Portal +* AI can gather and save resources on purpose +* AI will only buy army on demand instead of every turn +* AI can distinguish the value of all map objects +* General speed optimizations + +BATTLES: +* Towers should block ranged retaliation +* AI can bypass broken wall with moat instead of standing and waiting until gate is destroyed +* Towers do not attack war machines automatically +* Draw is possible now as battle outcome in case the battle ends with only summoned creatures (both sides loose) + +ADVENTURE MAP: +* Added buttons and keyboard shortcuts to quickly exchange army and artifacts between heroes +* Fix: Captured town should not be duplicated on the UI + +LAUNCHER: +* Implemented notifications about updates +* Supported redirection links for downloading mods 0.98 -> 0.99 diff --git a/Mods/vcmi/Data/QuickRecruitmentWindow/CreaturePurchaseCard.png b/Mods/vcmi/Data/QuickRecruitmentWindow/CreaturePurchaseCard.png new file mode 100755 index 000000000..9920a1ea2 Binary files /dev/null and b/Mods/vcmi/Data/QuickRecruitmentWindow/CreaturePurchaseCard.png differ diff --git a/Mods/vcmi/Data/s/std.verm b/Mods/vcmi/Data/s/std.verm new file mode 100755 index 000000000..865997ce7 --- /dev/null +++ b/Mods/vcmi/Data/s/std.verm @@ -0,0 +1,40 @@ +VERM +; standard verm file, global engine things should be put here + +!?PI; +; example 1 --- Hello World +![print ^Hello world!^] + +; example 2 --- simple arithmetics +![defun add [x y] [+ x y]] +![print [add 2 3]] + +; example 3 --- semantic macros +![defmacro do-n-times [times body] + `[progn + [setq do-counter 0] + [setq do-max ,times] + [do [< do-counter do-max] + [progn + [setq do-counter [+ do-counter 1]] + ,body + ] + ] + ] +] +![do-n-times 4 [print ^tekst\n^]] + + +; example 4 --- conditional expression +![if [> 2 1] [print ^Wieksze^] [print ^Mniejsze^]] + +; example 5 --- lambda expressions +![[lambda [x y] [if [> x y] [print ^wieksze^] [print ^mniejsze^]]] 2 3] + +; example 6 --- resursion +![defun factorial [n] + [if [= n 0] 1 + [* n [factorial [- n 1]]] + ] +] +![print [factorial 8]] \ No newline at end of file diff --git a/Mods/vcmi/Data/s/testy.erm b/Mods/vcmi/Data/s/testy.erm new file mode 100755 index 000000000..082c1891e --- /dev/null +++ b/Mods/vcmi/Data/s/testy.erm @@ -0,0 +1,14 @@ +ZVSE +!?PI; + !!VRv2777:S4; + !!DO1/0/5/1&v2777<>1:P0; + +!?FU1; + !!VRv2778:Sx16%2; + !!IF&x16>3:M^Hello world number %X16! To duza liczba^; + !!IF&v2778==0&x16<=3:M^Hello world number %X16! To mala parzysta liczba^; + !!IF&v2778==1&x16<=3:M^Hello world number %X16! To mala nieparzysta liczba^; + +!?PI; + !!VRz10:S^Composed hello ^; + !!IF:M^%Z10%%world%%, v2777=%V2777, v2778=%V2778!^; \ No newline at end of file diff --git a/Mods/vcmi/Maps/VCMI_Tests_2011b.h3m b/Mods/vcmi/Maps/VCMI_Tests_2011b.h3m new file mode 100755 index 000000000..6039eace4 Binary files /dev/null and b/Mods/vcmi/Maps/VCMI_Tests_2011b.h3m differ diff --git a/config/randomMap.json b/config/randomMap.json index 4a9a9a50e..e5c341c7a 100644 --- a/config/randomMap.json +++ b/config/randomMap.json @@ -22,6 +22,7 @@ }, "minGuardStrength" : 2000, "defaultRoadType" : "pc", //pd - dirt, pg - gravel, pc - cobblestone + "secondaryRoadType": "pd", "treasureValueLimit" : 20000, //generate pandora with gold for treasure above this limit "prisons" : { diff --git a/config/terrains.json b/config/terrains.json index 3b10c56d2..816e8ecb4 100644 --- a/config/terrains.json +++ b/config/terrains.json @@ -9,7 +9,8 @@ "code" : "dt", "river" : "rm", "battleFields" : ["dirt_birches", "dirt_hills", "dirt_pines"], - "terrainViewPatterns" : "dirt" + "terrainViewPatterns" : "dirt", + "horseSoundId" : 0 }, "sand" : { @@ -22,7 +23,8 @@ "river" : "rm", "battleFields" : ["sand_mesas"], "transitionRequired" : true, - "terrainViewPatterns" : "sand" + "terrainViewPatterns" : "sand", + "horseSoundId" : 1 }, "grass" : { @@ -33,7 +35,8 @@ "tiles" : "GRASTL", "code" : "gr", "river" : "rw", - "battleFields" : ["grass_hills", "grass_pines"] + "battleFields" : ["grass_hills", "grass_pines"], + "horseSoundId" : 2 }, "snow" : { @@ -44,7 +47,8 @@ "tiles" : "SNOWTL", "code" : "sn", "river" : "ri", - "battleFields" : ["snow_mountains", "snow_trees"] + "battleFields" : ["snow_mountains", "snow_trees"], + "horseSoundId" : 3 }, "swamp" : { @@ -55,7 +59,8 @@ "tiles" : "SWMPTL", "code" : "sw", "river" : "rw", - "battleFields" : ["swamp_trees"] + "battleFields" : ["swamp_trees"], + "horseSoundId" : 4 }, "rough" : { @@ -66,7 +71,8 @@ "tiles" : "ROUGTL", "code" : "rg", "river" : "rm", - "battleFields" : ["rough"] + "battleFields" : ["rough"], + "horseSoundId" : 5 }, "subterra" : { @@ -79,7 +85,8 @@ "code" : "sb", "river" : "rw", "battleFields" : ["subterranean"], - "rockTerrain" : "rock" + "rockTerrain" : "rock", + "horseSoundId" : 6 }, "lava" : { @@ -91,7 +98,8 @@ "code" : "lv", "river" : "rl", "battleFields" : ["lava"], - "rockTerrain" : "rock" + "rockTerrain" : "rock", + "horseSoundId" : 7 }, "water" : { @@ -104,7 +112,8 @@ "code" : "wt", "battleFields" : ["ship"], "transitionRequired" : true, - "terrainViewPatterns" : "water" + "terrainViewPatterns" : "water", + "horseSoundId" : 8 }, "rock" : { @@ -117,6 +126,7 @@ "code" : "rc", "battleFields" : ["rocklands"], "transitionRequired" : true, - "terrainViewPatterns" : "rock" + "terrainViewPatterns" : "rock", + "horseSoundId" : 9 } } diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index f9a5d84a2..ac9658bb1 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -51,9 +51,9 @@ const TeamID TeamID::NO_TEAM = TeamID(255); namespace GameConstants { #ifdef VCMI_NO_EXTRA_VERSION - const std::string VCMI_VERSION = std::string("VCMI 0.99"); + const std::string VCMI_VERSION = std::string("VCMI 1.0.0"); #else - const std::string VCMI_VERSION = std::string("VCMI 0.99 ") + GIT_SHA1; + const std::string VCMI_VERSION = std::string("VCMI 1.0.0.") + GIT_SHA1; #endif } diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 698228413..b658f77c4 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -69,6 +69,7 @@ void CMapGenerator::loadConfig() config.mineExtraResources = randomMapJson["mines"]["extraResourcesLimit"].Integer(); config.minGuardStrength = randomMapJson["minGuardStrength"].Integer(); config.defaultRoadType = randomMapJson["defaultRoadType"].String(); + config.secondaryRoadType = randomMapJson["secondaryRoadType"].String(); config.treasureValueLimit = randomMapJson["treasureValueLimit"].Integer(); for(auto & i : randomMapJson["prisons"]["experience"].Vector()) config.prisonExperience.push_back(i.Integer()); diff --git a/lib/rmg/CMapGenerator.h b/lib/rmg/CMapGenerator.h index 0224b777d..faf71541c 100644 --- a/lib/rmg/CMapGenerator.h +++ b/lib/rmg/CMapGenerator.h @@ -39,6 +39,7 @@ public: int mineExtraResources; int minGuardStrength; std::string defaultRoadType; + std::string secondaryRoadType; int treasureValueLimit; std::vector prisonExperience, prisonValues; std::vector scrollValues; diff --git a/lib/rmg/ObjectManager.cpp b/lib/rmg/ObjectManager.cpp index 5846c8bf5..b995941d9 100644 --- a/lib/rmg/ObjectManager.cpp +++ b/lib/rmg/ObjectManager.cpp @@ -27,7 +27,7 @@ void ObjectManager::process() { zone.fractalize(); - createRequiredObjects(); + createRequiredObjects(); } void ObjectManager::init() @@ -77,6 +77,21 @@ const rmg::Area & ObjectManager::getVisitableArea() const return objectsVisitableArea; } +std::vector ObjectManager::getMines() const +{ + std::vector mines; + + for (auto object : objects) + { + if (object->ID == Obj::MINE) + { + mines.push_back(object); + } + } + + return mines; +} + int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function weightFunction, OptimizeType optimizer) const { float bestWeight = 0.f; @@ -220,6 +235,7 @@ bool ObjectManager::createRequiredObjects() { logGlobal->trace("Creating required objects"); + RandomGeneratorUtil::randomShuffle(requiredObjects, generator.rand); for(const auto & object : requiredObjects) { auto * obj = object.first; diff --git a/lib/rmg/ObjectManager.h b/lib/rmg/ObjectManager.h index 3349d293d..ff3148af6 100644 --- a/lib/rmg/ObjectManager.h +++ b/lib/rmg/ObjectManager.h @@ -63,6 +63,8 @@ public: void createDistancesPriorityQueue(); const rmg::Area & getVisitableArea() const; + + std::vector getMines() const; protected: //content info diff --git a/lib/rmg/RoadPlacer.cpp b/lib/rmg/RoadPlacer.cpp index 03860fbae..8f605a0d6 100644 --- a/lib/rmg/RoadPlacer.cpp +++ b/lib/rmg/RoadPlacer.cpp @@ -10,6 +10,7 @@ #include "StdInc.h" #include "RoadPlacer.h" +#include "ObjectManager.h" #include "Functions.h" #include "CMapGenerator.h" #include "RmgMap.h" @@ -63,12 +64,13 @@ bool RoadPlacer::createRoad(const int3 & dst) } -void RoadPlacer::drawRoads() +void RoadPlacer::drawRoads(bool secondary) { zone.areaPossible().subtract(roads); zone.freePaths().unite(roads); map.getEditManager()->getTerrainSelection().setSelection(roads.getTilesVector()); - map.getEditManager()->drawRoad(generator.getConfig().defaultRoadType, &generator.rand); + std::string roadType = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType); + map.getEditManager()->drawRoad(roadType, &generator.rand); } void RoadPlacer::addRoadNode(const int3& node) @@ -78,7 +80,22 @@ void RoadPlacer::addRoadNode(const int3& node) void RoadPlacer::connectRoads() { - if(roadNodes.empty()) + bool noRoadNodes = false; + //Assumes objects are already placed + if (roadNodes.size() < 2) + { + //If there are no nodes, draw roads to mines + noRoadNodes = true; + if (auto* m = zone.getModificator()) + { + for (auto object : m->getMines()) + { + addRoadNode(object->visitablePos()); + } + } + } + + if(roadNodes.size() < 2) return; //take any tile from road nodes as destination zone for all other road nodes @@ -90,7 +107,8 @@ void RoadPlacer::connectRoads() createRoad(node); } - drawRoads(); + //Draw dirt roads if there are only mines + drawRoads(noRoadNodes); } char RoadPlacer::dump(const int3 & t) diff --git a/lib/rmg/RoadPlacer.h b/lib/rmg/RoadPlacer.h index 5c7f44337..585679ee6 100644 --- a/lib/rmg/RoadPlacer.h +++ b/lib/rmg/RoadPlacer.h @@ -28,7 +28,7 @@ public: protected: bool createRoad(const int3 & dst); - void drawRoads(); //actually updates tiles + void drawRoads(bool secondary = false); //actually updates tiles protected: rmg::Tileset roadNodes; //tiles to be connected with roads