1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

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)
This commit is contained in:
Ivan Savenko 2014-06-22 13:39:40 +03:00
parent 0a71e89f58
commit ab475195ac
28 changed files with 1822 additions and 1371 deletions

View File

@ -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<const CBank *>(obj));
case Obj::PYRAMID:
{
if(obj->subID == 0)
return fh->estimateBankDanger (VLC->objh->bankObjToIndex(obj));
return fh->estimateBankDanger (dynamic_cast<const CBank *>(obj));
else
return 0;
}

View File

@ -3,6 +3,7 @@
#include <limits>
#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 <ConstTransitivePtr<BankConfig>> & configs = VLC->objh->banksInfo[ID];
auto info = VLC->objtypeh->getHandlerFor(bank->ID, bank->subID)->getObjectInfo(bank->appearance);
ui64 val = std::numeric_limits<ui64>::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<std::string>(i))->setMinimum(bankVal * 0.5f);
bankDanger->term("Bank" + boost::lexical_cast<std::string>(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<fl::SingletonTerm*>(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<fl::SingletonTerm*>(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)
{

View File

@ -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);

View File

@ -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
}
]
}
]
}

View File

@ -49,6 +49,7 @@
[
"config/objects/generic.json",
"config/objects/moddables.json",
"config/objects/creatureBanks.json",
"config/objects/dwellings.json",
"config/objects/rewardable.json"
],

View File

@ -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 } ]
}
}
]
}
}
}
}

View File

@ -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" }
]
}
}
},

View File

@ -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" },

View File

@ -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" },

View File

@ -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<std::string, ArtifactPosition> 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<std::string, CArtifact::EartClass> 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<bool(ArtifactID)> accepts)
{
auto getAllowedArts = [&](std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *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<bool(ArtifactID)> 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<ILimiter> limiter = shared_ptr<ILimiter>(), int additionalInfo = 0)
{
auto added = new Bonus(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,-1,subtype);

View File

@ -203,11 +203,17 @@ public:
boost::optional<std::vector<CArtifact*>&> 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<bool(ArtifactID)> accepts);
ArtifactID pickRandomArtifact(CRandomGenerator & rand, int flags, std::function<bool(ArtifactID)> accepts);
bool legalArtifact(ArtifactID id);
void getAllowedArts(std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, int flag);
void getAllowed(std::vector<ConstTransitivePtr<CArtifact> > &out, int flags);
//void getAllowedArts(std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, int flag);
//void getAllowed(std::vector<ConstTransitivePtr<CArtifact> > &out, int flags);
bool isBigArtifact (ArtifactID artID) const {return bigArtifacts.find(artID) != bigArtifacts.end();}
void initAllowedArtifactsList(const std::vector<bool> &allowed); //allowed[art_id] -> 0 if not allowed, 1 if allowed
static ArtifactID creatureToMachineID(CreatureID id);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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<Res::ERes>(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<Res::ERes>(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<SpellID> 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<SpellID> 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<SpellID> 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);
}
}

View File

@ -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<ui32> artifacts; //fixed and deterministic
std::unique_ptr<BankConfig> 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 <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CArmedInstance&>(*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 <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CBank&>(*this);
h & spell;
}
friend class CBankInstanceConstructor;
};

View File

@ -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);

View File

@ -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<IObjectInfo> getObjectInfo(ObjectTemplate tmpl) const = 0;
template <typename Handler> void serialize(Handler &h, const int version)
{

View File

@ -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 <CreatureID, ui32> > &storage)
{
std::pair<CreatureID, si32> 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)

View File

@ -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 <CreatureID, ui32> > 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 <CreatureID, ui32> > creatures; //creatures granted in case of victory (creature ID, amount)
std::vector<ui16> 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 <typename Handler> 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 <ui32, std::vector < ConstTransitivePtr<BankConfig> > > banksInfo; //[index][preset]
std::map <ui32, std::string> creBanksNames; //[crebank index] -> name of this creature bank
std::vector<ui32> resVals; //default values of resources in gold
CObjectHandler();
~CObjectHandler();
int bankObjToIndex (const CGObjectInstance * obj);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & banksInfo & creBanksNames & resVals;
h & resVals;
}
};

View File

