From ab475195ac4e6e18644a7e072b1cd8122a18006e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 22 Jun 2014 13:39:40 +0300 Subject: [PATCH] Banks now use new scheme as well - Implemented Bank Constructor object. - Merged Pyramid object into common Bank class. Banks can now grant spells as part of their reward. - Move bank config code to config/objects/creatureBanks.json. Note: WoG banks are not updated yet, should be moved to WoG mod. - Updated AI code so it can correctly evaluate bank danger (should be generic enough for use with other objects) - New files JsonRandom.* that contain routines for loading random objects from Json (still WiP but should be stable) --- AI/VCAI/AIUtility.cpp | 5 +- AI/VCAI/Fuzzy.cpp | 59 +- AI/VCAI/Fuzzy.h | 3 +- config/bankconfig.json | 685 +-------------- config/gameConfig.json | 1 + config/objects/creatureBanks.json | 975 ++++++++++++++++++++++ config/objects/dwellings.json | 14 +- config/objects/generic.json | 6 - config/objects/moddables.json | 3 - lib/CArtHandler.cpp | 60 +- lib/CArtHandler.h | 10 +- lib/CMakeLists.txt | 1 + lib/NetPacks.h | 3 +- lib/VCMI_Lib.cpp | 4 +- lib/mapObjects/CBank.cpp | 442 ++++------ lib/mapObjects/CBank.h | 42 +- lib/mapObjects/CObjectClassesHandler.cpp | 8 +- lib/mapObjects/CObjectClassesHandler.h | 47 +- lib/mapObjects/CObjectHandler.cpp | 101 --- lib/mapObjects/CObjectHandler.h | 28 +- lib/mapObjects/CRewardableConstructor.cpp | 147 +--- lib/mapObjects/CRewardableConstructor.h | 2 +- lib/mapObjects/CommonConstructors.cpp | 203 ++++- lib/mapObjects/CommonConstructors.h | 63 +- lib/mapObjects/JsonRandom.cpp | 225 +++++ lib/mapObjects/JsonRandom.h | 50 ++ lib/mapping/MapFormatH3M.cpp | 2 +- lib/registerTypes/RegisterTypes.h | 4 +- 28 files changed, 1822 insertions(+), 1371 deletions(-) create mode 100644 config/objects/creatureBanks.json create mode 100644 lib/mapObjects/JsonRandom.cpp create mode 100644 lib/mapObjects/JsonRandom.h diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index 1cc87ce7d..e50f6a7aa 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -5,6 +5,7 @@ #include "../../lib/UnlockGuard.h" #include "../../lib/CConfigHandler.h" #include "../../lib/CHeroHandler.h" +#include "../../lib/mapObjects/CBank.h" /* * AIUtility.cpp, part of VCMI engine @@ -302,11 +303,11 @@ ui64 evaluateDanger(const CGObjectInstance *obj) case Obj::SHIPWRECK: //shipwreck case Obj::DERELICT_SHIP: //derelict ship // case Obj::PYRAMID: - return fh->estimateBankDanger (VLC->objh->bankObjToIndex(obj)); + return fh->estimateBankDanger (dynamic_cast(obj)); case Obj::PYRAMID: { if(obj->subID == 0) - return fh->estimateBankDanger (VLC->objh->bankObjToIndex(obj)); + return fh->estimateBankDanger (dynamic_cast(obj)); else return 0; } diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index cddff4d9a..db173016b 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -3,6 +3,7 @@ #include #include "../../lib/mapObjects/MapObjects.h" +#include "../../lib/mapObjects/CommonConstructors.h" #include "../../lib/CCreatureHandler.h" #include "../../lib/VCMI_Lib.h" #include "../../CCallback.h" @@ -41,16 +42,6 @@ struct armyStructure ui32 maxSpeed; }; -ui64 evaluateBankConfig (BankConfig * bc) -{ - ui64 danger = 0; - for (auto opt : bc->guards) - { - danger += VLC->creh->creatures[opt.first]->fightValue * opt.second; - } - return danger; -} - armyStructure evaluateArmyStructure (const CArmedInstance * army) { ui64 totalStrenght = army->getArmyStrength(); @@ -208,42 +199,26 @@ void FuzzyHelper::initTacticalAdvantage() } } -ui64 FuzzyHelper::estimateBankDanger (int ID) +ui64 FuzzyHelper::estimateBankDanger (const CBank * bank) { - std::vector > & configs = VLC->objh->banksInfo[ID]; + auto info = VLC->objtypeh->getHandlerFor(bank->ID, bank->subID)->getObjectInfo(bank->appearance); + ui64 val = std::numeric_limits::max(); try { - switch (configs.size()) - { - case 4: - try - { - for (int i = 0; i < 4; ++i) - { - int bankVal = evaluateBankConfig (VLC->objh->banksInfo[ID][i]); - bankDanger->term("Bank" + boost::lexical_cast(i))->setMinimum(bankVal * 0.5f); - bankDanger->term("Bank" + boost::lexical_cast(i))->setMaximum(bankVal * 1.5f); - } - //comparison purposes - //int averageValue = (evaluateBankConfig (VLC->objh->banksInfo[ID][0]) + evaluateBankConfig (VLC->objh->banksInfo[ID][3])) * 0.5; - //dynamic_cast(bankInput->term("SET"))->setValue(0.5); - bankInput->setInput (0.5); - engine.process (BANK_DANGER); - //engine.process(); - val = bankDanger->output().defuzzify(); //some expected value of this bank - } - catch (fl::FuzzyException & fe) - { - logAi->errorStream() << fe.name() << ": " << fe.message(); - } - break; - case 1: //rare case - Pyramid - val = evaluateBankConfig (VLC->objh->banksInfo[ID][0]); - break; - default: - logAi->warnStream() << ("Uhnandled bank config!"); - } + bankDanger->term("Bank0")->setMinimum(info->minGuards().totalStrength * 0.5f); + bankDanger->term("Bank0")->setMaximum(info->minGuards().totalStrength * 1.5f); + + bankDanger->term("Bank1")->setMinimum(info->maxGuards().totalStrength * 0.5f); + bankDanger->term("Bank1")->setMaximum(info->maxGuards().totalStrength * 1.5f); + + //comparison purposes + //int averageValue = (evaluateBankConfig (VLC->objh->banksInfo[ID][0]) + evaluateBankConfig (VLC->objh->banksInfo[ID][3])) * 0.5; + //dynamic_cast(bankInput->term("SET"))->setValue(0.5); + bankInput->setInput (0.5); + engine.process (BANK_DANGER); + //engine.process(); + val = bankDanger->output().defuzzify(); //some expected value of this bank } catch (fl::FuzzyException & fe) { diff --git a/AI/VCAI/Fuzzy.h b/AI/VCAI/Fuzzy.h index 5d9a28ab9..c81bb5569 100644 --- a/AI/VCAI/Fuzzy.h +++ b/AI/VCAI/Fuzzy.h @@ -14,6 +14,7 @@ class VCAI; class CArmedInstance; +class CBank; class FuzzyHelper { @@ -72,7 +73,7 @@ public: float evaluate (Goals::AbstractGoal & g); void setPriority (Goals::TSubgoal & g); - ui64 estimateBankDanger (int ID); + ui64 estimateBankDanger (const CBank * bank); float getTacticalAdvantage (const CArmedInstance *we, const CArmedInstance *enemy); //returns factor how many times enemy is stronger than us Goals::TSubgoal chooseSolution (Goals::TGoalVec vec); diff --git a/config/bankconfig.json b/config/bankconfig.json index d610606cd..778bd2f43 100644 --- a/config/bankconfig.json +++ b/config/bankconfig.json @@ -1,675 +1,9 @@ //Resources: Wood, Mercury, Ore, Sulfur, Crystal, Gems, Gold //Artifacts: Treasure, Minor, Major, Relic +//NOTE: all H3M banks were moved to objects/creatureBanks.json +//Remaining part should be moved to WoG mod { "banks": [ - { - "name" : "Cyclops Stockpile", - "levels": [ - { - "chance": 30, - "guards": [ { "number": 20, "id": 94 } ], - "upgrade_chance": 50, - "combat_value": 506, - "reward_resources": - { - "wood" : 4, - "mercury" : 4, - "ore" : 4, - "sulfur" : 4, - "crystal" : 4, - "gems" : 4, - "gold" : 0 - }, - "value": 10000, - "profitability": 20, - "easiest": 100 - }, - - { - "chance": 30, - "guards": [ { "number": 30, "id": 94 } ], - "upgrade_chance": 50, - "combat_value": 760, - "reward_resources": - { - "wood" : 6, - "mercury" : 6, - "ore" : 6, - "sulfur" : 6, - "crystal" : 6, - "gems" : 6 - }, - "value": 15000, - "profitability": 20, - "easiest": 150 - }, - { - "chance": 30, - "guards": [ { "number": 40, "id": 94 } ], - "upgrade_chance": 50, - "combat_value": 1013, - "reward_resources": - { - "wood" : 8, - "mercury" : 8, - "ore" : 8, - "sulfur" : 8, - "crystal" : 8, - "gems" : 8 - }, - "value": 20000, - "profitability": 20, - "easiest": 200 - }, - { - "chance": 10, - "guards": [ { "number": 50, "id": 94 } ], - "upgrade_chance": 50, - "combat_value": 1266, - "reward_resources": - { - "wood" : 10, - "mercury" : 10, - "ore" : 10, - "sulfur" : 10, - "crystal" : 10, - "gems" : 10 - }, - "value": 25000, - "profitability": 20, - "easiest": 250 - } - ] - }, - - { - "name" : "Dwarven Treasury", - "levels": [ - { - "chance": 30, - "guards": [ { "number": 50, "id": 16 } ], - "upgrade_chance": 50, - "combat_value": 194, - "reward_resources": - { - "crystal" : 2, - "gold" : 2500 - }, - "value": 3500, - "profitability": 18, - "easiest": 100 - }, - { - "chance": 30, - "guards": [ { "number": 75, "id": 16 } ], - "upgrade_chance": 50, - "combat_value": 291, - "reward_resources": - { - "crystal" : 3, - "gold" : 4000 - }, - "value": 5500, - "profitability": 19, - "easiest": 150 - }, - { - "chance": 30, - "guards": [ { "number": 100, "id": 16 } ], - "upgrade_chance": 50, - "combat_value": 388, - "reward_resources": - { - "crystal" : 5, - "gold" : 5000 - }, - "value": 7500, - "profitability": 19, - "easiest": 200 - }, - { - "chance": 10, - "guards": [ { "number": 150, "id": 16 } ], - "upgrade_chance": 50, - "combat_value": 582, - "reward_resources": - { - "crystal" : 10, - "gold" : 7500 - }, - "value": 12500, - "profitability": 21, - "easiest": 300 - } - ] - }, - - { - "name" : "Griffin Conservatory", - "levels": [ - { - "chance": 30, - "guards": [ { "number": 50, "id": 4 } ], - "upgrade_chance": 50, - "combat_value": 351, - "reward_creatures": [ { "number": 1, "id": 12 } ], - "value": 3000, - "profitability": 9, - "easiest": 100 - }, - { - "chance": 30, - "guards": [ { "number": 100, "id": 4 } ], - "upgrade_chance": 50, - "combat_value": 702, - "reward_creatures": [ { "number": 2, "id": 12 } ], - "value": 6000, - "profitability": 9, - "easiest": 200 - }, - { - "chance": 30, - "guards": [ { "number": 150, "id": 4 } ], - "upgrade_chance": 50, - "combat_value": 1053, - "reward_creatures": [ { "number": 3, "id": 12 } ], - "value": 9000, - "profitability": 9, - "easiest": 300 - }, - { - "chance": 10, - "guards": [ { "number": 200, "id": 4 } ], - "upgrade_chance": 50, - "combat_value": 1404, - "reward_creatures": [ { "number": 4, "id": 12 } ], - "value": 12000, - "profitability": 9, - "easiest": 400 - } - ] - }, - - { - "name" : "Imp Cache", - "levels": [ - { - "chance": 30, - "guards": [ { "number": 100, "id": 42 } ], - "upgrade_chance": 50, - "combat_value": 100, - "reward_resources": - { - "wood" : 0, - "mercury" : 2, - "ore" : 0, - "sulfur" : 0, - "crystal" : 0, - "gems" : 0, - "gold" : 1000 - }, - "value": 2000, - "profitability": 20, - "easiest": 100 - }, - { - "chance": 30, - "guards": [ { "number": 150, "id": 42 } ], - "upgrade_chance": 50, - "combat_value": 150, - "reward_resources": - { - "mercury" : 3, - "gold" : 1500 - }, - "value": 3000, - "profitability": 20, - "easiest": 150 - }, - { - "chance": 30, - "guards": [ { "number": 200, "id": 42 } ], - "upgrade_chance": 50, - "combat_value": 200, - "reward_resources": - { - "mercury" : 4, - "gold" : 2000 - }, - "value": 4000, - "profitability": 20, - "easiest": 200 - }, - { - "chance": 10, - "guards": [ { "number": 300, "id": 42 } ], - "upgrade_chance": 50, - "combat_value": 300, - "reward_resources": - { - "mercury" : 6, - "gold" : 3000 - }, - "value": 6000, - "profitability": 20, - "easiest": 300 - } - ] - }, - - { - "name" : "Medusa Stores", - "levels": [ - { - "chance": 30, - "guards": [ { "number": 20, "id": 76 } ], - "upgrade_chance": 50, - "combat_value": 207, - "reward_resources": - { - "sulfur" : 5, - "gold" : 2000 - }, - "value": 4500, - "profitability": 22, - "easiest": 100 - }, - { - "chance": 30, - "guards": [ { "number": 30, "id": 76 } ], - "upgrade_chance": 50, - "combat_value": 310, - "reward_resources": - { - "sulfur" : 6, - "gold" : 3000 - }, - "value": 6000, - "profitability": 19, - "easiest": 150 - }, - { - "chance": 30, - "guards": [ { "number": 40, "id": 76 } ], - "upgrade_chance": 50, - "combat_value": 414, - "reward_resources": - { - "sulfur" : 8, - "gold" : 4000 - }, - "value": 8000, - "profitability": 19, - "easiest": 200 - }, - { - "chance": 10, - "guards": [ { "number": 50, "id": 76 } ], - "upgrade_chance": 50, - "combat_value": 517, - "reward_resources": - { - "sulfur" : 10, - "gold" : 5000 - }, - "value": 10000, - "profitability": 19, - "easiest": 250 - } - ] - }, - - { - "name" : "Naga Bank", - "levels": [ - { - "chance": 30, - "guards": [ { "number": 10, "id": 38 } ], - "upgrade_chance": 50, - "combat_value": 403, - "reward_resources": - { - "gems" : 8, - "gold" : 4000 - }, - "value": 8000, - "profitability": 20, - "easiest": 100 - }, - { - "chance": 30, - "guards": [ { "number": 15, "id": 38 } ], - "upgrade_chance": 50, - "combat_value": 605, - "reward_resources": - { - "gems" : 12, - "gold" : 6000 - }, - "value": 12000, - "profitability": 20, - "easiest": 150 - }, - { - "chance": 30, - "guards": [ { "number": 20, "id": 38 } ], - "upgrade_chance": 50, - "combat_value": 806, - "reward_resources": - { - "gems" : 16, - "gold" : 8000 - }, - "value": 16000, - "profitability": 20, - "easiest": 200 - }, - { - "chance": 10, - "guards": [ { "number": 30, "id": 38 } ], - "upgrade_chance": 50, - "combat_value": 1210, - "reward_resources": - { - "gems" : 24, - "gold" : 12000 - }, - "value": 24000, - "profitability": 20, - "easiest": 300 - } - ] - }, - - { - "name" : "Dragon Fly Hive", - "levels": [ - { - "chance": 30, - "guards": [ { "number": 30, "id": 105} ], - "upgrade_chance": 0, - "combat_value": 154, - "reward_creatures": [ { "number": 4, "id": 108 } ], - "value": 3200, - "profitability": 21, - "easiest": 100 - }, - { - "chance": 30, - "guards": [ { "number": 45, "id": 105 } ], - "upgrade_chance": 0, - "combat_value": 230, - "reward_creatures": [ { "number": 6, "id": 108 } ], - "value": 4800, - "profitability": 21, - "easiest": 150 - }, - { - "chance": 30, - "guards": [ { "number": 60, "id": 105 } ], - "upgrade_chance": 0, - "combat_value": 307, - "reward_creatures": [ { "number": 8, "id": 108 } ], - "value": 6400, - "profitability": 21, - "easiest": 200 - }, - { - "chance": 10, - "guards": [ { "number": 90, "id": 105 } ], - "upgrade_chance": 0, - "combat_value": 461, - "reward_creatures": [ { "number": 12, "id": 108 } ], - "value": 9600, - "profitability": 21, - "easiest": 300 - } - ] - }, - - { - "name" : "Shipwreck", - "levels": [ - { - "chance": 30, - "guards": [ { "number": 10, "id": 60 } ], - "upgrade_chance": 0, - "combat_value": 31, - "reward_resources": - { - "gold" : 2000 - }, - "value": 2000, - "profitability": 65, - "easiest": 100 - }, - { - "chance": 30, - "guards": [ { "number": 15, "id": 60 } ], - "upgrade_chance": 0, - "combat_value": 46, - "reward_resources": - { - "gold" : 3000 - }, - "value": 3000, - "profitability": 65, - "easiest": 150 - }, - { - "chance": 30, - "guards": [ { "number": 25, "id": 60 } ], - "upgrade_chance": 0, - "combat_value": 77, - "reward_resources": - { - "gold" : 4000 - }, - "reward_artifacts": [ 1, 0, 0, 0 ], - "value": 5000, - "profitability": 65, - "easiest": 250 - }, - { - "chance": 10, - "guards": [ { "number": 50, "id": 60 } ], - "upgrade_chance": 0, - "combat_value": 154, - "reward_resources": - { - "gold" : 5000 - }, - "reward_artifacts": [ 0, 1, 0, 0 ], - "value": 7000, - "profitability": 45, - "easiest": 500 - } - ] - }, - - { - "name" : "Derelict Ship", - "levels": [ - { - "chance": 30, - "guards": [ { "number": 20, "id": 115 } ], - "upgrade_chance": 0, - "combat_value": 138, - "reward_resources": - { - "gold" : 3000 - }, - "value": 3000, - "profitability": 22, - "easiest": 100 - }, - { - "chance": 30, - "guards": [ { "number": 30, "id": 115 } ], - "upgrade_chance": 0, - "combat_value": 207, - "reward_resources": - { - "gold" : 3000 - }, - "reward_artifacts": [ 1, 0, 0, 0 ], - "value": 4000, - "profitability": 19, - "easiest": 150 - }, - { - "chance": 30, - "guards": [ { "number": 40, "id": 115 } ], - "upgrade_chance": 0, - "combat_value": 276, - "reward_resources": - { - "gold" : 4000 - }, - "reward_artifacts": [ 1, 0, 0, 0 ], - "value": 5000, - "profitability": 18, - "easiest": 200 - }, - { - "chance": 10, - "guards": [ { "number": 60, "id": 115 } ], - "upgrade_chance": 0, - "combat_value": 414, - "reward_resources": - { - "gold" : 6000 - }, - "reward_artifacts": [ 0, 1, 0, 0 ], - "value": 8000, - "profitability": 19, - "easiest": 300 - } - ] - }, - - { - "name" : "Crypt", - "levels": [ - { - "chance": 30, - "guards": [ { "number": 30, "id": 56 }, { "number": 20, "id": 58 }, { "number": 0, "id": 60 } , { "number": 0, "id": 62 } ], - "upgrade_chance": 0, - "combat_value": 75, - "reward_resources": - { - "gold" : 1500 - }, - "value": 1500, - "profitability": 20, - "easiest": 100 - }, - { - "chance": 30, - "guards": [ { "number": 25, "id": 56 }, { "number": 20, "id": 58 }, { "number": 5, "id": 60 }, { "number": 0, "id": 62 } ], - "upgrade_chance": 0, - "combat_value": 94, - "reward_resources": - { - "gold" : 2000 - }, - "value": 2000, - "profitability": 21, - "easiest": 126 - }, - { - "chance": 30, - "guards": [ { "number": 20, "id": 56 }, { "number": 20, "id": 58 }, { "number": 10, "id": 60 }, { "number": 5, "id": 62 } ], - "upgrade_chance": 0, - "combat_value": 169, - "reward_resources": - { - "gold" : 2500 - }, - "reward_artifacts": [ 1, 0, 0, 0 ], - "value": 3500, - "profitability": 21, - "easiest": 225 - }, - { - "chance": 10, - "guards": [ { "number": 20, "id": 56 }, { "number": 20, "id": 58 }, { "number": 10, "id": 60 }, { "number": 10, "id": 62 } ], - "upgrade_chance": 0, - "combat_value": 225, - "reward_resources": - { - "gold" : 5000 - }, - "reward_artifacts": [ 1, 0, 0, 0 ], - "value": 6000, - "profitability": 27, - "easiest": 299 - } - ] - }, - - { - "name" : "Dragon Utopia", - "levels": [ - { - "chance": 30, - "guards": [ { "number": 8, "id": 26 }, { "number": 5, "id": 82 }, { "number": 2, "id": 27 }, { "number": 1, "id": 83 } ], - "upgrade_chance": 0, - "combat_value": 769, - "reward_resources": - { - "gold" : 20000 - }, - "reward_artifacts": [ 1, 1, 1, 1 ], - "value": 38000, - "profitability": 21, - "easiest": 100 - }, - { - "chance": 30, - "guards": [ { "number": 8, "id": 26 }, { "number": 6, "id": 82 }, { "number": 3, "id": 27 }, { "number": 2, "id": 83 } ], - "upgrade_chance": 0, - "combat_value": 209, - "reward_resources": - { - "gold" : 30000 - }, - "reward_artifacts": [ 0, 1, 1, 2 ], - "value": 57000, - "profitability": 26, - "easiest": 125 - }, - { - "chance": 30, - "guards": [ { "number": 8, "id": 26 }, { "number": 6, "id": 82 }, { "number": 4, "id": 27 }, { "number": 3, "id": 83 } ], - "upgrade_chance": 0, - "combat_value": 556, - "reward_resources": - { - "gold" : 40000 - }, - "reward_artifacts": [ 0, 0, 1, 3 ], - "value": 75000, - "profitability": 29, - "easiest": 145 - }, - { - "chance": 10, - "guards": [ { "number": 8, "id": 26 }, { "number": 7, "id": 82 }, { "number": 6, "id": 27 }, { "number": 5, "id": 83 } ], - "upgrade_chance": 0, - "combat_value": 343, - "reward_resources": - { - "gold" : 50000 - }, - "reward_artifacts": [ 0, 0, 0, 4 ], - "value": 90000, - "profitability": 27, - "easiest": 189 - } - ] - }, - { "name" : "Hunting Lodge", "levels": [ @@ -1252,21 +586,6 @@ "easiest": 250 } ] - }, - - { - "name" : "Pyramid", - "levels": [ - { - "chance": 100, - "guards": [ { "number": 20, "id": 116 }, { "number": 10, "id": 117 }, { "number": 20, "id": 116 }, { "number": 10, "id": 117 } ], - "upgrade_chance": 0, - "combat_value": 786, - "value": 15000, - "profitability": 19, - "easiest": 100 - } - ] } ] } diff --git a/config/gameConfig.json b/config/gameConfig.json index ad35ef0e9..801287573 100644 --- a/config/gameConfig.json +++ b/config/gameConfig.json @@ -49,6 +49,7 @@ [ "config/objects/generic.json", "config/objects/moddables.json", + "config/objects/creatureBanks.json", "config/objects/dwellings.json", "config/objects/rewardable.json" ], diff --git a/config/objects/creatureBanks.json b/config/objects/creatureBanks.json new file mode 100644 index 000000000..b0d0c7218 --- /dev/null +++ b/config/objects/creatureBanks.json @@ -0,0 +1,975 @@ +{ + "creatureBank" : { + "index" :16, + "handler": "bank", + "types" : { + "cyclopsStockpile" : + { + "index" : 0, + "name" : "Cyclops Stockpile", + "levels": [ + { + "chance": 30, + "guards": [ + { "amount": 4, "type": "cyclop" }, + { "amount": 4, "type": "cyclop" }, + { "amount": 4, "type": "cyclop", "upgradeChance": 50 }, + { "amount": 4, "type": "cyclop" }, + { "amount": 4, "type": "cyclop" } + ], + + "combat_value": 506, + "reward" : { + "value": 10000, + "resources": + { + "wood" : 4, + "mercury" : 4, + "ore" : 4, + "sulfur" : 4, + "crystal" : 4, + "gems" : 4, + "gold" : 0 + } + } + }, + + { + "chance": 30, + "guards": [ + { "amount": 6, "type": "cyclop" }, + { "amount": 6, "type": "cyclop" }, + { "amount": 6, "type": "cyclop", "upgradeChance": 50 }, + { "amount": 6, "type": "cyclop" }, + { "amount": 6, "type": "cyclop" } + ], + "combat_value": 760, + "reward" : { + "value": 15000, + "resources": + { + "wood" : 6, + "mercury" : 6, + "ore" : 6, + "sulfur" : 6, + "crystal" : 6, + "gems" : 6 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 8, "type": "cyclop" }, + { "amount": 8, "type": "cyclop" }, + { "amount": 8, "type": "cyclop", "upgradeChance": 50 }, + { "amount": 8, "type": "cyclop" }, + { "amount": 8, "type": "cyclop" } + ], + "combat_value": 1013, + "reward" : { + "value": 20000 + "resources": + { + "wood" : 8, + "mercury" : 8, + "ore" : 8, + "sulfur" : 8, + "crystal" : 8, + "gems" : 8 + } + } + }, + { + "chance": 10, + "guards": [ + { "amount": 10, "type": "cyclop" }, + { "amount": 10, "type": "cyclop" }, + { "amount": 10, "type": "cyclop", "upgradeChance": 50 }, + { "amount": 10, "type": "cyclop" }, + { "amount": 10, "type": "cyclop" } + ], + "combat_value": 1266, + "reward" : { + "value": 25000, + "resources": + { + "wood" : 10, + "mercury" : 10, + "ore" : 10, + "sulfur" : 10, + "crystal" : 10, + "gems" : 10 + } + } + } + ] + }, + "dwarvenTreasury" : { + "index" : 1, + "resetDuraition" : 28, + "name" : "Dwarven Treasury", + "levels": [ + { + "chance": 30, + "guards": [ + { "amount": 10, "type": "dwarf" }, + { "amount": 10, "type": "dwarf" }, + { "amount": 10, "type": "dwarf", "upgradeChance": 50 }, + { "amount": 10, "type": "dwarf" }, + { "amount": 10, "type": "dwarf" } + ], + "combat_value": 194, + "reward" : { + "value": 3500, + "resources": + { + "crystal" : 2, + "gold" : 2500 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 15, "type": "dwarf" }, + { "amount": 15, "type": "dwarf" }, + { "amount": 15, "type": "dwarf", "upgradeChance": 50 }, + { "amount": 15, "type": "dwarf" }, + { "amount": 15, "type": "dwarf" } + ], + "combat_value": 291, + "reward" : { + "value": 5500, + "resources": + { + "crystal" : 3, + "gold" : 4000 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 20, "type": "dwarf" }, + { "amount": 20, "type": "dwarf" }, + { "amount": 20, "type": "dwarf", "upgradeChance": 50 }, + { "amount": 20, "type": "dwarf" }, + { "amount": 20, "type": "dwarf" } + ], + "combat_value": 388, + "reward" : { + "value": 7500, + "resources": + { + "crystal" : 5, + "gold" : 5000 + } + } + }, + { + "chance": 10, + "guards": [ + { "amount": 30, "type": "dwarf" }, + { "amount": 30, "type": "dwarf" }, + { "amount": 30, "type": "dwarf", "upgradeChance": 50 }, + { "amount": 30, "type": "dwarf" }, + { "amount": 30, "type": "dwarf" } + ], + "combat_value": 582, + "reward" : { + "value": 12500, + "resources": + { + "crystal" : 10, + "gold" : 7500 + } + } + } + ] + }, + "griffinConservatory" : { + "index" : 2, + "resetDuraition" : 28, + "name" : "Griffin Conservatory", + "levels": [ + { + "chance": 30, + "guards": [ + { "amount": 10, "type": "griffin" }, + { "amount": 10, "type": "griffin" }, + { "amount": 10, "type": "griffin", "upgradeChance": 50 }, + { "amount": 10, "type": "griffin" }, + { "amount": 10, "type": "griffin" } + ], + "combat_value": 351, + "reward" : { + "value": 3000, + "creatures": [ { "amount": 1, "type": "angel" } ] + } + }, + { + "chance": 30, + "guards": [ + { "amount": 20, "type": "griffin" }, + { "amount": 20, "type": "griffin" }, + { "amount": 20, "type": "griffin", "upgradeChance": 50 }, + { "amount": 20, "type": "griffin" }, + { "amount": 20, "type": "griffin" } + ], + "combat_value": 702, + "reward" : { + "value": 6000, + "creatures": [ { "amount": 2, "type": "angel" } ] + } + }, + { + "chance": 30, + "guards": [ + { "amount": 30, "type": "griffin" }, + { "amount": 30, "type": "griffin" }, + { "amount": 30, "type": "griffin", "upgradeChance": 50 }, + { "amount": 30, "type": "griffin" }, + { "amount": 30, "type": "griffin" } + ], + "combat_value": 1053, + "reward" : { + "value": 9000, + "creatures": [ { "amount": 3, "type": "angel" } ] + } + }, + { + "chance": 10, + "guards": [ + { "amount": 40, "type": "griffin" }, + { "amount": 40, "type": "griffin" }, + { "amount": 40, "type": "griffin", "upgradeChance": 50 }, + { "amount": 40, "type": "griffin" }, + { "amount": 40, "type": "griffin" } + ], + "combat_value": 1404, + "reward" : { + "value": 12000, + "creatures": [ { "amount": 4, "type": "angel" } ] + } + } + ] + }, + "inpCache" : { + "index" : 3, + "resetDuraition" : 28, + "name" : "Imp Cache", + "levels": [ + { + "chance": 30, + "guards": [ + { "amount": 20, "type": "imp" }, + { "amount": 20, "type": "imp" }, + { "amount": 20, "type": "imp", "upgradeChance": 50 }, + { "amount": 20, "type": "imp" }, + { "amount": 20, "type": "imp" } + ], + "combat_value": 100, + "reward" : { + "value": 2000, + "resources": + { + "wood" : 0, + "mercury" : 2, + "ore" : 0, + "sulfur" : 0, + "crystal" : 0, + "gems" : 0, + "gold" : 1000 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 30, "type": "imp" }, + { "amount": 30, "type": "imp" }, + { "amount": 30, "type": "imp", "upgradeChance": 50 }, + { "amount": 30, "type": "imp" }, + { "amount": 30, "type": "imp" } + ], + "combat_value": 150, + "reward" : { + "value": 3000, + "resources": + { + "mercury" : 3, + "gold" : 1500 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 40, "type": "imp" }, + { "amount": 40, "type": "imp" }, + { "amount": 40, "type": "imp", "upgradeChance": 50 }, + { "amount": 40, "type": "imp" }, + { "amount": 40, "type": "imp" } + ], + "combat_value": 200, + "reward" : { + "value": 4000, + "resources": + { + "mercury" : 4, + "gold" : 2000 + } + } + }, + { + "chance": 10, + "guards": [ + { "amount": 60, "type": "imp" }, + { "amount": 60, "type": "imp" }, + { "amount": 60, "type": "imp", "upgradeChance": 50 }, + { "amount": 60, "type": "imp" }, + { "amount": 60, "type": "imp" } + ], + "combat_value": 300, + "reward" : { + "value": 6000, + "resources": + { + "mercury" : 6, + "gold" : 3000 + } + } + } + ] + }, + "medusaStore" : { + "index" : 4, + "resetDuraition" : 28, + "name" : "Medusa Stores", + "levels": [ + { + "chance": 30, + "guards": [ + { "amount": 4, "type": "medusa" }, + { "amount": 4, "type": "medusa" }, + { "amount": 4, "type": "medusa", "upgradeChance": 50 }, + { "amount": 4, "type": "medusa" }, + { "amount": 4, "type": "medusa" } + ], + "combat_value": 207, + "reward" : { + "value": 4500, + "resources": + { + "sulfur" : 5, + "gold" : 2000 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 6, "type": "medusa" }, + { "amount": 6, "type": "medusa" }, + { "amount": 6, "type": "medusa", "upgradeChance": 50 }, + { "amount": 6, "type": "medusa" }, + { "amount": 6, "type": "medusa" } + ], + "combat_value": 310, + "reward" : { + "value": 6000, + "resources": + { + "sulfur" : 6, + "gold" : 3000 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 8, "type": "medusa" }, + { "amount": 8, "type": "medusa" }, + { "amount": 8, "type": "medusa", "upgradeChance": 50 }, + { "amount": 8, "type": "medusa" }, + { "amount": 8, "type": "medusa" } + ], + "combat_value": 414, + "reward" : { + "value": 8000, + "resources": + { + "sulfur" : 8, + "gold" : 4000 + } + } + }, + { + "chance": 10, + "guards": [ + { "amount": 10, "type": "medusa" }, + { "amount": 10, "type": "medusa" }, + { "amount": 10, "type": "medusa", "upgradeChance": 50 }, + { "amount": 10, "type": "medusa" }, + { "amount": 10, "type": "medusa" } + ], + "combat_value": 517, + "reward" : { + "value": 10000, + "resources": + { + "sulfur" : 10, + "gold" : 5000 + } + } + } + ] + }, + "nagaBank" : { + "index" : 5, + "resetDuraition" : 28, + "name" : "Naga Bank", + "levels": [ + { + "chance": 30, + "guards": [ + { "amount": 2, "type": "naga" }, + { "amount": 2, "type": "naga" }, + { "amount": 2, "type": "naga", "upgradeChance": 50 }, + { "amount": 2, "type": "naga" }, + { "amount": 2, "type": "naga" } + ], + "combat_value": 403, + "reward" : { + "value": 8000, + "resources": + { + "gems" : 8, + "gold" : 4000 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 3, "type": "naga" }, + { "amount": 3, "type": "naga" }, + { "amount": 3, "type": "naga", "upgradeChance": 50 }, + { "amount": 3, "type": "naga" }, + { "amount": 3, "type": "naga" } + ], + "combat_value": 605, + "reward" : { + "value": 12000, + "resources": + { + "gems" : 12, + "gold" : 6000 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 4, "type": "naga" }, + { "amount": 4, "type": "naga" }, + { "amount": 4, "type": "naga", "upgradeChance": 50 }, + { "amount": 4, "type": "naga" }, + { "amount": 4, "type": "naga" } + ], + "combat_value": 806, + "reward" : { + "value": 16000, + "resources": + { + "gems" : 16, + "gold" : 8000 + } + } + }, + { + "chance": 10, + "guards": [ + { "amount": 6, "type": "naga" }, + { "amount": 6, "type": "naga" }, + { "amount": 6, "type": "naga", "upgradeChance": 50 }, + { "amount": 6, "type": "naga" }, + { "amount": 6, "type": "naga" } + ], + "combat_value": 1210, + "reward" : { + "value": 24000, + "resources": + { + "gems" : 24, + "gold" : 12000 + } + } + } + ] + }, + "dragonflyHive" : { + "index" : 6, + "resetDuraition" : 28, + "name" : "Dragon Fly Hive", + "levels": [ + { + "chance": 30, + "guards": [ + { "amount": 6, "type": "dragonFly" }, + { "amount": 6, "type": "dragonFly" }, + { "amount": 6, "type": "dragonFly" }, + { "amount": 6, "type": "dragonFly" }, + { "amount": 6, "type": "dragonFly" } + ], + "combat_value": 154, + "reward" : { + "value": 3200, + "creatures": [ { "amount": 4, "type": "vyvern" } ] + } + }, + { + "chance": 30, + "guards": [ + { "amount": 9, "type": "dragonFly" }, + { "amount": 9, "type": "dragonFly" }, + { "amount": 9, "type": "dragonFly" }, + { "amount": 9, "type": "dragonFly" }, + { "amount": 9, "type": "dragonFly" } + ], + "combat_value": 230, + "reward" : { + "value": 4800, + "creatures": [ { "amount": 6, "type": "vyvern" } ] + } + }, + { + "chance": 30, + "guards": [ + { "amount": 12, "type": "dragonFly" }, + { "amount": 12, "type": "dragonFly" }, + { "amount": 12, "type": "dragonFly" }, + { "amount": 12, "type": "dragonFly" }, + { "amount": 12, "type": "dragonFly" } + ], + "combat_value": 307, + "reward" : { + "value": 6400, + "creatures": [ { "amount": 8, "type": "vyvern" } ] + } + }, + { + "chance": 10, + "guards": [ + { "amount": 18, "type": "dragonFly" }, + { "amount": 18, "type": "dragonFly" }, + { "amount": 18, "type": "dragonFly" }, + { "amount": 18, "type": "dragonFly" }, + { "amount": 18, "type": "dragonFly" } + ], + "combat_value": 461, + "reward" : { + "value": 9600, + "creatures": [ { "amount": 12, "type": "vyvern" } ] + } + } + ] + } + } + }, + "shipwreck" : { + "index" :85, + "handler": "bank", + "types" : { + "shipwreck" : { + "index" : 0, + "resetDuraition" : 28, + "name" : "Shipwreck", + "levels": [ + { + "chance": 30, + "guards": [ + { "amount": 2, "type": "wight" }, + { "amount": 2, "type": "wight" }, + { "amount": 2, "type": "wight" }, + { "amount": 2, "type": "wight" }, + { "amount": 2, "type": "wight" } + ], + "combat_value": 31, + "reward" : { + "value": 2000, + "resources": + { + "gold" : 2000 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 3, "type": "wight" }, + { "amount": 3, "type": "wight" }, + { "amount": 3, "type": "wight" }, + { "amount": 3, "type": "wight" }, + { "amount": 3, "type": "wight" } + ], + "combat_value": 46, + "reward" : { + "value": 3000, + "resources": + { + "gold" : 3000 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 5, "type": "wight" }, + { "amount": 5, "type": "wight" }, + { "amount": 5, "type": "wight" }, + { "amount": 5, "type": "wight" }, + { "amount": 5, "type": "wight" } + ], + "combat_value": 77, + "reward" : { + "value": 5000, + "resources": + { + "gold" : 4000 + }, + "artifacts": [ { "class" : "TREASURE" } ] + } + }, + { + "chance": 10, + "guards": [ + { "amount": 10, "type": "wight" }, + { "amount": 10, "type": "wight" }, + { "amount": 10, "type": "wight" }, + { "amount": 10, "type": "wight" }, + { "amount": 10, "type": "wight" } + ], + "combat_value": 154, + "reward" : { + "value": 7000, + "resources": + { + "gold" : 5000 + }, + "artifacts": [ { "class" : "MINOR" } ] + } + } + ] + } + } + } + "derelictShip" : { + "index" :24, + "handler": "bank", + "types" : { + "derelictShip" : { + "index" : 0, + "resetDuraition" : 28, + "name" : "Derelict Ship", + "levels": [ + { + "chance": 30, + "guards": [ + { "amount": 4, "type": "waterElemental" }, + { "amount": 4, "type": "waterElemental" }, + { "amount": 4, "type": "waterElemental" }, + { "amount": 4, "type": "waterElemental" }, + { "amount": 4, "type": "waterElemental" } + ], + "combat_value": 138, + "reward" : { + "value": 3000, + "resources": + { + "gold" : 3000 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 6, "type": "waterElemental" }, + { "amount": 6, "type": "waterElemental" }, + { "amount": 6, "type": "waterElemental" }, + { "amount": 6, "type": "waterElemental" }, + { "amount": 6, "type": "waterElemental" } + ], + "combat_value": 207, + "reward" : { + "value": 4000, + "resources": + { + "gold" : 3000 + }, + "artifacts": [ { "class" : "TREASURE" } ] + } + }, + { + "chance": 30, + "guards": [ + { "amount": 8, "type": "waterElemental" }, + { "amount": 8, "type": "waterElemental" }, + { "amount": 8, "type": "waterElemental" }, + { "amount": 8, "type": "waterElemental" }, + { "amount": 8, "type": "waterElemental" } + ], + "combat_value": 276, + "reward" : { + "value": 5000, + "resources": + { + "gold" : 4000 + }, + "artifacts": [ { "class" : "TREASURE" } ] + } + }, + { + "chance": 10, + "guards": [ + { "amount": 12, "type": "waterElemental" }, + { "amount": 12, "type": "waterElemental" }, + { "amount": 12, "type": "waterElemental" }, + { "amount": 12, "type": "waterElemental" }, + { "amount": 12, "type": "waterElemental" } + ], + "combat_value": 414, + "reward" : { + "value": 8000, + "resources": + { + "gold" : 6000 + }, + "artifacts": [ { "class" : "MINOR" } ] + } + } + ] + } + } + }, + "crypt" : { + "index" :84, + "handler": "bank", + "types" : { + "crypt" : { + "index" : 0, + "resetDuraition" : 28, + "name" : "Crypt", + "levels": [ + { + "chance": 30, + "guards": [ + { "amount": 10, "type": "skeleton" }, + { "amount": 10, "type": "walkingDead" }, + { "amount": 10, "type": "walkingDead" }, + { "amount": 10, "type": "skeleton" }, + { "amount": 10, "type": "skeleton" } + ], + "combat_value": 75, + "reward" : { + "value": 1500, + "resources": + { + "gold" : 1500 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 13, "type": "skeleton" }, + { "amount": 10, "type": "walkingDead" }, + { "amount": 5, "type": "wight" }, + { "amount": 10, "type": "walkingDead" }, + { "amount": 12, "type": "skeleton" } + ], + "combat_value": 94, + "reward" : { + "value": 2000, + "resources": + { + "gold" : 2000 + } + } + }, + { + "chance": 30, + "guards": [ + { "amount": 20, "type": "skeleton" }, + { "amount": 20, "type": "walkingDead" }, + { "amount": 10, "type": "wight" }, + { "amount": 5, "type": "vampire" } + ], + "combat_value": 169, + "reward" : { + "value": 3500, + "resources": + { + "gold" : 2500 + }, + "artifacts": [ { "class" : "TREASURE" } ] + } + }, + { + "chance": 10, + "guards": [ + { "amount": 20, "type": "skeleton" }, + { "amount": 20, "type": "walkingDead" }, + { "amount": 10, "type": "wight" }, + { "amount": 10, "type": "vampire" } + ], + "combat_value": 225, + "reward" : { + "value": 6000, + "resources": + { + "gold" : 5000 + }, + "artifacts": [ { "class" : "TREASURE" } ] + } + } + ] + } + } + }, + "dragonUtopia" : { + "index" :25, + "handler": "bank", + "types" : { + "dragonUtopia" : { + "index" : 0, + "resetDuraition" : 28, + "name" : "Dragon Utopia", + "levels": [ + { + "chance": 30, + "guards": [ + { "amount": 8, "type": "greenDragon" }, + { "amount": 5, "type": "redDragon" }, + { "amount": 2, "type": "goldDragon" }, + { "amount": 1, "type": "blackDragon" } + ], + "combat_value": 769, + "reward" : { + "value": 38000, + "resources": + { + "gold" : 20000 + }, + "artifacts": [ + { "class" : "TREASURE" } + { "class" : "MINOR" } + { "class" : "MAJOR" } + { "class" : "RELIC" } + ] + } + }, + { + "chance": 30, + "guards": [ + { "amount": 8, "type": "greenDragon" }, + { "amount": 6, "type": "redDragon" }, + { "amount": 3, "type": "goldDragon" }, + { "amount": 2, "type": "blackDragon" } + ], + "combat_value": 209, + "reward" : { + "value": 57000, + "resources": + { + "gold" : 30000 + }, + "artifacts": [ + { "class" : "MINOR" } + { "class" : "MAJOR" } + { "class" : "RELIC" } + { "class" : "RELIC" } + ] + } + }, + { + "chance": 30, + "guards": [ + { "amount": 8, "type": "greenDragon" }, + { "amount": 6, "type": "redDragon" }, + { "amount": 4, "type": "goldDragon" }, + { "amount": 3, "type": "blackDragon" } + ], + "combat_value": 556, + "reward" : { + "value": 75000, + "resources": + { + "gold" : 40000 + }, + "artifacts": [ 0, 0, 1, 3 ] + "artifacts": [ + { "class" : "MAJOR" } + { "class" : "RELIC" } + { "class" : "RELIC" } + { "class" : "RELIC" } + ] + } + }, + { + "chance": 10, + "guards": [ + { "amount": 8, "type": "greenDragon" }, + { "amount": 7, "type": "redDragon" }, + { "amount": 6, "type": "goldDragon" }, + { "amount": 5, "type": "blackDragon" } + ], + "combat_value": 343, + "reward" : { + "value": 90000, + "resources": + { + "gold" : 50000 + }, + "artifacts": [ 0, 0, 0, 4 ] + "artifacts": [ + { "class" : "RELIC" } + { "class" : "RELIC" } + { "class" : "RELIC" } + { "class" : "RELIC" } + ] + } + } + ] + } + } + }, + "pyramid" : { + "index" :63, + "handler": "bank", + "types" : { + "pyramid" : { + "index" : 0, + "resetDuraition" : 28, + "name" : "Pyramid", + "levels": [ + { + "chance": 100, + "guards": [ + { "amount": 20, "type": "goldGolem" }, + { "amount": 10, "type": "diamondGolem" }, + { "amount": 20, "type": "goldGolem" }, + { "amount": 10, "type": "diamondGolem" } + ], + "combat_value": 786, + "reward" : { + "value": 15000, + "spells" : [ { "level" : 5 } ] + } + } + ] + } + } + } +} diff --git a/config/objects/dwellings.json b/config/objects/dwellings.json index 8b5c83f52..0f8150163 100644 --- a/config/objects/dwellings.json +++ b/config/objects/dwellings.json @@ -104,9 +104,9 @@ [ "fireElemental" ], [ "earthElemental" ] ], - "guards" : { - "earthElemental" : 12 - } + "guards" : [ + { "amount" : 12, "type" : "earthElemental" } + ] }, "golemFactory" : { "index" : 1, @@ -116,10 +116,10 @@ [ "goldGolem" ], [ "diamondGolem" ] ], - "guards" : { - "goldGolem" : 9, - "diamondGolem" : 6 - } + "guards" : [ + { "amount" : 9, "type" : "goldGolem" }, + { "amount" : 6, "type" : "diamondGolem" } + ] } } }, diff --git a/config/objects/generic.json b/config/objects/generic.json index 759214acd..bc1e1dcf8 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -16,11 +16,6 @@ "blackMarket" : { "index" :7, "handler": "blackMarket" }, - "crypt" : { "index" :84, "handler": "bank" }, - "shipwreck" : { "index" :85, "handler": "bank" }, - "derelictShip" : { "index" :24, "handler": "bank" }, - "dragonUtopia" : { "index" :25, "handler": "bank" }, - "pandoraBox" : { "index" :6, "handler": "pandora" }, "event" : { "index" :26, "handler": "event" }, @@ -45,7 +40,6 @@ "magicWell" : { "index" :49, "handler": "magicWell" }, "obelisk" : { "index" :57, "handler": "obelisk" }, "oceanBottle" : { "index" :59, "handler": "sign" }, - "pyramid" : { "index" :63, "handler": "pyramid" }, "scholar" : { "index" :81, "handler": "scholar" }, "shipyard" : { "index" :87, "handler": "shipyard" }, "sign" : { "index" :91, "handler": "sign" }, diff --git a/config/objects/moddables.json b/config/objects/moddables.json index 195e4989d..b03c5f6a5 100644 --- a/config/objects/moddables.json +++ b/config/objects/moddables.json @@ -184,9 +184,6 @@ } }, - // subtype: different content - "creatureBank" : { "index" :16, "handler": "bank" }, - // subtype: 0 = normal, 1 = anti-magic "garrisonHorizontal" : { "index" :33, "handler": "garrison" }, "garrisonVertical" : { "index" :219, "handler": "garrison" }, diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 72e9f5065..561300a7b 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -262,12 +262,21 @@ CArtifact * CArtHandler::loadFromJson(const JsonNode & node) return art; } -void CArtHandler::addSlot(CArtifact * art, const std::string & slotID) +ArtifactPosition CArtHandler::stringToSlot(std::string slotName) { #define ART_POS(x) ( #x, ArtifactPosition::x ) static const std::map artifactPositionMap = boost::assign::map_list_of ART_POS_LIST; #undef ART_POS + auto it = artifactPositionMap.find (slotName); + if (it != artifactPositionMap.end()) + return it->second; + logGlobal->warnStream() << "Warning! Artifact slot " << slotName << " not recognized!"; + return ArtifactPosition::PRE_FIRST; +} + +void CArtHandler::addSlot(CArtifact * art, const std::string & slotID) +{ if (slotID == "MISC") { art->possibleSlots[ArtBearer::HERO] += ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5; @@ -278,14 +287,9 @@ void CArtHandler::addSlot(CArtifact * art, const std::string & slotID) } else { - auto it = artifactPositionMap.find (slotID); - if (it != artifactPositionMap.end()) - { - auto slot = it->second; + auto slot = stringToSlot(slotID); + if (slot != ArtifactPosition::PRE_FIRST) art->possibleSlots[ArtBearer::HERO].push_back (slot); - } - else - logGlobal->warnStream() << "Warning! Artifact slot " << slotID << " not recognized!"; } } @@ -303,7 +307,7 @@ void CArtHandler::loadSlots(CArtifact * art, const JsonNode & node) } } -void CArtHandler::loadClass(CArtifact * art, const JsonNode & node) +CArtifact::EartClass CArtHandler::stringToClass(std::string className) { static const std::map artifactClassMap = boost::assign::map_list_of ("TREASURE", CArtifact::ART_TREASURE) @@ -312,16 +316,17 @@ void CArtHandler::loadClass(CArtifact * art, const JsonNode & node) ("RELIC", CArtifact::ART_RELIC) ("SPECIAL", CArtifact::ART_SPECIAL); - auto it = artifactClassMap.find (node["class"].String()); + auto it = artifactClassMap.find (className); if (it != artifactClassMap.end()) - { - art->aClass = it->second; - } - else - { - logGlobal->warnStream() << "Warning! Artifact rarity " << node["class"].String() << " not recognized!"; - art->aClass = CArtifact::ART_SPECIAL; - } + return it->second; + + logGlobal->warnStream() << "Warning! Artifact rarity " << className << " not recognized!"; + return CArtifact::ART_SPECIAL; +} + +void CArtHandler::loadClass(CArtifact * art, const JsonNode & node) +{ + art->aClass = stringToClass(node["class"].String()); } void CArtHandler::loadType(CArtifact * art, const JsonNode & node) @@ -424,7 +429,7 @@ CreatureID CArtHandler::machineIDToCreature(ArtifactID id) return CreatureID::NONE; //this artifact is not a creature } -ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags) +ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags, std::function accepts) { auto getAllowedArts = [&](std::vector > &out, std::vector *arts, CArtifact::EartClass flag) { @@ -433,8 +438,11 @@ ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags) for (auto & arts_i : *arts) { - CArtifact *art = arts_i; - out.push_back(art); + if (accepts(arts_i->id)) + { + CArtifact *art = arts_i; + out.push_back(art); + } } }; @@ -469,6 +477,16 @@ ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags) return artID; } +ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, std::function accepts) +{ + return pickRandomArtifact(rand, 0xff, accepts); +} + +ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags) +{ + return pickRandomArtifact(rand, flags, [](ArtifactID){ return true;}); +} + Bonus *createBonus(Bonus::BonusType type, int val, int subtype, Bonus::ValueType valType, shared_ptr limiter = shared_ptr(), int additionalInfo = 0) { auto added = new Bonus(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,-1,subtype); diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index 1105f9d4a..592e1de58 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -203,11 +203,17 @@ public: boost::optional&> listFromClass(CArtifact::EartClass artifactClass); + ArtifactPosition stringToSlot(std::string slotName); + CArtifact::EartClass stringToClass(std::string className); + /// Gets a artifact ID randomly and removes the selected artifact from this handler. ArtifactID pickRandomArtifact(CRandomGenerator & rand, int flags); + ArtifactID pickRandomArtifact(CRandomGenerator & rand, std::function accepts); + ArtifactID pickRandomArtifact(CRandomGenerator & rand, int flags, std::function accepts); + bool legalArtifact(ArtifactID id); - void getAllowedArts(std::vector > &out, std::vector *arts, int flag); - void getAllowed(std::vector > &out, int flags); + //void getAllowedArts(std::vector > &out, std::vector *arts, int flag); + //void getAllowed(std::vector > &out, int flags); bool isBigArtifact (ArtifactID artID) const {return bigArtifacts.find(artID) != bigArtifacts.end();} void initAllowedArtifactsList(const std::vector &allowed); //allowed[art_id] -> 0 if not allowed, 1 if allowed static ArtifactID creatureToMachineID(CreatureID id); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 6ef239348..89ae588cb 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -31,6 +31,7 @@ set(lib_SRCS mapObjects/CQuest.cpp mapObjects/CRewardableConstructor.cpp mapObjects/CRewardableObject.cpp + mapObjects/JsonRandom.cpp mapObjects/MiscObjects.cpp mapObjects/ObjectTemplate.cpp diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 62ff3ce37..5f8c0d666 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1021,8 +1021,7 @@ namespace ObjProperty BONUS_VALUE_FIRST, BONUS_VALUE_SECOND, //used in Rampart for special building that generates resources (storing resource type and quantity) //creature-bank specific - BANK_DAYCOUNTER, BANK_CLEAR_ARTIFACTS, BANK_ADD_ARTIFACT, BANK_MULTIPLIER, BANK_CONFIG_PRESET, - BANK_CLEAR_CONFIG, BANK_INIT_ARMY, BANK_RESET, + BANK_DAYCOUNTER, BANK_RESET, BANK_CLEAR, //object with reward REWARD_RESET, REWARD_SELECT diff --git a/lib/VCMI_Lib.cpp b/lib/VCMI_Lib.cpp index 706c21886..620663058 100644 --- a/lib/VCMI_Lib.cpp +++ b/lib/VCMI_Lib.cpp @@ -45,11 +45,11 @@ DLL_LINKAGE void preinitDLL(CConsoleHandler *Console) DLL_LINKAGE void loadDLLClasses() { - try + //try { VLC->init(); } - HANDLE_EXCEPTION; + //HANDLE_EXCEPTION; } const IBonusTypeHandler * LibClasses::getBth() const diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index 0302310d9..8f4f52582 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -14,6 +14,8 @@ #include "../NetPacks.h" #include "../CGeneralTextHandler.h" #include "../CSoundBase.h" +#include "CommonConstructors.h" +#include "../CSpellHandler.h" using namespace boost::assign; @@ -24,134 +26,51 @@ static std::string & visitedTxt(const bool visited) return VLC->generaltexth->allTexts[id]; } +CBank::CBank() +{ +} + +CBank::~CBank() +{ +} + void CBank::initObj() { - index = VLC->objh->bankObjToIndex(this); - bc = nullptr; daycounter = 0; - multiplier = 1; + resetDuration = 0; + VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, cb->gameState()->getRandomGenerator()); } + const std::string & CBank::getHoverText() const { bool visited = (bc == nullptr); - hoverName = VLC->objh->creBanksNames[index] + " " + visitedTxt(visited); + hoverName = visitedTxt(visited); // FIXME: USE BANK_SPECIFIC NAMES return hoverName; } -void CBank::reset(ui16 var1) //prevents desync + +void CBank::setConfig(const BankConfig & config) { - ui8 chance = 0; - for (auto & elem : VLC->objh->banksInfo[index]) - { - if (var1 < (chance += elem->chance)) - { - bc = elem; - break; - } - } - artifacts.clear(); + bc.reset(new BankConfig(config)); + clear(); // remove all stacks, if any + + for (auto & stack : config.guards) + setCreature (SlotID(stacksCount()), stack.type->idNumber, stack.count); } -void CBank::initialize() const -{ - cb->setObjProperty(id, ObjProperty::BANK_RESET, cb->gameState()->getRandomGenerator().nextInt()); //synchronous reset - - for (ui8 i = 0; i <= 3; i++) - { - for (ui8 n = 0; n < bc->artifacts[i]; n++) - { - CArtifact::EartClass artClass; - switch(i) - { - case 0: artClass = CArtifact::ART_TREASURE; break; - case 1: artClass = CArtifact::ART_MINOR; break; - case 2: artClass = CArtifact::ART_MAJOR; break; - case 3: artClass = CArtifact::ART_RELIC; break; - default: assert(0); continue; - } - - int artID = VLC->arth->pickRandomArtifact(cb->gameState()->getRandomGenerator(), artClass); - cb->setObjProperty(id, ObjProperty::BANK_ADD_ARTIFACT, artID); - } - } - - cb->setObjProperty(id, ObjProperty::BANK_INIT_ARMY, cb->gameState()->getRandomGenerator().nextInt()); //get army -} void CBank::setPropertyDer (ui8 what, ui32 val) -/// random values are passed as arguments and processed identically on all clients { switch (what) { case ObjProperty::BANK_DAYCOUNTER: //daycounter - if (val == 0) - daycounter = 1; //yes, 1 - else - daycounter++; - break; - case ObjProperty::BANK_MULTIPLIER: //multiplier, in percent - multiplier = val / 100.0; - break; - case 13: //bank preset - bc = VLC->objh->banksInfo[index][val]; + daycounter+=val; break; case ObjProperty::BANK_RESET: - reset (val%100); + initObj(); + daycounter = 1; //yes, 1 since "today" daycounter won't be incremented break; - case ObjProperty::BANK_CLEAR_CONFIG: - bc = nullptr; + case ObjProperty::BANK_CLEAR: + bc.reset(); break; - case ObjProperty::BANK_CLEAR_ARTIFACTS: //remove rewards from Derelict Ship - artifacts.clear(); - break; - case ObjProperty::BANK_INIT_ARMY: //set ArmedInstance army - { - int upgraded = 0; - if (val%100 < bc->upgradeChance) //once again anti-desync - upgraded = 1; - switch (bc->guards.size()) - { - case 1: - for (int i = 0; i < 4; ++i) - setCreature (SlotID(i), bc->guards[0].first, bc->guards[0].second / 5 ); - setCreature (SlotID(4), CreatureID(bc->guards[0].first + upgraded), bc->guards[0].second / 5 ); - break; - case 4: - { - if (bc->guards.back().second) //all stacks are present - { - for (auto & elem : bc->guards) - { - setCreature (SlotID(stacksCount()), elem.first, elem.second); - } - } - else if (bc->guards[2].second)//Wraiths are present, split two stacks in Crypt - { - setCreature (SlotID(0), bc->guards[0].first, bc->guards[0].second / 2 ); - setCreature (SlotID(1), bc->guards[1].first, bc->guards[1].second / 2); - setCreature (SlotID(2), CreatureID(bc->guards[2].first + upgraded), bc->guards[2].second); - setCreature (SlotID(3), bc->guards[1].first, bc->guards[1].second / 2 ); - setCreature (SlotID(4), bc->guards[0].first, bc->guards[0].second - (bc->guards[0].second / 2) ); - - } - else //split both stacks - { - for (int i = 0; i < 3; ++i) //skellies - setCreature (SlotID(2*i), bc->guards[0].first, bc->guards[0].second / 3); - for (int i = 0; i < 2; ++i) //zombies - setCreature (SlotID(2*i+1), bc->guards[1].first, bc->guards[1].second / 2); - } - } - break; - default: - logGlobal->warnStream() << "Error: Unexpected army data: " << bc->guards.size() <<" items found"; - return; - } - } - break; - case ObjProperty::BANK_ADD_ARTIFACT: //add Artifact - { - artifacts.push_back (val); - break; - } } } @@ -159,25 +78,19 @@ void CBank::newTurn() const { if (bc == nullptr) { - if (cb->getDate() == 1) - initialize(); //initialize on first day - else if (daycounter >= 28 && (subID < 13 || subID > 16)) //no reset for Emissaries + if (resetDuration != 0) { - initialize(); - cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 0); //daycounter 0 - if (ID == Obj::DERELICT_SHIP && cb->getDate() > 1) - { - cb->setObjProperty (id, ObjProperty::BANK_MULTIPLIER, 0);//ugly hack to make derelict ships usable only once - cb->setObjProperty (id, ObjProperty::BANK_CLEAR_ARTIFACTS, 0); - } + if (daycounter >= resetDuration) + cb->setObjProperty (id, ObjProperty::BANK_RESET, 0); //daycounter 0 + else + cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++ } - else - cb->setObjProperty (id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++ } } + bool CBank::wasVisited (PlayerColor player) const { - return !bc; + return !bc; //FIXME: player A should not know about visit done by player B } void CBank::onHeroVisit (const CGHeroInstance * h) const @@ -185,6 +98,7 @@ void CBank::onHeroVisit (const CGHeroInstance * h) const if (bc) { int banktext = 0; + ui16 soundID = soundBase::ROGUE; switch (ID) { case Obj::CREATURE_BANK: @@ -202,13 +116,17 @@ void CBank::onHeroVisit (const CGHeroInstance * h) const case Obj::SHIPWRECK: banktext = 122; break; + case Obj::PYRAMID: + soundID = soundBase::MYSTERY; + banktext = 105; + break; } BlockingDialog bd (true, false); bd.player = h->getOwner(); - bd.soundID = soundBase::ROGUE; - bd.text.addTxt(MetaString::ADVOB_TXT,banktext); - if (ID == Obj::CREATURE_BANK) - bd.text.addReplacement(VLC->objh->creBanksNames[index]); + bd.soundID = soundID; + bd.text.addTxt(MetaString::ADVOB_TXT, banktext); + //if (ID == Obj::CREATURE_BANK) + // bd.text.addReplacement(VLC->objh->creBanksNames[index]); // FIXME: USE BANK SPECIFIC NAMES cb->showBlockingDialog (&bd); } else @@ -216,105 +134,100 @@ void CBank::onHeroVisit (const CGHeroInstance * h) const InfoWindow iw; iw.soundID = soundBase::GRAVEYARD; iw.player = h->getOwner(); - if (ID == Obj::CRYPT) //morale penalty for empty Crypt + if (ID == Obj::PYRAMID) // You come upon the pyramid ... pyramid is completely empty. { - GiveBonus gbonus; - gbonus.id = h->id.getNum(); - gbonus.bonus.duration = Bonus::ONE_BATTLE; - gbonus.bonus.source = Bonus::OBJECT; - gbonus.bonus.sid = ID; - gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[98]; - gbonus.bonus.type = Bonus::MORALE; - gbonus.bonus.val = -1; - cb->giveHeroBonus(&gbonus); - iw.text << VLC->generaltexth->advobtxt[120]; - iw.components.push_back (Component (Component::MORALE, 0 , -1, 0)); + iw.text << VLC->generaltexth->advobtxt[107]; + iw.components.push_back (Component (Component::LUCK, 0 , -2, 0)); + GiveBonus gb; + gb.bonus = Bonus(Bonus::ONE_BATTLE,Bonus::LUCK,Bonus::OBJECT,-2,id.getNum(),VLC->generaltexth->arraytxt[70]); + gb.id = h->id.getNum(); + cb->giveHeroBonus(&gb); } else { - iw.text << VLC->generaltexth->advobtxt[33]; - iw.text.addReplacement(VLC->objh->creBanksNames[index]); + iw.text << VLC->generaltexth->advobtxt[33];// This was X, now is completely empty + //iw.text.addReplacement(VLC->objh->creBanksNames[index]); // FIXME: USE BANK SPECIFIC NAMES } cb->showInfoDialog(&iw); } } -void CBank::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const +void CBank::doVisit(const CGHeroInstance * hero) const { - if (result.winner == 0) + int textID = -1; + InfoWindow iw; + iw.player = hero->getOwner(); + MetaString loot; + + switch (ID) { - int textID = -1; - InfoWindow iw; - iw.player = hero->getOwner(); - MetaString loot; - - switch (ID) + case Obj::CREATURE_BANK: + case Obj::DRAGON_UTOPIA: + textID = 34; + break; + case Obj::DERELICT_SHIP: + if (!bc) + textID = 43; + else { - case Obj::CREATURE_BANK: case Obj::DRAGON_UTOPIA: - textID = 34; - break; - case Obj::DERELICT_SHIP: - if (multiplier) - textID = 43; - else - { - GiveBonus gbonus; - gbonus.id = hero->id.getNum(); - gbonus.bonus.duration = Bonus::ONE_BATTLE; - gbonus.bonus.source = Bonus::OBJECT; - gbonus.bonus.sid = ID; - gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[101]; - gbonus.bonus.type = Bonus::MORALE; - gbonus.bonus.val = -1; - cb->giveHeroBonus(&gbonus); - textID = 42; - iw.components.push_back (Component (Component::MORALE, 0 , -1, 0)); - } - break; - case Obj::CRYPT: - if (bc->resources.size() != 0) - textID = 121; - else - { - iw.components.push_back (Component (Component::MORALE, 0 , -1, 0)); - GiveBonus gbonus; - gbonus.id = hero->id.getNum(); - gbonus.bonus.duration = Bonus::ONE_BATTLE; - gbonus.bonus.source = Bonus::OBJECT; - gbonus.bonus.sid = ID; - gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[ID]; - gbonus.bonus.type = Bonus::MORALE; - gbonus.bonus.val = -1; - cb->giveHeroBonus(&gbonus); - textID = 120; - iw.components.push_back (Component (Component::MORALE, 0 , -1, 0)); - } - break; - case Obj::SHIPWRECK: - if (bc->resources.size()) - textID = 124; - else - textID = 123; - break; + GiveBonus gbonus; + gbonus.id = hero->id.getNum(); + gbonus.bonus.duration = Bonus::ONE_BATTLE; + gbonus.bonus.source = Bonus::OBJECT; + gbonus.bonus.sid = ID; + gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[101]; + gbonus.bonus.type = Bonus::MORALE; + gbonus.bonus.val = -1; + cb->giveHeroBonus(&gbonus); + textID = 42; + iw.components.push_back (Component (Component::MORALE, 0 , -1, 0)); } - - //grant resources - if (textID != 42) //empty derelict ship gives no cash + break; + case Obj::CRYPT: + if (bc) + textID = 121; + else { - for (int it = 0; it < bc->resources.size(); it++) + iw.components.push_back (Component (Component::MORALE, 0 , -1, 0)); + GiveBonus gbonus; + gbonus.id = hero->id.getNum(); + gbonus.bonus.duration = Bonus::ONE_BATTLE; + gbonus.bonus.source = Bonus::OBJECT; + gbonus.bonus.sid = ID; + gbonus.bdescr << "\n" << VLC->generaltexth->arraytxt[ID]; + gbonus.bonus.type = Bonus::MORALE; + gbonus.bonus.val = -1; + cb->giveHeroBonus(&gbonus); + textID = 120; + iw.components.push_back (Component (Component::MORALE, 0 , -1, 0)); + } + break; + case Obj::SHIPWRECK: + if (bc) + textID = 124; + else + textID = 123; + break; + case Obj::PYRAMID: + textID = 106; + } + + //grant resources + if (bc) + { + for (int it = 0; it < bc->resources.size(); it++) + { + if (bc->resources[it] != 0) { - if (bc->resources[it] != 0) - { - iw.components.push_back (Component (Component::RESOURCE, it, bc->resources[it], 0)); - loot << "%d %s"; - loot.addReplacement(iw.components.back().val); - loot.addReplacement(MetaString::RES_NAMES, iw.components.back().subtype); - cb->giveResource (hero->getOwner(), static_cast(it), bc->resources[it]); - } + iw.components.push_back (Component (Component::RESOURCE, it, bc->resources[it], 0)); + loot << "%d %s"; + loot.addReplacement(iw.components.back().val); + loot.addReplacement(MetaString::RES_NAMES, iw.components.back().subtype); + cb->giveResource (hero->getOwner(), static_cast(it), bc->resources[it]); } } //grant artifacts - for (auto & elem : artifacts) + for (auto & elem : bc->artifacts) { iw.components.push_back (Component (Component::ARTIFACT, elem, 0, 0)); loot << "%s"; @@ -327,22 +240,52 @@ void CBank::battleFinished(const CGHeroInstance *hero, const BattleResult &resul iw.text.addTxt (MetaString::ADVOB_TXT, textID); if (textID == 34) { - iw.text.addReplacement(MetaString::CRE_PL_NAMES, result.casualties[1].begin()->first); + const CCreature * strongest = boost::range::max_element(bc->guards, [](const CStackBasicDescriptor & a, const CStackBasicDescriptor & b) + { + return a.type->fightValue < b.type->fightValue; + })->type; + + iw.text.addReplacement(MetaString::CRE_PL_NAMES, strongest->idNumber); iw.text.addReplacement(loot.buildList()); } cb->showInfoDialog(&iw); } + + if (!bc->spells.empty()) + { + std::set spells; + + bool noWisdom = false; + for (SpellID spell : bc->spells) + { + iw.text.addTxt (MetaString::SPELL_NAME, spell); + if (VLC->spellh->objects[spell]->level <= hero->getSecSkillLevel(SecondarySkill::WISDOM) + 2) + { + spells.insert(spell); + iw.components.push_back(Component (Component::SPELL, spell, 0, 0)); + } + else + noWisdom = true; + } + + if (!hero->getArt(ArtifactPosition::SPELLBOOK)) + iw.text.addTxt (MetaString::ADVOB_TXT, 109); //no spellbook + else if (noWisdom) + iw.text.addTxt (MetaString::ADVOB_TXT, 108); //no expert Wisdom + if (spells.empty()) + cb->changeSpells (hero, true, spells); + } loot.clear(); iw.components.clear(); iw.text.clear(); //grant creatures CCreatureSet ourArmy; - for (auto it = bc->creatures.cbegin(); it != bc->creatures.cend(); it++) + for (auto slot : bc->creatures) { - SlotID slot = ourArmy.getSlotFor(it->first); - ourArmy.addToSlot(slot, it->first, it->second); + ourArmy.addToSlot(ourArmy.getSlotFor(slot.type->idNumber), slot.type->idNumber, slot.count); } + for (auto & elem : ourArmy.Slots()) { iw.components.push_back(Component(*elem.second)); @@ -362,7 +305,15 @@ void CBank::battleFinished(const CGHeroInstance *hero, const BattleResult &resul cb->showInfoDialog(&iw); cb->giveCreatures(this, hero, ourArmy, false); } - cb->setObjProperty (id, ObjProperty::BANK_CLEAR_CONFIG, 0); //bc = nullptr + cb->setObjProperty (id, ObjProperty::BANK_CLEAR, 0); //bc = nullptr + } +} + +void CBank::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const +{ + if (result.winner == 0) + { + doVisit(hero); } } @@ -370,74 +321,9 @@ void CBank::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) cons { if (answer) { - cb->startBattleI(hero, this, true); - } -} - -void CGPyramid::initObj() -{ - std::vector available; - cb->getAllowedSpells (available, 5); - if (available.size()) - { - bc = VLC->objh->banksInfo[21].front(); //TODO: remove hardcoded value? - spell = *RandomGeneratorUtil::nextItem(available, cb->gameState()->getRandomGenerator()); - } - else - { - logGlobal->errorStream() <<"No spells available for Pyramid! Object set to empty."; - } - setPropertyDer(ObjProperty::BANK_INIT_ARMY, cb->gameState()->getRandomGenerator().nextInt()); //set guards at game start -} -const std::string & CGPyramid::getHoverText() const -{ - hoverName = VLC->objh->creBanksNames[21]+ " " + visitedTxt((bc==nullptr)); - return hoverName; -} -void CGPyramid::onHeroVisit (const CGHeroInstance * h) const -{ - if (bc) - { - BlockingDialog bd (true, false); - bd.player = h->getOwner(); - bd.soundID = soundBase::MYSTERY; - bd.text << VLC->generaltexth->advobtxt[105]; - cb->showBlockingDialog(&bd); - } - else - { - InfoWindow iw; - iw.player = h->getOwner(); - iw.text << VLC->generaltexth->advobtxt[107]; - iw.components.push_back (Component (Component::LUCK, 0 , -2, 0)); - GiveBonus gb; - gb.bonus = Bonus(Bonus::ONE_BATTLE,Bonus::LUCK,Bonus::OBJECT,-2,id.getNum(),VLC->generaltexth->arraytxt[70]); - gb.id = h->id.getNum(); - cb->giveHeroBonus(&gb); - cb->showInfoDialog(&iw); - } -} - -void CGPyramid::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const -{ - if (result.winner == 0) - { - InfoWindow iw; - iw.player = hero->getOwner(); - iw.text.addTxt (MetaString::ADVOB_TXT, 106); - iw.text.addTxt (MetaString::SPELL_NAME, spell); - if (!hero->getArt(ArtifactPosition::SPELLBOOK)) - iw.text.addTxt (MetaString::ADVOB_TXT, 109); //no spellbook - else if (hero->getSecSkillLevel(SecondarySkill::WISDOM) < 3) - iw.text.addTxt (MetaString::ADVOB_TXT, 108); //no expert Wisdom + if (bc) // not looted bank + cb->startBattleI(hero, this, true); else - { - std::set spells; - spells.insert (SpellID(spell)); - cb->changeSpells (hero, true, spells); - iw.components.push_back(Component (Component::SPELL, spell, 0, 0)); - } - cb->showInfoDialog(&iw); - cb->setObjProperty (id, ObjProperty::BANK_CLEAR_CONFIG, 0); + doVisit(hero); } } diff --git a/lib/mapObjects/CBank.h b/lib/mapObjects/CBank.h index 71e1bc00d..c8f8471aa 100644 --- a/lib/mapObjects/CBank.h +++ b/lib/mapObjects/CBank.h @@ -13,19 +13,26 @@ * */ +class BankConfig; +class CBankInstanceConstructor; + class DLL_LINKAGE CBank : public CArmedInstance { - public: - int index; //banks have unusal numbering - see ZCRBANK.txt and initObj() - BankConfig *bc; - double multiplier; //for improved banks script - std::vector artifacts; //fixed and deterministic + std::unique_ptr bc; ui32 daycounter; + ui32 resetDuration; + + void setPropertyDer(ui8 what, ui32 val) override; + void doVisit(const CGHeroInstance * hero) const; + +public: + CBank(); + ~CBank(); + + void setConfig(const BankConfig & bc); void initObj() override; const std::string & getHoverText() const override; - void initialize() const; - void reset(ui16 var1); void newTurn() const override; bool wasVisited (PlayerColor player) const override; void onHeroVisit(const CGHeroInstance * h) const override; @@ -35,25 +42,8 @@ class DLL_LINKAGE CBank : public CArmedInstance template void serialize(Handler &h, const int version) { h & static_cast(*this); - h & index & multiplier & artifacts & daycounter & bc; + h & daycounter & bc & resetDuration; } -protected: - void setPropertyDer(ui8 what, ui32 val) override; -}; -class DLL_LINKAGE CGPyramid : public CBank -{ -public: - ui16 spell; - void initObj() override; - const std::string & getHoverText() const override; - void newTurn() const override {}; //empty, no reset - void onHeroVisit(const CGHeroInstance * h) const override; - void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; - - template void serialize(Handler &h, const int version) - { - h & static_cast(*this); - h & spell; - } + friend class CBankInstanceConstructor; }; diff --git a/lib/mapObjects/CObjectClassesHandler.cpp b/lib/mapObjects/CObjectClassesHandler.cpp index 553a40a15..122068eb6 100644 --- a/lib/mapObjects/CObjectClassesHandler.cpp +++ b/lib/mapObjects/CObjectClassesHandler.cpp @@ -35,13 +35,13 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER_CLASS("dwelling", CDwellingInstanceConstructor); SET_HANDLER_CLASS("hero", CHeroInstanceConstructor); SET_HANDLER_CLASS("town", CTownInstanceConstructor); + SET_HANDLER_CLASS("bank", CBankInstanceConstructor); SET_HANDLER_CLASS("static", CObstacleConstructor); SET_HANDLER_CLASS("", CObstacleConstructor); SET_HANDLER("generic", CGObjectInstance); SET_HANDLER("market", CGMarket); - SET_HANDLER("bank", CBank); SET_HANDLER("cartographer", CCartographer); SET_HANDLER("artifact", CGArtifact); SET_HANDLER("blackMarket", CGBlackMarket); @@ -67,7 +67,6 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER("pandora", CGPandoraBox); SET_HANDLER("pickable", CGPickable); SET_HANDLER("prison", CGHeroInstance); - SET_HANDLER("pyramid", CGPyramid); SET_HANDLER("questGuard", CGQuestGuard); SET_HANDLER("resource", CGResource); SET_HANDLER("scholar", CGScholar); @@ -130,6 +129,11 @@ si32 selectNextID(const JsonNode & fixedID, const Map & map, si32 defaultID) void CObjectClassesHandler::loadObjectEntry(const JsonNode & entry, ObjectContainter * obj) { + if (!handlerConstructors.count(obj->handlerName)) + { + logGlobal->errorStream() << "Handler with name " << obj->handlerName << " was not found!"; + return; + } auto handler = handlerConstructors.at(obj->handlerName)(); handler->init(entry); diff --git a/lib/mapObjects/CObjectClassesHandler.h b/lib/mapObjects/CObjectClassesHandler.h index 441f57a26..413deea9e 100644 --- a/lib/mapObjects/CObjectClassesHandler.h +++ b/lib/mapObjects/CObjectClassesHandler.h @@ -50,20 +50,45 @@ struct RandomMapInfo class IObjectInfo { public: - virtual bool givesResources() const = 0; + struct CArmyStructure + { + ui32 totalStrength; + ui32 shootersStrength; + ui32 flyersStrength; + ui32 walkersStrength; - virtual bool givesExperience() const = 0; - virtual bool givesMana() const = 0; - virtual bool givesMovement() const = 0; + CArmyStructure() : + totalStrength(0), + shootersStrength(0), + flyersStrength(0), + walkersStrength(0) + {} - virtual bool givesPrimarySkills() const = 0; - virtual bool givesSecondarySkills() const = 0; + bool operator <(const CArmyStructure & other) + { + return this->totalStrength < other.totalStrength; + } + }; - virtual bool givesArtifacts() const = 0; - virtual bool givesCreatures() const = 0; - virtual bool givesSpells() const = 0; + /// Returns possible composition of guards. Actual guards would be + /// somewhere between these two values + virtual CArmyStructure minGuards() const { return CArmyStructure(); } + virtual CArmyStructure maxGuards() const { return CArmyStructure(); } - virtual bool givesBonuses() const = 0; + virtual bool givesResources() const { return false; } + + virtual bool givesExperience() const { return false; } + virtual bool givesMana() const { return false; } + virtual bool givesMovement() const { return false; } + + virtual bool givesPrimarySkills() const { return false; } + virtual bool givesSecondarySkills() const { return false; } + + virtual bool givesArtifacts() const { return false; } + virtual bool givesCreatures() const { return false; } + virtual bool givesSpells() const { return false; } + + virtual bool givesBonuses() const { return false; } }; class CGObjectInstance; @@ -118,7 +143,7 @@ public: virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const = 0; /// Returns object configuration, if available. Othervice returns NULL - virtual const IObjectInfo * getObjectInfo(ObjectTemplate tmpl) const = 0; + virtual std::unique_ptr getObjectInfo(ObjectTemplate tmpl) const = 0; template void serialize(Handler &h, const int version) { diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index 828a7ce9a..bdc06a286 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -108,53 +108,6 @@ void IObjectInterface::garrisonDialogClosed(const CGHeroInstance *hero) const void IObjectInterface::heroLevelUpDone(const CGHeroInstance *hero) const {} -// Bank helper. Find the creature ID and their number, and store the -// result in storage (either guards or reward creatures). -static void readCreatures(const JsonNode &creature, std::vector< std::pair > &storage) -{ - std::pair creInfo = std::make_pair(CreatureID::NONE, 0); - - //TODO: replace numeric id's with mod-friendly string id's - creInfo.second = creature["number"].Float(); - creInfo.first = CreatureID((si32)creature["id"].Float()); - storage.push_back(creInfo); -} - -// Bank helper. Process a bank level. -static void readBankLevel(const JsonNode &level, BankConfig &bc) -{ - int idx; - - bc.chance = level["chance"].Float(); - - for(const JsonNode &creature : level["guards"].Vector()) - { - readCreatures(creature, bc.guards); - } - - bc.upgradeChance = level["upgrade_chance"].Float(); - bc.combatValue = level["combat_value"].Float(); - - bc.resources = Res::ResourceSet(level["reward_resources"]); - - for(const JsonNode &creature : level["reward_creatures"].Vector()) - { - readCreatures(creature, bc.creatures); - } - - bc.artifacts.resize(4); - idx = 0; - for(const JsonNode &artifact : level["reward_artifacts"].Vector()) - { - bc.artifacts[idx] = artifact.Float(); - idx ++; - } - - bc.value = level["value"].Float(); - bc.rewardDifficulty = level["profitability"].Float(); - bc.easiest = level["easiest"].Float(); -} - CObjectHandler::CObjectHandler() { logGlobal->traceStream() << "\t\tReading resources prices "; @@ -164,62 +117,8 @@ CObjectHandler::CObjectHandler() resVals.push_back(price.Float()); } logGlobal->traceStream() << "\t\tDone loading resource prices!"; - - logGlobal->traceStream() << "\t\tReading banks configs"; - const JsonNode config3(ResourceID("config/bankconfig.json")); - int bank_num = 0; - for(const JsonNode &bank : config3["banks"].Vector()) - { - creBanksNames[bank_num] = bank["name"].String(); - - int level_num = 0; - for(const JsonNode &level : bank["levels"].Vector()) - { - banksInfo[bank_num].push_back(new BankConfig); - BankConfig &bc = *banksInfo[bank_num].back(); - bc.level = level_num; - - readBankLevel(level, bc); - level_num ++; - } - - bank_num ++; - } - logGlobal->traceStream() << "\t\tDone loading banks configs"; } -CObjectHandler::~CObjectHandler() -{ - for(auto & mapEntry : banksInfo) - { - for(auto & vecEntry : mapEntry.second) - { - vecEntry.dellNull(); - } - } -} - -int CObjectHandler::bankObjToIndex (const CGObjectInstance * obj) -{ - switch (obj->ID) //find appriopriate key - { - case Obj::CREATURE_BANK: - return obj->subID; - case Obj::DERELICT_SHIP: - return 8; - case Obj::DRAGON_UTOPIA: - return 10; - case Obj::CRYPT: - return 9; - case Obj::SHIPWRECK: - return 7; - case Obj::PYRAMID: - return 21; - default: - logGlobal->warnStream() << "Unrecognized Bank indetifier!"; - return 0; - } -} PlayerColor CGObjectInstance::getOwner() const { //if (state) diff --git a/lib/mapObjects/CObjectHandler.h b/lib/mapObjects/CObjectHandler.h index a9ab899b0..e1031a123 100644 --- a/lib/mapObjects/CObjectHandler.h +++ b/lib/mapObjects/CObjectHandler.h @@ -163,41 +163,15 @@ private: CGObjectInstance * obj; }; -struct BankConfig -{ - BankConfig() {level = chance = upgradeChance = combatValue = value = rewardDifficulty = easiest = 0; }; - ui8 level; //1 - 4, how hard the battle will be - ui8 chance; //chance for this level being chosen - ui8 upgradeChance; //chance for creatures to be in upgraded versions - std::vector< std::pair > guards; //creature ID, amount - ui32 combatValue; //how hard are guards of this level - Res::ResourceSet resources; //resources given in case of victory - std::vector< std::pair > creatures; //creatures granted in case of victory (creature ID, amount) - std::vector artifacts; //number of artifacts given in case of victory [0] -> treasure, [1] -> minor [2] -> major [3] -> relic - ui32 value; //overall value of given things - ui32 rewardDifficulty; //proportion of reward value to difficulty of guards; how profitable is this creature Bank config - ui16 easiest; //?!? - - template void serialize(Handler &h, const int version) - { - h & level & chance & upgradeChance & guards & combatValue & resources & creatures & artifacts & value & rewardDifficulty & easiest; - } -}; - class DLL_LINKAGE CObjectHandler { public: - std::map > > banksInfo; //[index][preset] - std::map creBanksNames; //[crebank index] -> name of this creature bank std::vector resVals; //default values of resources in gold CObjectHandler(); - ~CObjectHandler(); - - int bankObjToIndex (const CGObjectInstance * obj); template void serialize(Handler &h, const int version) { - h & banksInfo & creBanksNames & resVals; + h & resVals; } }; diff --git a/lib/mapObjects/CRewardableConstructor.cpp b/lib/mapObjects/CRewardableConstructor.cpp index d2abb8336..e5b02e0c9 100644 --- a/lib/mapObjects/CRewardableConstructor.cpp +++ b/lib/mapObjects/CRewardableConstructor.cpp @@ -4,6 +4,7 @@ #include "../CRandomGenerator.h" #include "../StringConstants.h" #include "../CCreatureHandler.h" +#include "JsonRandom.h" /* * CRewardableConstructor.cpp, part of VCMI engine @@ -16,100 +17,6 @@ */ namespace { - si32 loadValue(const JsonNode & value, CRandomGenerator & rng, si32 defaultValue = 0) - { - if (value.isNull()) - return defaultValue; - if (value.getType() == JsonNode::DATA_FLOAT) - return value.Float(); - si32 min = value["min"].Float(); - si32 max = value["max"].Float(); - return rng.getIntRange(min, max)(); - } - - TResources loadResources(const JsonNode & value, CRandomGenerator & rng) - { - TResources ret; - for (size_t i=0; i loadPrimary(const JsonNode & value, CRandomGenerator & rng) - { - std::vector ret; - for (auto & name : PrimarySkill::names) - { - ret.push_back(loadValue(value[name], rng)); - } - return ret; - } - - std::map loadSecondary(const JsonNode & value, CRandomGenerator & rng) - { - std::map ret; - for (auto & pair : value.Struct()) - { - SecondarySkill id(VLC->modh->identifiers.getIdentifier(pair.second.meta, "skill", pair.first).get()); - ret[id] = loadValue(pair.second, rng); - } - return ret; - } - - std::vector loadArtifacts(const JsonNode & value, CRandomGenerator & rng) - { - std::vector ret; - for (const JsonNode & entry : value.Vector()) - { - ArtifactID art(VLC->modh->identifiers.getIdentifier("artifact", entry).get()); - ret.push_back(art); - } - return ret; - } - - std::vector loadSpells(const JsonNode & value, CRandomGenerator & rng) - { - std::vector ret; - for (const JsonNode & entry : value.Vector()) - { - SpellID spell(VLC->modh->identifiers.getIdentifier("spell", entry).get()); - ret.push_back(spell); - } - return ret; - } - - std::vector loadCreatures(const JsonNode & value, CRandomGenerator & rng) - { - std::vector ret; - for (auto & pair : value.Struct()) - { - CStackBasicDescriptor stack; - stack.type = VLC->creh->creatures[VLC->modh->identifiers.getIdentifier(pair.second.meta, "creature", pair.first).get()]; - stack.count = loadValue(pair.second, rng); - ret.push_back(stack); - } - return ret; - } - - std::vector loadBonuses(const JsonNode & value) - { - std::vector ret; - for (const JsonNode & entry : value.Vector()) - { - Bonus * bonus = JsonUtils::parseBonus(entry); - ret.push_back(*bonus); - delete bonus; - } - return ret; - } - - std::vector loadComponents(const JsonNode & value) - { - //TODO - } - MetaString loadMessage(const JsonNode & value) { MetaString ret; @@ -167,38 +74,42 @@ void CRandomRewardObjectInfo::configureObject(CRewardableObject * object, CRando const JsonNode & limiter = reward["limiter"]; CVisitInfo info; // load limiter - info.limiter.numOfGrants = loadValue(limiter["numOfGrants"], rng); - info.limiter.dayOfWeek = loadValue(limiter["dayOfWeek"], rng); - info.limiter.minLevel = loadValue(limiter["minLevel"], rng); - info.limiter.resources = loadResources(limiter["resources"], rng); + info.limiter.numOfGrants = JsonRandom::loadValue(limiter["numOfGrants"], rng); + info.limiter.dayOfWeek = JsonRandom::loadValue(limiter["dayOfWeek"], rng); + info.limiter.minLevel = JsonRandom::loadValue(limiter["minLevel"], rng); + info.limiter.resources = JsonRandom::loadResources(limiter["resources"], rng); - info.limiter.primary = loadPrimary(limiter["primary"], rng); - info.limiter.secondary = loadSecondary(limiter["secondary"], rng); - info.limiter.artifacts = loadArtifacts(limiter["artifacts"], rng); - info.limiter.creatures = loadCreatures(limiter["creatures"], rng); + info.limiter.primary = JsonRandom::loadPrimary(limiter["primary"], rng); + info.limiter.secondary = JsonRandom::loadSecondary(limiter["secondary"], rng); + info.limiter.artifacts = JsonRandom::loadArtifacts(limiter["artifacts"], rng); + info.limiter.creatures = JsonRandom::loadCreatures(limiter["creatures"], rng); - info.reward.resources = loadResources(reward["resources"], rng); + info.reward.resources = JsonRandom::loadResources(reward["resources"], rng); - info.reward.gainedExp = loadValue(reward["gainedExp"], rng); - info.reward.gainedLevels = loadValue(reward["gainedLevels"], rng); + info.reward.gainedExp = JsonRandom::loadValue(reward["gainedExp"], rng); + info.reward.gainedLevels = JsonRandom::loadValue(reward["gainedLevels"], rng); - info.reward.manaDiff = loadValue(reward["manaPoints"], rng); - info.reward.manaPercentage = loadValue(reward["manaPercentage"], rng, -1); + info.reward.manaDiff = JsonRandom::loadValue(reward["manaPoints"], rng); + info.reward.manaPercentage = JsonRandom::loadValue(reward["manaPercentage"], rng, -1); - info.reward.movePoints = loadValue(reward["movePoints"], rng); - info.reward.movePercentage = loadValue(reward["movePercentage"], rng, -1); + info.reward.movePoints = JsonRandom::loadValue(reward["movePoints"], rng); + info.reward.movePercentage = JsonRandom::loadValue(reward["movePercentage"], rng, -1); - info.reward.bonuses = loadBonuses(reward["bonuses"]); + info.reward.bonuses = JsonRandom::loadBonuses(reward["bonuses"]); - info.reward.primary = loadPrimary(reward["primary"], rng); - info.reward.secondary = loadSecondary(reward["secondary"], rng); + info.reward.primary = JsonRandom::loadPrimary(reward["primary"], rng); + info.reward.secondary = JsonRandom::loadSecondary(reward["secondary"], rng); - info.reward.artifacts = loadArtifacts(reward["artifacts"], rng); - info.reward.spells = loadSpells(reward["spells"], rng); - info.reward.creatures = loadCreatures(reward["creatures"], rng); + std::vector spells; + for (size_t i=0; i<6; i++) + IObjectInterface::cb->getAllowedSpells(spells, i); + + info.reward.artifacts = JsonRandom::loadArtifacts(reward["artifacts"], rng); + info.reward.spells = JsonRandom::loadSpells(reward["spells"], rng, spells); + info.reward.creatures = JsonRandom::loadCreatures(reward["creatures"], rng); info.message = loadMessage(reward["message"]); - info.selectChance = loadValue(reward["selectChance"], rng); + info.selectChance = JsonRandom::loadValue(reward["selectChance"], rng); } object->onSelect = loadMessage(parameters["onSelectMessage"]); @@ -284,7 +195,7 @@ void CRewardableConstructor::configureObject(CGObjectInstance * object, CRandomG objectInfo.configureObject(dynamic_cast(object), rng); } -const IObjectInfo * CRewardableConstructor::getObjectInfo(ObjectTemplate tmpl) const +std::unique_ptr CRewardableConstructor::getObjectInfo(ObjectTemplate tmpl) const { - return &objectInfo; + return std::unique_ptr(new CRandomRewardObjectInfo(objectInfo)); } diff --git a/lib/mapObjects/CRewardableConstructor.h b/lib/mapObjects/CRewardableConstructor.h index a22679167..171b5a6ca 100644 --- a/lib/mapObjects/CRewardableConstructor.h +++ b/lib/mapObjects/CRewardableConstructor.h @@ -54,5 +54,5 @@ public: void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; - const IObjectInfo * getObjectInfo(ObjectTemplate tmpl) const override; + std::unique_ptr getObjectInfo(ObjectTemplate tmpl) const override; }; diff --git a/lib/mapObjects/CommonConstructors.cpp b/lib/mapObjects/CommonConstructors.cpp index 792088726..14f45fd9c 100644 --- a/lib/mapObjects/CommonConstructors.cpp +++ b/lib/mapObjects/CommonConstructors.cpp @@ -3,9 +3,11 @@ #include "CGTownInstance.h" #include "CGHeroInstance.h" +#include "CBank.h" #include "../mapping/CMap.h" #include "../CHeroHandler.h" #include "../CCreatureHandler.h" +#include "JsonRandom.h" /* * CommonConstructors.cpp, part of VCMI engine @@ -176,33 +178,6 @@ CGObjectInstance * CDwellingInstanceConstructor::create(ObjectTemplate tmpl) con return obj; } -namespace -{ - si32 loadValue(const JsonNode & value, CRandomGenerator & rng, si32 defaultValue = 0) - { - if (value.isNull()) - return defaultValue; - if (value.getType() == JsonNode::DATA_FLOAT) - return value.Float(); - si32 min = value["min"].Float(); - si32 max = value["max"].Float(); - return rng.getIntRange(min, max)(); - } - - std::vector loadCreatures(const JsonNode & value, CRandomGenerator & rng) - { - std::vector ret; - for (auto & pair : value.Struct()) - { - CStackBasicDescriptor stack; - stack.type = VLC->creh->creatures[VLC->modh->identifiers.getIdentifier(pair.second.meta, "creature", pair.first).get()]; - stack.count = loadValue(pair.second, rng); - ret.push_back(stack); - } - return ret; - } -} - void CDwellingInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator &rng) const { CGDwelling * dwelling = dynamic_cast(object); @@ -221,7 +196,7 @@ void CDwellingInstanceConstructor::configureObject(CGObjectInstance * object, CR const CCreature * crea = availableCreatures.at(0).at(0); dwelling->putStack(SlotID(0), new CStackInstance(crea->idNumber, crea->growth * 3 )); } - else for (auto & stack : loadCreatures(guards, rng)) + else for (auto & stack : JsonRandom::loadCreatures(guards, rng)) { dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(stack.type->idNumber, stack.count)); } @@ -237,3 +212,175 @@ bool CDwellingInstanceConstructor::producesCreature(const CCreature * crea) cons } return false; } + +CBankInstanceConstructor::CBankInstanceConstructor() +{ +} + +void CBankInstanceConstructor::initTypeData(const JsonNode & input) +{ + //TODO: name = input["name"].String(); + levels = input["levels"].Vector(); + bankResetDuration = input["resetDuration"].Float(); +} + +CGObjectInstance *CBankInstanceConstructor::create(ObjectTemplate tmpl) const +{ + return createTyped(tmpl); +} + +BankConfig CBankInstanceConstructor::generateConfig(const JsonNode & level, CRandomGenerator & rng) const +{ + BankConfig bc; + + bc.chance = level["chance"].Float(); + + bc.guards = JsonRandom::loadCreatures(level["guards"], rng); + bc.upgradeChance = level["upgrade_chance"].Float(); + bc.combatValue = level["combat_value"].Float(); + + std::vector spells; + for (size_t i=0; i<6; i++) + IObjectInterface::cb->getAllowedSpells(spells, i); + + bc.resources = Res::ResourceSet(level["reward"]["resources"]); + bc.creatures = JsonRandom::loadCreatures(level["reward"]["creatures"], rng); + bc.artifacts = JsonRandom::loadArtifacts(level["reward"]["artifacts"], rng); + bc.spells = JsonRandom::loadSpells(level["reward"]["spells"], rng, spells); + + bc.value = level["value"].Float(); + + return bc; +} + +void CBankInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const +{ + auto bank = dynamic_cast(object); + + bank->resetDuration = bankResetDuration; + + si32 totalChance = 0; + for (auto & node : levels) + totalChance += node["chance"].Float(); + + assert(totalChance != 0); + + si32 selectedChance = rng.nextInt(totalChance - 1); + + for (auto & node : levels) + { + if (selectedChance < node["chance"].Float()) + { + bank->setConfig(generateConfig(node, rng)); + } + else + { + selectedChance -= node["chance"].Float(); + } + + } +} + +CBankInfo::CBankInfo(JsonVector config): + config(config) +{ +} + +static void addStackToArmy(IObjectInfo::CArmyStructure & army, const CCreature * crea, si32 amount) +{ + army.totalStrength += crea->fightValue * amount; + + bool walker = true; + if (crea->hasBonusOfType(Bonus::SHOOTER)) + { + army.shootersStrength += crea->fightValue * amount; + walker = false; + } + if (crea->hasBonusOfType(Bonus::FLYING)) + { + army.flyersStrength += crea->fightValue * amount; + walker = false; + } + if (walker) + army.walkersStrength += crea->fightValue * amount; +} + +IObjectInfo::CArmyStructure CBankInfo::minGuards() const +{ + std::vector armies; + for (auto configEntry : config) + { + auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]); + IObjectInfo::CArmyStructure army; + for (auto & stack : stacks) + { + assert(!stack.allowedCreatures.empty()); + auto weakest = boost::range::min_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b) + { + return a->fightValue < b->fightValue; + }); + addStackToArmy(army, *weakest, stack.minAmount); + } + armies.push_back(army); + } + return *boost::range::min_element(armies); +} + +IObjectInfo::CArmyStructure CBankInfo::maxGuards() const +{ + std::vector armies; + for (auto configEntry : config) + { + auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]); + IObjectInfo::CArmyStructure army; + for (auto & stack : stacks) + { + assert(!stack.allowedCreatures.empty()); + auto strongest = boost::range::max_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b) + { + return a->fightValue < b->fightValue; + }); + addStackToArmy(army, *strongest, stack.maxAmount); + } + armies.push_back(army); + } + return *boost::range::max_element(armies); +} + +bool CBankInfo::givesResources() const +{ + for (const JsonNode & node : config) + if (!node["reward"]["resources"].isNull()) + return true; + return false; +} + +bool CBankInfo::givesArtifacts() const +{ + for (const JsonNode & node : config) + if (!node["reward"]["artifacts"].isNull()) + return true; + return false; +} + +bool CBankInfo::givesCreatures() const +{ + for (const JsonNode & node : config) + if (!node["reward"]["creatures"].isNull()) + return true; + return false; +} + +bool CBankInfo::givesSpells() const +{ + for (const JsonNode & node : config) + if (!node["reward"]["spells"].isNull()) + return true; + return false; +} + + +std::unique_ptr CBankInstanceConstructor::getObjectInfo(ObjectTemplate tmpl) const +{ + return std::unique_ptr(new CBankInfo(levels)); +} diff --git a/lib/mapObjects/CommonConstructors.h b/lib/mapObjects/CommonConstructors.h index da3db805c..241673d12 100644 --- a/lib/mapObjects/CommonConstructors.h +++ b/lib/mapObjects/CommonConstructors.h @@ -19,6 +19,8 @@ class CGDwelling; //class CGArtifact; //class CGCreature; class CHeroClass; +class CBank; +class CStackBasicDescriptor; /// Class that is used for objects that do not have dedicated handler template @@ -45,7 +47,7 @@ public: { } - virtual const IObjectInfo * getObjectInfo(ObjectTemplate tmpl) const + virtual std::unique_ptr getObjectInfo(ObjectTemplate tmpl) const { return nullptr; } @@ -63,6 +65,7 @@ class CTownInstanceConstructor : public CDefaultObjectTypeHandler&>(*this); } }; + +struct BankConfig +{ + BankConfig() { chance = upgradeChance = combatValue = value = 0; }; + ui32 value; //overall value of given things + ui32 chance; //chance for this level being chosen + ui32 upgradeChance; //chance for creatures to be in upgraded versions + ui32 combatValue; //how hard are guards of this level + std::vector guards; //creature ID, amount + Res::ResourceSet resources; //resources given in case of victory + std::vector creatures; //creatures granted in case of victory (creature ID, amount) + std::vector artifacts; //artifacts given in case of victory + std::vector spells; // granted spell(s), for Pyramid + + template void serialize(Handler &h, const int version) + { + h & chance & upgradeChance & guards & combatValue & resources & creatures & artifacts & value & spells; + } +}; + +class CBankInfo : public IObjectInfo +{ + JsonVector config; +public: + CBankInfo(JsonVector config); + + CArmyStructure minGuards() const; + CArmyStructure maxGuards() const; + bool givesResources() const; + bool givesArtifacts() const; + bool givesCreatures() const; + bool givesSpells() const; +}; + +class CBankInstanceConstructor : public CDefaultObjectTypeHandler +{ + BankConfig generateConfig(const JsonNode & conf, CRandomGenerator & rng) const; + + JsonVector levels; +protected: + void initTypeData(const JsonNode & input); + +public: + // all banks of this type will be reset N days after clearing, + si32 bankResetDuration; + + CBankInstanceConstructor(); + + CGObjectInstance *create(ObjectTemplate tmpl) const; + void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const; + + std::unique_ptr getObjectInfo(ObjectTemplate tmpl) const; +}; diff --git a/lib/mapObjects/JsonRandom.cpp b/lib/mapObjects/JsonRandom.cpp new file mode 100644 index 000000000..ac001a589 --- /dev/null +++ b/lib/mapObjects/JsonRandom.cpp @@ -0,0 +1,225 @@ +/* + * + * CRewardableObject.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "JsonRandom.h" + +#include "../JsonNode.h" +#include "../CRandomGenerator.h" +#include "../StringConstants.h" +#include "../VCMI_Lib.h" +#include "../CModHandler.h" +#include "../CArtHandler.h" +#include "../CCreatureHandler.h" +#include "../CCreatureSet.h" +#include "../CSpellHandler.h" + +namespace JsonRandom +{ + si32 loadValue(const JsonNode & value, CRandomGenerator & rng, si32 defaultValue) + { + if (value.isNull()) + return defaultValue; + if (value.getType() == JsonNode::DATA_FLOAT) + return value.Float(); + if (!value["amount"].isNull()) + return value["amount"].Float(); + si32 min = value["min"].Float(); + si32 max = value["max"].Float(); + return rng.getIntRange(min, max)(); + } + + TResources loadResources(const JsonNode & value, CRandomGenerator & rng) + { + TResources ret; + for (size_t i=0; i loadPrimary(const JsonNode & value, CRandomGenerator & rng) + { + std::vector ret; + for (auto & name : PrimarySkill::names) + { + ret.push_back(loadValue(value[name], rng)); + } + return ret; + } + + std::map loadSecondary(const JsonNode & value, CRandomGenerator & rng) + { + std::map ret; + for (auto & pair : value.Struct()) + { + SecondarySkill id(VLC->modh->identifiers.getIdentifier(pair.second.meta, "skill", pair.first).get()); + ret[id] = loadValue(pair.second, rng); + } + return ret; + } + + ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng) + { + if (value.getType() == JsonNode::DATA_STRING) + return ArtifactID(VLC->modh->identifiers.getIdentifier("artifact", value).get()); + + std::set allowedClasses; + std::set allowedPositions; + ui32 minValue = 0; + ui32 maxValue = std::numeric_limits::max(); + + if (value["class"].getType() == JsonNode::DATA_STRING) + allowedClasses.insert(VLC->arth->stringToClass(value["class"].String())); + else + for (auto & entry : value["class"].Vector()) + allowedClasses.insert(VLC->arth->stringToClass(entry.String())); + + if (value["slot"].getType() == JsonNode::DATA_STRING) + allowedPositions.insert(VLC->arth->stringToSlot(value["class"].String())); + else + for (auto & entry : value["slot"].Vector()) + allowedPositions.insert(VLC->arth->stringToSlot(entry.String())); + + if (value["minValue"].isNull()) minValue = value["minValue"].Float(); + if (value["maxValue"].isNull()) maxValue = value["maxValue"].Float(); + + return VLC->arth->pickRandomArtifact(rng, [=](ArtifactID artID) -> bool + { + CArtifact * art = VLC->arth->artifacts[artID]; + + if (!vstd::iswithin(art->price, minValue, maxValue)) + return false; + + if (!allowedClasses.empty() && !allowedClasses.count(art->aClass)) + return false; + + if (!allowedPositions.empty()) + { + for (auto pos : art->possibleSlots[ArtBearer::HERO]) + { + if (allowedPositions.count(pos)) + return true; + } + return false; + } + return true; + }); + } + + std::vector loadArtifacts(const JsonNode & value, CRandomGenerator & rng) + { + std::vector ret; + for (const JsonNode & entry : value.Vector()) + { + ret.push_back(loadArtifact(entry, rng)); + } + return ret; + } + + SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, std::vector spells) + { + if (value.getType() == JsonNode::DATA_STRING) + return SpellID(VLC->modh->identifiers.getIdentifier("spell", value).get()); + if (value["type"].getType() == JsonNode::DATA_STRING) + return SpellID(VLC->modh->identifiers.getIdentifier("spell", value["type"]).get()); + + spells.erase(std::remove_if(spells.begin(), spells.end(), [=](SpellID spell) + { + return VLC->spellh->objects[spell]->level != si32(value["level"].Float()); + }), spells.end()); + + return SpellID(*RandomGeneratorUtil::nextItem(spells, rng)); + } + + std::vector loadSpells(const JsonNode & value, CRandomGenerator & rng, std::vector spells) + { + // possible extensions: (taken from spell json config) + // "type": "adventure",//"adventure", "combat", "ability" + // "school": {"air":true, "earth":true, "fire":true, "water":true}, + // "level": 1, + + std::vector ret; + for (const JsonNode & entry : value.Vector()) + { + ret.push_back(loadSpell(entry, rng, spells)); + } + return ret; + } + + CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng) + { + CStackBasicDescriptor stack; + stack.type = VLC->creh->creatures[VLC->modh->identifiers.getIdentifier("creature", value["type"]).get()]; + stack.count = loadValue(value, rng); + if (!value["upgradeChance"].isNull() && !stack.type->upgrades.empty()) + { + if (int(value["upgradeChance"].Float()) > rng.nextInt(99)) // select random upgrade + { + stack.type = VLC->creh->creatures[*RandomGeneratorUtil::nextItem(stack.type->upgrades, rng)]; + } + } + return stack; + } + + std::vector loadCreatures(const JsonNode & value, CRandomGenerator & rng) + { + std::vector ret; + for (const JsonNode & node : value.Vector()) + { + ret.push_back(loadCreature(node, rng)); + } + return ret; + } + + std::vector evaluateCreatures(const JsonNode & value) + { + std::vector ret; + for (const JsonNode & node : value.Vector()) + { + RandomStackInfo info; + + if (!node["amount"].isNull()) + info.minAmount = info.maxAmount = node["amount"].Float(); + else + { + info.minAmount = node["min"].Float(); + info.maxAmount = node["max"].Float(); + } + const CCreature * crea = VLC->creh->creatures[VLC->modh->identifiers.getIdentifier("creature", node["type"]).get()]; + info.allowedCreatures.push_back(crea); + if (!node["upgradeChance"].Float() > 0) + { + for (auto creaID : crea->upgrades) + info.allowedCreatures.push_back(VLC->creh->creatures[creaID]); + } + } + return ret; + } + + std::vector loadBonuses(const JsonNode & value) + { + std::vector ret; + for (const JsonNode & entry : value.Vector()) + { + Bonus * bonus = JsonUtils::parseBonus(entry); + ret.push_back(*bonus); + delete bonus; + } + return ret; + } + + std::vector loadComponents(const JsonNode & value) + { + //TODO + } +} diff --git a/lib/mapObjects/JsonRandom.h b/lib/mapObjects/JsonRandom.h new file mode 100644 index 000000000..6e44da040 --- /dev/null +++ b/lib/mapObjects/JsonRandom.h @@ -0,0 +1,50 @@ +#pragma once + +#include "../GameConstants.h" +#include "../ResourceSet.h" + +/* + * JsonRandom.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +class JsonNode; +typedef std::vector JsonVector; +class CRandomGenerator; + +class Bonus; +class Component; +class CStackBasicDescriptor; + +namespace JsonRandom +{ + struct RandomStackInfo + { + std::vector allowedCreatures; + si32 minAmount; + si32 maxAmount; + }; + + si32 loadValue(const JsonNode & value, CRandomGenerator & rng, si32 defaultValue = 0); + TResources loadResources(const JsonNode & value, CRandomGenerator & rng); + std::vector loadPrimary(const JsonNode & value, CRandomGenerator & rng); + std::map loadSecondary(const JsonNode & value, CRandomGenerator & rng); + + ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng); + std::vector loadArtifacts(const JsonNode & value, CRandomGenerator & rng); + + SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, std::vector spells); + std::vector loadSpells(const JsonNode & value, CRandomGenerator & rng, std::vector spells); + + CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng); + std::vector loadCreatures(const JsonNode & value, CRandomGenerator & rng); + std::vector evaluateCreatures(const JsonNode & value); + + std::vector loadBonuses(const JsonNode & value); + std::vector loadComponents(const JsonNode & value); +} diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 21ddf65ee..f064cc70a 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -1569,7 +1569,7 @@ void CMapLoaderH3M::readObjects() { if(objTempl.subid == 0) { - nobj = new CGPyramid(); + nobj = new CBank(); } else { diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index 6bc8ca0ba..dd7467108 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -69,7 +69,6 @@ void registerTypesMapObjects1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); - s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); } @@ -81,13 +80,13 @@ void registerTypesMapObjectTypes(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); #define REGISTER_GENERIC_HANDLER(TYPENAME) s.template registerType >() REGISTER_GENERIC_HANDLER(CGObjectInstance); REGISTER_GENERIC_HANDLER(CGMarket); - REGISTER_GENERIC_HANDLER(CBank); REGISTER_GENERIC_HANDLER(CCartographer); REGISTER_GENERIC_HANDLER(CGArtifact); REGISTER_GENERIC_HANDLER(CGBlackMarket); @@ -113,7 +112,6 @@ void registerTypesMapObjectTypes(Serializer &s) REGISTER_GENERIC_HANDLER(CGOnceVisitable); REGISTER_GENERIC_HANDLER(CGPandoraBox); REGISTER_GENERIC_HANDLER(CGPickable); - REGISTER_GENERIC_HANDLER(CGPyramid); REGISTER_GENERIC_HANDLER(CGQuestGuard); REGISTER_GENERIC_HANDLER(CGResource); REGISTER_GENERIC_HANDLER(CGScholar);