@ -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<GameConstants::RESOURCE_QUANTITY; i++)
{
ret[i] = loadValue(value[GameConstants::RESOURCE_NAMES[i]], rng);
}
return ret;
}
std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng)
{
std::vector<si32> ret;
for (auto & name : PrimarySkill::names)
{
ret.push_back(loadValue(value[name], rng));
}
return ret;
}
std::map<SecondarySkill, si32> loadSecondary(const JsonNode & value, CRandomGenerator & rng)
{
std::map<SecondarySkill, si32> 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<ArtifactID> loadArtifacts(const JsonNode & value, CRandomGenerator & rng)
{
std::vector<ArtifactID> ret;
for (const JsonNode & entry : value.Vector())
{
ArtifactID art(VLC->modh->identifiers.getIdentifier("artifact", entry).get());
ret.push_back(art);
}
return ret;
}
std::vector<SpellID> loadSpells(const JsonNode & value, CRandomGenerator & rng)
{
std::vector<SpellID> ret;
for (const JsonNode & entry : value.Vector())
{
SpellID spell(VLC->modh->identifiers.getIdentifier("spell", entry).get());
ret.push_back(spell);
}
return ret;
}
std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng)
{
std::vector<CStackBasicDescriptor> 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<Bonus> loadBonuses(const JsonNode & value)
{
std::vector<Bonus> ret;
for (const JsonNode & entry : value.Vector())
{
Bonus * bonus = JsonUtils::parseBonus(entry);
ret.push_back(*bonus);
delete bonus;
}
return ret;
}
std::vector<Component> 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<SpellID> 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<CRewardableObject*>(object), rng);
}
const IObjectInfo * CRewardableConstructor::getObjectInfo(ObjectTemplate tmpl) const
std::unique_ptr<IObjectInfo> CRewardableConstructor::getObjectInfo(ObjectTemplate tmpl) const
{
return &objectInfo;
return std::unique_ptr<IObjectInfo>(new CRandomRewardObjectInfo(objectInfo));
}

View File

@ -54,5 +54,5 @@ public:
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
const IObjectInfo * getObjectInfo(ObjectTemplate tmpl) const override;
std::unique_ptr<IObjectInfo> getObjectInfo(ObjectTemplate tmpl) const override;
};

View File

@ -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<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng)
{
std::vector<CStackBasicDescriptor> 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<CGDwelling*>(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<SpellID> 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<CBank*>(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<IObjectInfo::CArmyStructure> 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<IObjectInfo::CArmyStructure> 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<IObjectInfo> CBankInstanceConstructor::getObjectInfo(ObjectTemplate tmpl) const
{
return std::unique_ptr<IObjectInfo>(new CBankInfo(levels));
}

View File

@ -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<class ObjectType>
@ -45,7 +47,7 @@ public:
{
}
virtual const IObjectInfo * getObjectInfo(ObjectTemplate tmpl) const
virtual std::unique_ptr<IObjectInfo> getObjectInfo(ObjectTemplate tmpl) const
{
return nullptr;
}
@ -63,6 +65,7 @@ class CTownInstanceConstructor : public CDefaultObjectTypeHandler<CGTownInstance
JsonNode filtersJson;
protected:
bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const;
void initTypeData(const JsonNode & input);
public:
CFaction * faction;
@ -70,7 +73,6 @@ public:
CTownInstanceConstructor();
CGObjectInstance * create(ObjectTemplate tmpl) const;
void initTypeData(const JsonNode & input);
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const;
void afterLoadFinalization();
@ -86,6 +88,7 @@ class CHeroInstanceConstructor : public CDefaultObjectTypeHandler<CGHeroInstance
JsonNode filtersJson;
protected:
bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const;
void initTypeData(const JsonNode & input);
public:
CHeroClass * heroClass;
@ -93,7 +96,6 @@ public:
CHeroInstanceConstructor();
CGObjectInstance * create(ObjectTemplate tmpl) const;
void initTypeData(const JsonNode & input);
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const;
void afterLoadFinalization();
@ -112,12 +114,12 @@ class CDwellingInstanceConstructor : public CDefaultObjectTypeHandler<CGDwelling
protected:
bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const;
void initTypeData(const JsonNode & input);
public:
CDwellingInstanceConstructor();
CGObjectInstance * create(ObjectTemplate tmpl) const;
void initTypeData(const JsonNode & input);
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const;
bool producesCreature(const CCreature * crea) const;
@ -128,3 +130,56 @@ public:
h & static_cast<CDefaultObjectTypeHandler<CGDwelling>&>(*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<CStackBasicDescriptor> guards; //creature ID, amount
Res::ResourceSet resources; //resources given in case of victory
std::vector<CStackBasicDescriptor> creatures; //creatures granted in case of victory (creature ID, amount)
std::vector<ArtifactID> artifacts; //artifacts given in case of victory
std::vector<SpellID> spells; // granted spell(s), for Pyramid
template <typename Handler> 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<CBank>
{
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<IObjectInfo> getObjectInfo(ObjectTemplate tmpl) const;
};

View File

@ -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<GameConstants::RESOURCE_QUANTITY; i++)
{
ret[i] = loadValue(value[GameConstants::RESOURCE_NAMES[i]], rng);
}
return ret;
}
std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng)
{
std::vector<si32> ret;
for (auto & name : PrimarySkill::names)
{
ret.push_back(loadValue(value[name], rng));
}
return ret;
}
std::map<SecondarySkill, si32> loadSecondary(const JsonNode & value, CRandomGenerator & rng)
{
std::map<SecondarySkill, si32> 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<CArtifact::EartClass> allowedClasses;
std::set<ArtifactPosition> allowedPositions;
ui32 minValue = 0;
ui32 maxValue = std::numeric_limits<ui32>::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<ArtifactID> loadArtifacts(const JsonNode & value, CRandomGenerator & rng)
{
std::vector<ArtifactID> ret;
for (const JsonNode & entry : value.Vector())
{
ret.push_back(loadArtifact(entry, rng));
}
return ret;
}
SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, std::vector<SpellID> 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<SpellID> loadSpells(const JsonNode & value, CRandomGenerator & rng, std::vector<SpellID> 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<SpellID> 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<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng)
{
std::vector<CStackBasicDescriptor> ret;
for (const JsonNode & node : value.Vector())
{
ret.push_back(loadCreature(node, rng));
}
return ret;
}
std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value)
{
std::vector<RandomStackInfo> 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<Bonus> loadBonuses(const JsonNode & value)
{
std::vector<Bonus> ret;
for (const JsonNode & entry : value.Vector())
{
Bonus * bonus = JsonUtils::parseBonus(entry);
ret.push_back(*bonus);
delete bonus;
}
return ret;
}
std::vector<Component> loadComponents(const JsonNode & value)
{
//TODO
}
}

View File

@ -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<JsonNode> JsonVector;
class CRandomGenerator;
class Bonus;
class Component;
class CStackBasicDescriptor;
namespace JsonRandom
{
struct RandomStackInfo
{
std::vector<const CCreature *> allowedCreatures;
si32 minAmount;
si32 maxAmount;
};
si32 loadValue(const JsonNode & value, CRandomGenerator & rng, si32 defaultValue = 0);
TResources loadResources(const JsonNode & value, CRandomGenerator & rng);
std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng);
std::map<SecondarySkill, si32> loadSecondary(const JsonNode & value, CRandomGenerator & rng);
ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng);
std::vector<ArtifactID> loadArtifacts(const JsonNode & value, CRandomGenerator & rng);
SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, std::vector<SpellID> spells);
std::vector<SpellID> loadSpells(const JsonNode & value, CRandomGenerator & rng, std::vector<SpellID> spells);
CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng);
std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng);
std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value);
std::vector<Bonus> loadBonuses(const JsonNode & value);
std::vector<Component> loadComponents(const JsonNode & value);
}

View File

@ -1569,7 +1569,7 @@ void CMapLoaderH3M::readObjects()
{
if(objTempl.subid == 0)
{
nobj = new CGPyramid();
nobj = new CBank();
}
else
{

View File

@ -69,7 +69,6 @@ void registerTypesMapObjects1(Serializer &s)
s.template registerType<CArmedInstance, CGResource>();
s.template registerType<CArmedInstance, CGMine>();
s.template registerType<CArmedInstance, CBank>();
s.template registerType<CBank, CGPyramid>();
s.template registerType<CArmedInstance, CGSeerHut>(); s.template registerType<IQuestObject, CGSeerHut>();
s.template registerType<CGSeerHut, CGQuestGuard>();
}
@ -81,13 +80,13 @@ void registerTypesMapObjectTypes(Serializer &s)
s.template registerType<AObjectTypeHandler, CHeroInstanceConstructor>();
s.template registerType<AObjectTypeHandler, CTownInstanceConstructor>();
s.template registerType<AObjectTypeHandler, CDwellingInstanceConstructor>();
s.template registerType<AObjectTypeHandler, CBankInstanceConstructor>();
s.template registerType<AObjectTypeHandler, CObstacleConstructor>();
#define REGISTER_GENERIC_HANDLER(TYPENAME) s.template registerType<AObjectTypeHandler, CDefaultObjectTypeHandler<TYPENAME> >()
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);