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

Merge branch 'develop' into SpellsRefactoring4

This commit is contained in:
AlexVinS 2014-12-24 00:15:27 +03:00
commit eebf65e88f
46 changed files with 887 additions and 689 deletions

View File

@ -321,13 +321,13 @@ BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
}
}
void CStupidAI::saveGame(COSer<CSaveFile> &h, const int version)
void CStupidAI::saveGame(COSer & h, const int version)
{
//TODO to be implemented with saving/loading during the battles
assert(0);
}
void CStupidAI::loadGame(CISer<CLoadFile> &h, const int version)
void CStupidAI::loadGame(CISer & h, const int version)
{
//TODO to be implemented with saving/loading during the battles
assert(0);

View File

@ -36,8 +36,8 @@ public:
BattleAction goTowards(const CStack * stack, BattleHex hex );
virtual void saveGame(COSer<CSaveFile> &h, const int version) override;
virtual void loadGame(CISer<CLoadFile> &h, const int version) override;
virtual void saveGame(COSer & h, const int version) override;
virtual void loadGame(CISer & h, const int version) override;
};

View File

@ -234,8 +234,11 @@ ui64 evaluateDanger(crint3 tile, const CGHeroInstance *visitor)
{
//TODO: don't downcast objects AI shouldn't know about!
auto armedObj = dynamic_cast<const CArmedInstance*>(dangerousObject);
if(armedObj)
objectDanger *= fh->getTacticalAdvantage(visitor, armedObj); //this line tends to go infinite for allied towns (?)
if (armedObj)
{
float tacticalAdvantage = fh->getTacticalAdvantage(visitor, armedObj);
objectDanger *= tacticalAdvantage; //this line tends to go infinite for allied towns (?)
}
}
if (dangerousObject->ID == Obj::SUBTERRANEAN_GATE)
{ //check guard on the other side of the gate

View File

@ -20,9 +20,11 @@
*/
#define MIN_AI_STRENGHT (0.5f) //lower when combat AI gets smarter
#define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 10 times weaker than us
#define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us
struct BankConfig;
class IObjectInfo;
class CBankInfo;
class Engine;
class InputVariable;
class CGTownInstance;
@ -35,6 +37,22 @@ FuzzyHelper *fh;
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
engineBase::engineBase()
{
engine.addRuleBlock(&rules);
}
void engineBase::configure()
{
engine.configure("Minimum", "Maximum", "Minimum", "AlgebraicSum", "Centroid");
logAi->infoStream() << engine.toString();
}
void engineBase::addRule(const std::string &txt)
{
rules.addRule(fl::Rule::parse(txt, &engine));
}
struct armyStructure
{
float walkers, shooters, flyers;
@ -72,45 +90,18 @@ armyStructure evaluateArmyStructure (const CArmedInstance * army)
as.shooters = shootersStrenght / totalStrenght;
as.flyers = flyersStrenght / totalStrenght;
as.maxSpeed = maxSpeed;
assert(as.walkers || as.flyers || as.shooters);
return as;
}
FuzzyHelper::FuzzyHelper()
{
initBank();
initTacticalAdvantage();
ta.configure();
initVisitTile();
engine.configure("AlgebraicProduct", "AlgebraicSum",
"AlgebraicProduct", "AlgebraicSum", "Centroid");
logAi->infoStream() << engine.toString();
vt.configure();
}
void FuzzyHelper::initBank()
{
try
{
//Trivial bank estimation
bankInput = new fl::InputVariable("BankInput");
bankDanger = new fl::OutputVariable("BankDanger");
bankInput->addTerm(new fl::Rectangle("SET", 0.5 - 5 * fl::fuzzylite::macheps(),
0.5 + 5 * fl::fuzzylite::macheps()));
engine.addInputVariable(bankInput);
engine.addOutputVariable(bankDanger);
engine.addRuleBlock(&bankBlock);
for (int i = 0; i < 4; ++i)
{
bankDanger->addTerm(new fl::Triangle("Bank" + boost::lexical_cast<std::string>(i), 0, 1));
bankBlock.addRule(fl::Rule::parse("if BankInput is SET then BankDanger is Bank" + boost::lexical_cast<std::string>(i), &engine));
}
}
catch (fl::Exception & fe)
{
logAi->errorStream() << "initBank : " << fe.getWhat();
}
}
void FuzzyHelper::initTacticalAdvantage()
{
@ -132,9 +123,10 @@ void FuzzyHelper::initTacticalAdvantage()
for (auto val : helper)
{
engine.addInputVariable(val);
ta.engine.addInputVariable(val);
val->addTerm(new fl::Ramp("FEW", 0.6, 0.0));
val->addTerm(new fl::Ramp("MANY", 0.4, 1));
val->setRange(0.0, 1.0);
}
ta.ourSpeed = new fl::InputVariable("OurSpeed");
@ -144,57 +136,66 @@ void FuzzyHelper::initTacticalAdvantage()
for (auto val : helper)
{
engine.addInputVariable(val);
ta.engine.addInputVariable(val);
val->addTerm(new fl::Ramp("LOW", 6.5, 3));
val->addTerm(new fl::Triangle("MEDIUM", 5.5, 10.5));
val->addTerm(new fl::Ramp("HIGH", 8.5, 16));
val->setRange(0, 25);
}
ta.castleWalls = new fl::InputVariable("CastleWalls");
engine.addInputVariable(ta.castleWalls);
ta.castleWalls->addTerm(new fl::Rectangle("NONE", CGTownInstance::NONE - 5.0 * fl::fuzzylite::macheps(),
CGTownInstance::NONE + 5.0 * fl::fuzzylite::macheps()));
ta.engine.addInputVariable(ta.castleWalls);
{
fl::scalar a = CGTownInstance::FORT, d = 2.5;
fl::scalar b = a + (d - a) * 1.0 / 5.0;
fl::scalar c = a + (d - a) * 4.0 / 5.0;
ta.castleWalls->addTerm(new fl::Trapezoid("MEDIUM", a, b, c, d));
fl::Rectangle* none = new fl::Rectangle("NONE", CGTownInstance::NONE, CGTownInstance::NONE + (CGTownInstance::FORT - CGTownInstance::NONE) * 0.5f);
ta.castleWalls->addTerm(none);
fl::Trapezoid* medium = new fl::Trapezoid("MEDIUM", (CGTownInstance::FORT - CGTownInstance::NONE) * 0.5f, CGTownInstance::FORT,
CGTownInstance::CITADEL, CGTownInstance::CITADEL + (CGTownInstance::CASTLE - CGTownInstance::CITADEL) * 0.5f);
ta.castleWalls->addTerm(medium);
fl::Ramp* high = new fl::Ramp("HIGH", CGTownInstance::CITADEL - 0.1, CGTownInstance::CASTLE);
ta.castleWalls->addTerm(high);
ta.castleWalls->setRange(CGTownInstance::NONE, CGTownInstance::CASTLE);
}
ta.castleWalls->addTerm(new fl::Ramp("HIGH", CGTownInstance::CITADEL - 0.1, CGTownInstance::CASTLE));
ta.bankPresent = new fl::InputVariable("Bank");
engine.addInputVariable(ta.bankPresent);
ta.bankPresent->addTerm(new fl::Rectangle("FALSE", 0.0 - 5.0 * fl::fuzzylite::macheps(),
0.0 + 5.0 * fl::fuzzylite::macheps()));
ta.bankPresent->addTerm(new fl::Rectangle("TRUE", 1.0 - 5.0 * fl::fuzzylite::macheps(),
0.0 + 5.0 * fl::fuzzylite::macheps()));
ta.engine.addInputVariable(ta.bankPresent);
{
fl::Rectangle* termFalse = new fl::Rectangle("FALSE", 0.0, 0.5f);
ta.bankPresent->addTerm(termFalse);
fl::Rectangle* termTrue = new fl::Rectangle("TRUE", 0.5f, 1);
ta.bankPresent->addTerm(termTrue);
ta.bankPresent->setRange(0, 1);
}
ta.threat = new fl::OutputVariable("Threat");
engine.addOutputVariable(ta.threat);
ta.engine.addOutputVariable(ta.threat);
ta.threat->addTerm(new fl::Ramp("LOW", 1, MIN_AI_STRENGHT));
ta.threat->addTerm(new fl::Triangle("MEDIUM", 0.8, 1.2));
ta.threat->addTerm(new fl::Ramp("HIGH", 1, 1.5));
ta.threat->setRange(MIN_AI_STRENGHT, 1.5);
engine.addRuleBlock(&ta.tacticalAdvantage);
ta.addRule("if OurShooters is MANY and EnemySpeed is LOW then Threat is LOW");
ta.addRule("if OurShooters is MANY and EnemyShooters is FEW then Threat is LOW");
ta.addRule("if OurSpeed is LOW and EnemyShooters is MANY then Threat is HIGH");
ta.addRule("if OurSpeed is HIGH and EnemyShooters is MANY then Threat is LOW");
ta.tacticalAdvantage.addRule(fl::Rule::parse("if OurShooters is MANY and EnemySpeed is LOW then Threat is LOW", &engine));
ta.tacticalAdvantage.addRule(fl::Rule::parse("if OurShooters is MANY and EnemyShooters is FEW then Threat is LOW", &engine));
ta.tacticalAdvantage.addRule(fl::Rule::parse("if OurSpeed is LOW and EnemyShooters is MANY then Threat is HIGH", &engine));
ta.tacticalAdvantage.addRule(fl::Rule::parse("if OurSpeed is HIGH and EnemyShooters is MANY then Threat is LOW", &engine));
ta.tacticalAdvantage.addRule(fl::Rule::parse("if OurWalkers is FEW and EnemyShooters is MANY then Threat is somewhat LOW", &engine));
ta.tacticalAdvantage.addRule(fl::Rule::parse("if OurShooters is MANY and EnemySpeed is HIGH then Threat is somewhat HIGH", &engine));
ta.addRule("if OurWalkers is FEW and EnemyShooters is MANY then Threat is somewhat LOW");
ta.addRule("if OurShooters is MANY and EnemySpeed is HIGH then Threat is somewhat HIGH");
//just to cover all cases
ta.tacticalAdvantage.addRule(fl::Rule::parse("if EnemySpeed is MEDIUM then Threat is MEDIUM", &engine));
ta.tacticalAdvantage.addRule(fl::Rule::parse("if EnemySpeed is LOW and OurShooters is FEW then Threat is MEDIUM", &engine));
ta.addRule("if OurShooters is FEW and EnemySpeed is HIGH then Threat is MEDIUM");
ta.addRule("if EnemySpeed is MEDIUM then Threat is MEDIUM");
ta.addRule("if EnemySpeed is LOW and OurShooters is FEW then Threat is MEDIUM");
ta.tacticalAdvantage.addRule(fl::Rule::parse("if Bank is TRUE and OurShooters is MANY then Threat is somewhat HIGH", &engine));
ta.tacticalAdvantage.addRule(fl::Rule::parse("if Bank is TRUE and EnemyShooters is MANY then Threat is LOW", &engine));
ta.addRule("if Bank is TRUE and OurShooters is MANY then Threat is somewhat HIGH");
ta.addRule("if Bank is TRUE and EnemyShooters is MANY then Threat is LOW");
ta.tacticalAdvantage.addRule(fl::Rule::parse("if CastleWalls is HIGH and OurWalkers is MANY then Threat is very HIGH", &engine));
ta.tacticalAdvantage.addRule(fl::Rule::parse("if CastleWalls is HIGH and OurFlyers is MANY and OurShooters is MANY then Threat is MEDIUM", &engine));
ta.tacticalAdvantage.addRule(fl::Rule::parse("if CastleWalls is MEDIUM and OurShooters is MANY and EnemyWalkers is MANY then Threat is LOW", &engine));
ta.addRule("if CastleWalls is HIGH and OurWalkers is MANY then Threat is very HIGH");
ta.addRule("if CastleWalls is HIGH and OurFlyers is MANY and OurShooters is MANY then Threat is MEDIUM");
ta.addRule("if CastleWalls is MEDIUM and OurShooters is MANY and EnemyWalkers is MANY then Threat is LOW");
}
catch (fl::Exception & pe)
@ -205,34 +206,20 @@ void FuzzyHelper::initTacticalAdvantage()
ui64 FuzzyHelper::estimateBankDanger (const CBank * bank)
{
auto info = VLC->objtypeh->getHandlerFor(bank->ID, bank->subID)->getObjectInfo(bank->appearance);
//this one is not fuzzy anymore, just calculate weighted average
ui64 val = std::numeric_limits<ui64>::max();
try
{
fl::Triangle* bank0 = dynamic_cast<fl::Triangle*> (bankDanger->getTerm("Bank0"));
fl::Triangle* bank1 = dynamic_cast<fl::Triangle*> (bankDanger->getTerm("Bank1"));
if (bank0 && bank1)
{
bank0->setVertexA(info->minGuards().totalStrength * 0.5f);
bank0->setVertexC(info->minGuards().totalStrength * 1.5f);
bank1->setVertexA(info->maxGuards().totalStrength * 0.5f);
bank1->setVertexC(info->maxGuards().totalStrength * 1.5f);
}
auto objectInfo = VLC->objtypeh->getHandlerFor(bank->ID, bank->subID)->getObjectInfo(bank->appearance);
//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->setInputValue(0.5);
//engine.process(BANK_DANGER);//TODO: Process Bank_Dange only
engine.process(); //TODO: Process Bank_Dange only
val = bankDanger->getOutputValue(); //some expected value of this bank
}
catch (fl::Exception & fe)
CBankInfo * bankInfo = dynamic_cast<CBankInfo *> (objectInfo.get());
ui64 totalStrength = 0;
ui8 totalChance = 0;
for (auto config : bankInfo->getPossibleGuards())
{
logAi->errorStream() << "estimateBankDanger " << ": " << fe.getWhat();
totalStrength += config.second.totalStrength * config.first;
totalChance += config.first;
}
return val;
return totalStrength / totalChance;
}
@ -269,13 +256,26 @@ float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedI
ta.castleWalls->setInputValue(0);
//engine.process(TACTICAL_ADVANTAGE);//TODO: Process only Tactical_Advantage
engine.process();
ta.engine.process();
output = ta.threat->getOutputValue();
}
catch (fl::Exception & fe)
{
logAi->errorStream() << "getTacticalAdvantage " << ": " << fe.getWhat();
}
logAi->traceStream() << "getTacticalAdvantage output: " << output;
if (output < 0 || (output != output))
{
fl::InputVariable* tab[] = {ta.bankPresent, ta.castleWalls, ta.ourWalkers, ta.ourShooters, ta.ourFlyers, ta.ourSpeed, ta.enemyWalkers, ta.enemyShooters, ta.enemyFlyers, ta.enemySpeed};
std::string names[] = {"bankPresent", "castleWalls", "ourWalkers", "ourShooters", "ourFlyers", "ourSpeed", "enemyWalkers", "enemyShooters", "enemyFlyers", "enemySpeed" };
std::stringstream log("Warning! Fuzzy engine doesn't cover this set of parameters: ");
for (int i = 0; i < boost::size(tab); i++)
log << names[i] << ": " << tab[i]->getInputValue() << " ";
logAi->errorStream() << log.str();
assert(false);
}
return output;
}
@ -343,63 +343,67 @@ void FuzzyHelper::initVisitTile()
{
try
{
vt.strengthRatio = new fl::InputVariable("strengthRatio"); //hero must be strong enough to defeat guards
vt.heroStrength = new fl::InputVariable("heroStrength"); //we want to use weakest possible hero
vt.turnDistance = new fl::InputVariable("turnDistance"); //we want to use hero who is near
vt.missionImportance = new fl::InputVariable("lockedMissionImportance"); //we may want to preempt hero with low-priority mission
vt.value = new fl::OutputVariable("Value");
vt.value->setMinimum(0);
vt.value->setMaximum(5);
std::vector<fl::InputVariable*> helper = {vt.strengthRatio, vt.heroStrength, vt.turnDistance, vt.missionImportance};
for (auto val : helper)
{
engine.addInputVariable(val);
vt.engine.addInputVariable(val);
}
engine.addOutputVariable(vt.value);
vt.engine.addOutputVariable(vt.value);
vt.strengthRatio->addTerm(new fl::Ramp("LOW", SAFE_ATTACK_CONSTANT, 0));
vt.strengthRatio->addTerm(new fl::Ramp("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3));
vt.strengthRatio->setRange(0, SAFE_ATTACK_CONSTANT * 3 );
//strength compared to our main hero
vt.heroStrength->addTerm(new fl::Ramp("LOW", 0.2, 0));
vt.heroStrength->addTerm(new fl::Triangle("MEDIUM", 0.2, 0.8));
vt.heroStrength->addTerm(new fl::Ramp("HIGH", 0.5, 1));
vt.heroStrength->setRange(0.0, 1.0);
vt.turnDistance->addTerm(new fl::Ramp("SMALL", 0.5, 0));
vt.turnDistance->addTerm(new fl::Triangle("MEDIUM", 0.1, 0.8));
vt.turnDistance->addTerm(new fl::Ramp("LONG", 0.5, 3));
vt.turnDistance->setRange(0.0, 3.0);
vt.missionImportance->addTerm(new fl::Ramp("LOW", 2.5, 0));
vt.missionImportance->addTerm(new fl::Triangle("MEDIUM", 2, 3));
vt.missionImportance->addTerm(new fl::Ramp("HIGH", 2.5, 5));
vt.missionImportance->setRange(0.0, 5.0);
//an issue: in 99% cases this outputs center of mass (2.5) regardless of actual input :/
//should be same as "mission Importance" to keep consistency
vt.value->addTerm(new fl::Ramp("LOW", 2.5, 0));
vt.value->addTerm(new fl::Triangle("MEDIUM", 2, 3)); //can't be center of mass :/
vt.value->addTerm(new fl::Ramp("HIGH", 2.5, 5));
engine.addRuleBlock (&vt.rules);
vt.value->setRange(0.0,5.0);
//use unarmed scouts if possible
vt.rules.addRule(fl::Rule::parse("if strengthRatio is HIGH and heroStrength is LOW then Value is very HIGH", &engine));
vt.addRule("if strengthRatio is HIGH and heroStrength is LOW then Value is very HIGH");
//we may want to use secondary hero(es) rather than main hero
vt.rules.addRule(fl::Rule::parse("if strengthRatio is HIGH and heroStrength is MEDIUM then Value is somewhat HIGH", &engine));
vt.rules.addRule(fl::Rule::parse("if strengthRatio is HIGH and heroStrength is HIGH then Value is somewhat LOW", &engine));
vt.addRule("if strengthRatio is HIGH and heroStrength is MEDIUM then Value is somewhat HIGH");
vt.addRule("if strengthRatio is HIGH and heroStrength is HIGH then Value is somewhat LOW");
//don't assign targets to heroes who are too weak, but prefer targets of our main hero (in case we need to gather army)
vt.rules.addRule(fl::Rule::parse("if strengthRatio is LOW and heroStrength is LOW then Value is very LOW", &engine));
vt.addRule("if strengthRatio is LOW and heroStrength is LOW then Value is very LOW");
//attempt to arm secondary heroes is not stupid
vt.rules.addRule(fl::Rule::parse("if strengthRatio is LOW and heroStrength is MEDIUM then Value is somewhat HIGH", &engine));
vt.rules.addRule(fl::Rule::parse("if strengthRatio is LOW and heroStrength is HIGH then Value is LOW", &engine));
vt.addRule("if strengthRatio is LOW and heroStrength is MEDIUM then Value is somewhat HIGH");
vt.addRule("if strengthRatio is LOW and heroStrength is HIGH then Value is LOW");
//do not cancel important goals
vt.rules.addRule(fl::Rule::parse("if lockedMissionImportance is HIGH then Value is very LOW", &engine));
vt.rules.addRule(fl::Rule::parse("if lockedMissionImportance is MEDIUM then Value is somewhat LOW", &engine));
vt.rules.addRule(fl::Rule::parse("if lockedMissionImportance is LOW then Value is HIGH", &engine));
vt.addRule("if lockedMissionImportance is HIGH then Value is very LOW");
vt.addRule("if lockedMissionImportance is MEDIUM then Value is somewhat LOW");
vt.addRule("if lockedMissionImportance is LOW then Value is HIGH");
//pick nearby objects if it's easy, avoid long walks
vt.rules.addRule(fl::Rule::parse("if turnDistance is SMALL then Value is HIGH", &engine));
vt.rules.addRule(fl::Rule::parse("if turnDistance is MEDIUM then Value is MEDIUM", &engine));
vt.rules.addRule(fl::Rule::parse("if turnDistance is LONG then Value is LOW", &engine));
vt.addRule("if turnDistance is SMALL then Value is HIGH");
vt.addRule("if turnDistance is MEDIUM then Value is MEDIUM");
vt.addRule("if turnDistance is LONG then Value is LOW");
}
catch (fl::Exception & fe)
{
@ -442,7 +446,7 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g)
vt.turnDistance->setInputValue(turns);
vt.missionImportance->setInputValue(missionImportance);
engine.process();
vt.engine.process();
//engine.process(VISIT_TILE); //TODO: Process only Visit_Tile
g.priority = vt.value->getOutputValue();
}
@ -450,6 +454,7 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g)
{
logAi->errorStream() << "evaluate VisitTile " << ": " << fe.getWhat();
}
assert (g.priority >= 0);
return g.priority;
}
@ -493,6 +498,7 @@ float FuzzyHelper::evaluate (Goals::ClearWayTo & g)
}
else
return -1;
}
float FuzzyHelper::evaluate (Goals::BuildThis & g)

View File

@ -16,17 +16,22 @@ class VCAI;
class CArmedInstance;
class CBank;
class engineBase
{
public:
fl::Engine engine;
fl::RuleBlock rules;
engineBase();
void configure();
void addRule(const std::string &txt);
};
class FuzzyHelper
{
friend class VCAI;
fl::Engine engine;
fl::InputVariable* bankInput;
fl::OutputVariable* bankDanger;
fl::RuleBlock bankBlock;
class TacticalAdvantage
class TacticalAdvantage : public engineBase
{
public:
fl::InputVariable * ourWalkers, * ourShooters, * ourFlyers, * enemyWalkers, * enemyShooters, * enemyFlyers;
@ -34,11 +39,10 @@ class FuzzyHelper
fl::InputVariable * bankPresent;
fl::InputVariable * castleWalls;
fl::OutputVariable * threat;
fl::RuleBlock tacticalAdvantage;
~TacticalAdvantage();
} ta;
class EvalVisitTile
class EvalVisitTile : public engineBase
{
public:
fl::InputVariable * strengthRatio;
@ -55,7 +59,6 @@ public:
//blocks should be initialized in this order, which may be confusing :/
FuzzyHelper();
void initBank();
void initTacticalAdvantage();
void initVisitTile();

View File

@ -369,6 +369,9 @@ TSubgoal GetObj::whatToDoToAchieve()
const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
if(!obj)
return sptr (Goals::Explore());
if (obj->tempOwner == ai->playerID) //we can't capture our own object -> move to Win codition
throw cannotFulfillGoalException("Cannot capture my own object " + obj->getObjectName());
int3 pos = obj->visitablePos();
if (hero)
{
@ -377,8 +380,11 @@ TSubgoal GetObj::whatToDoToAchieve()
}
else
{
if (ai->isAccessible(obj->pos))
return sptr (Goals::VisitTile(pos).sethero(hero)); //we must visit object with same hero, if any
for (auto h : cb->getHeroesInfo())
{
if (ai->isAccessibleForHero(pos, h))
return sptr(Goals::VisitTile(pos).sethero(h)); //we must visit object with same hero, if any
}
}
return sptr (Goals::ClearWayTo(pos).sethero(hero));
}
@ -709,7 +715,12 @@ TGoalVec VisitTile::getAllPossibleSubgoals()
{
auto obj = frontOrNull(cb->getVisitableObjs(tile));
if (obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile
ret.push_back (sptr(Goals::VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true)));
{
if (hero.get(true) && hero->id == obj->id) //if it's assigned hero, visit tile. If it's different hero, we can't visit tile now
ret.push_back(sptr(Goals::VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true)));
else
throw cannotFulfillGoalException("Tile is already occupied by another hero "); //FIXME: we should give up this tile earlier
}
else
ret.push_back (sptr(Goals::ClearWayTo(tile)));
}

View File

@ -598,7 +598,7 @@ void VCAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *do
});
}
void VCAI::saveGame(COSer<CSaveFile> &h, const int version)
void VCAI::saveGame(COSer & h, const int version)
{
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
NET_EVENT_HANDLER;
@ -609,7 +609,7 @@ void VCAI::saveGame(COSer<CSaveFile> &h, const int version)
serializeInternal(h, version);
}
void VCAI::loadGame(CISer<CLoadFile> &h, const int version)
void VCAI::loadGame(CISer & h, const int version)
{
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
NET_EVENT_HANDLER;

View File

@ -190,8 +190,8 @@ public:
virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; //TODO
virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
virtual void saveGame(COSer<CSaveFile> &h, const int version) override; //saving
virtual void loadGame(CISer<CLoadFile> &h, const int version) override; //loading
virtual void saveGame(COSer & h, const int version) override; //saving
virtual void loadGame(CISer & h, const int version) override; //loading
virtual void finish() override;
virtual void availableCreaturesChanged(const CGDwelling *town) override;

View File

@ -1278,13 +1278,13 @@ template <typename Handler> void CPlayerInterface::serializeTempl( Handler &h, c
h & spellbookSettings;
}
void CPlayerInterface::saveGame( COSer<CSaveFile> &h, const int version )
void CPlayerInterface::saveGame( COSer & h, const int version )
{
EVENT_HANDLER_CALLED_BY_CLIENT;
serializeTempl(h,version);
}
void CPlayerInterface::loadGame( CISer<CLoadFile> &h, const int version )
void CPlayerInterface::loadGame( CISer & h, const int version )
{
EVENT_HANDLER_CALLED_BY_CLIENT;
serializeTempl(h,version);

View File

@ -193,8 +193,8 @@ public:
void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
void playerStartsTurn(PlayerColor player) override; //called before yourTurn on active itnerface
void showComp(const Component &comp, std::string message) override; //display component in the advmapint infobox
void saveGame(COSer<CSaveFile> &h, const int version) override; //saving
void loadGame(CISer<CLoadFile> &h, const int version) override; //loading
void saveGame(COSer & h, const int version) override; //saving
void loadGame(CISer & h, const int version) override; //loading
//for battles
void actionFinished(const BattleAction& action) override;//occurs AFTER action taken by active stack or by the hero

View File

@ -2171,6 +2171,10 @@ void InfoCard::changeSelection( const CMapInfo *to )
else
mapDescription->setText(to->mapHeader->description);
mapDescription->label->scrollTextTo(0);
if (mapDescription->slider)
mapDescription->slider->moveToMin();
if(SEL->screenType != CMenuScreen::newGame && SEL->screenType != CMenuScreen::campaignList) {
//difficulty->block(true);
difficulty->setSelected(SEL->sInfo.difficulty);

View File

@ -331,7 +331,7 @@ void CClient::loadGame(const std::string & fname, const bool server, const std::
std::cout << x << std::endl;
std::cout << "ENDCLIENTPLAYERS\n";
serialize(*loader,0,clientPlayers);
serialize(loader->serializer,0,clientPlayers);
*serv << ui32(clientPlayers.size());
for(auto & elem : clientPlayers)
*serv << ui8(elem.getNum());
@ -493,11 +493,10 @@ void CClient::newGame( CConnection *con, StartInfo *si )
// }
}
template <typename Handler>
void CClient::serialize( Handler &h, const int version )
void CClient::serialize(COSer & h, const int version)
{
h & hotSeat;
if(h.saving)
assert(h.saving);
h & hotSeat;
{
ui8 players = playerint.size();
h & players;
@ -507,11 +506,15 @@ void CClient::serialize( Handler &h, const int version )
LOG_TRACE_PARAMS(logGlobal, "Saving player %s interface", i->first);
assert(i->first == i->second->playerID);
h & i->first & i->second->dllName & i->second->human;
i->second->saveGame(dynamic_cast<COSer<CSaveFile>&>(h), version);
//evil cast that i still like better than sfinae-magic. If I had a "static if"...
i->second->saveGame(h, version);
}
}
else
}
void CClient::serialize(CISer & h, const int version)
{
assert(!h.saving);
h & hotSeat;
{
ui8 players = 0; //fix for uninitialized warning
h & players;
@ -551,7 +554,7 @@ void CClient::serialize( Handler &h, const int version )
nInt->playerID = pid;
installNewPlayerInterface(nInt, pid);
nInt->loadGame(dynamic_cast<CISer<CLoadFile>&>(h), version); //another evil cast, check above
nInt->loadGame(h, version); //another evil cast, check above
}
if(!vstd::contains(battleints, PlayerColor::NEUTRAL))
@ -559,11 +562,10 @@ void CClient::serialize( Handler &h, const int version )
}
}
template <typename Handler>
void CClient::serialize( Handler &h, const int version, const std::set<PlayerColor>& playerIDs)
void CClient::serialize(COSer & h, const int version, const std::set<PlayerColor> & playerIDs)
{
assert(h.saving);
h & hotSeat;
if(h.saving)
{
ui8 players = playerint.size();
h & players;
@ -573,11 +575,15 @@ void CClient::serialize( Handler &h, const int version, const std::set<PlayerCol
LOG_TRACE_PARAMS(logGlobal, "Saving player %s interface", i->first);
assert(i->first == i->second->playerID);
h & i->first & i->second->dllName & i->second->human;
i->second->saveGame(dynamic_cast<COSer<CSaveFile>&>(h), version);
//evil cast that i still like better than sfinae-magic. If I had a "static if"...
i->second->saveGame(h, version);
}
}
else
}
void CClient::serialize(CISer & h, const int version, const std::set<PlayerColor> & playerIDs)
{
assert(!h.saving);
h & hotSeat;
{
ui8 players = 0; //fix for uninitialized warning
h & players;
@ -620,7 +626,7 @@ void CClient::serialize( Handler &h, const int version, const std::set<PlayerCol
if(playerIDs.count(pid))
installNewPlayerInterface(nInt, pid);
nInt->loadGame(dynamic_cast<CISer<CLoadFile>&>(h), version); //another evil cast, check above
nInt->loadGame(h, version);
}
if(playerIDs.count(PlayerColor::NEUTRAL))
@ -901,8 +907,6 @@ std::string CClient::aiNameForPlayer(const PlayerSettings &ps, bool battleAI)
return goodAI;
}
template void CClient::serialize( CISer<CLoadFile> &h, const int version );
template void CClient::serialize( COSer<CSaveFile> &h, const int version );
void CServerHandler::startServer()
{

View File

@ -32,6 +32,8 @@ struct SharedMem;
class CClient;
class CScriptingModule;
struct CPathsInfo;
class CISer;
class COSer;
namespace boost { class thread; }
/// structure to handle running server and connecting to it
@ -236,7 +238,10 @@ public:
//////////////////////////////////////////////////////////////////////////
template <typename Handler> void serialize(Handler &h, const int version);
template <typename Handler> void serialize(Handler &h, const int version, const std::set<PlayerColor>& playerIDs);
void serialize(COSer &h, const int version);
void serialize(CISer &h, const int version);
void serialize(COSer &h, const int version, const std::set<PlayerColor>& playerIDs);
void serialize(CISer &h, const int version, const std::set<PlayerColor>& playerIDs);
void battleFinished();
};

View File

@ -153,6 +153,7 @@
<AdditionalLibraryDirectories>$(FFMPEGDIR)\lib;.;..\..\libs;..</AdditionalLibraryDirectories>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalOptions>/LTCG %(AdditionalOptions)</AdditionalOptions>
</Link>
<CustomBuildStep>
<Command>

View File

@ -479,7 +479,7 @@ void CMinimapInstance::showAll(SDL_Surface *to)
blitAtLoc(minimap, 0, 0, to);
//draw heroes
std::vector <const CGHeroInstance *> heroes = LOCPLINT->cb->getHeroesInfo(false);
std::vector <const CGHeroInstance *> heroes = LOCPLINT->cb->getHeroesInfo(false); //TODO: do we really need separate function for drawing heroes?
for(auto & hero : heroes)
{
int3 position = hero->getPosition(false);

View File

@ -744,6 +744,11 @@ void CSlider::keyPressed(const SDL_KeyboardEvent & key)
moveTo(moveDest);
}
void CSlider::moveToMin()
{
moveTo(0);
}
void CSlider::moveToMax()
{
moveTo(amount);

View File

@ -230,6 +230,7 @@ public:
void moveRight();
void moveTo(int value);
void moveBy(int amount);
void moveToMin();
void moveToMax();
/// Amount modifier

View File

@ -280,15 +280,20 @@ void CTextBox::resize(Point newSize)
void CTextBox::setText(const std::string &text)
{
label->pos.w = pos.w; // reset to default before textSize.y check
label->setText(text);
if (label->textSize.y <= label->pos.h && slider)
if(label->textSize.y <= label->pos.h && slider)
{
// slider is no longer needed
vstd::clear_pointer(slider);
label->pos.w = pos.w;
}
else if(slider)
{
// decrease width again if slider still used
label->pos.w = pos.w - 32;
label->setText(text);
}
else if (label->textSize.y > label->pos.h && !slider)
else if(label->textSize.y > label->pos.h)
{
// create slider and update widget
label->pos.w = pos.w - 32;

View File

@ -109,7 +109,7 @@ const CGObjectInstance* CGameInfoCallback::getObj(ObjectInstanceID objid, bool v
return nullptr;
}
if(!isVisible(ret, player))
if(!isVisible(ret, player) && ret->tempOwner != player)
{
if(verbose)
logGlobal->errorStream() << "Cannot get object with id " << oid << ". Object is not visible.";
@ -518,7 +518,8 @@ std::vector < const CGHeroInstance *> CPlayerSpecificInfoCallback::getHeroesInfo
std::vector < const CGHeroInstance *> ret;
for(auto hero : gs->map->heroesOnMap)
{
if( !player || (hero->tempOwner == *player) ||
// !player || // - why would we even get access to hero not owned by any player?
if((hero->tempOwner == *player) ||
(isVisible(hero->getPosition(false), player) && !onlyOur) )
{
ret.push_back(hero);

View File

@ -243,7 +243,7 @@ void CAdventureAI::yourTacticPhase(int distance)
battleAI->yourTacticPhase(distance);
}
void CAdventureAI::saveGame(COSer<CSaveFile> &h, const int version) /*saving */
void CAdventureAI::saveGame(COSer & h, const int version) /*saving */
{
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
CGlobalAI::saveGame(h, version);
@ -256,7 +256,7 @@ void CAdventureAI::saveGame(COSer<CSaveFile> &h, const int version) /*saving */
}
}
void CAdventureAI::loadGame(CISer<CLoadFile> &h, const int version) /*loading */
void CAdventureAI::loadGame(CISer & h, const int version) /*loading */
{
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
CGlobalAI::loadGame(h, version);
@ -273,10 +273,10 @@ void CAdventureAI::loadGame(CISer<CLoadFile> &h, const int version) /*loading */
}
}
void CBattleGameInterface::saveGame(COSer<CSaveFile> &h, const int version)
void CBattleGameInterface::saveGame(COSer & h, const int version)
{
}
void CBattleGameInterface::loadGame(CISer<CLoadFile> &h, const int version)
void CBattleGameInterface::loadGame(CISer & h, const int version)
{
}

View File

@ -50,8 +50,8 @@ class CPathsInfo;
class CCreature;
class CLoadFile;
class CSaveFile;
template <typename Serializer> class CISer;
template <typename Serializer> class COSer;
class CISer;
class COSer;
struct ArtifactLocation;
class CScriptingModule;
@ -69,8 +69,8 @@ public:
virtual BattleAction activeStack(const CStack * stack)=0; //called when it's turn of that stack
virtual void yourTacticPhase(int distance){}; //called when interface has opportunity to use Tactics skill -> use cb->battleMakeTacticAction from this function
virtual void saveGame(COSer<CSaveFile> &h, const int version);
virtual void loadGame(CISer<CLoadFile> &h, const int version);
virtual void saveGame(COSer &h, const int version);
virtual void loadGame(CISer &h, const int version);
};
@ -142,6 +142,6 @@ public:
virtual void battleEnd(const BattleResult *br);
virtual void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom);
virtual void saveGame(COSer<CSaveFile> &h, const int version); //saving
virtual void loadGame(CISer<CLoadFile> &h, const int version); //loading
virtual void saveGame(COSer & h, const int version); //saving
virtual void loadGame(CISer & h, const int version); //loading
};

View File

@ -878,7 +878,7 @@ void CGameState::initDuel()
else
{
CLoadFile lf(scenarioOps->mapname);
lf >> dp;
lf.serializer >> dp;
}
}
catch(...)
@ -2240,6 +2240,10 @@ bool CGameState::isVisible( const CGObjectInstance *obj, boost::optional<PlayerC
if(!player)
return true;
//we should always see our own heroes - but sometimes not visible heroes cause crash :?
if (player == obj->tempOwner)
return true;
if(*player == PlayerColor::NEUTRAL) //-> TODO ??? needed?
return false;
//object is visible when at least one blocked tile is visible

View File

@ -18,14 +18,9 @@
using namespace boost;
using namespace boost::asio::ip;
extern template void registerTypes<CISer<CConnection> >(CISer<CConnection>& s);
extern template void registerTypes<COSer<CConnection> >(COSer<CConnection>& s);
extern template void registerTypes<CISer<CMemorySerializer> >(CISer<CMemorySerializer>& s);
extern template void registerTypes<COSer<CMemorySerializer> >(COSer<CMemorySerializer>& s);
extern template void registerTypes<CSaveFile>(CSaveFile & s);
extern template void registerTypes<CLoadFile>(CLoadFile & s);
extern template void registerTypes<CISer>(CISer & s);
extern template void registerTypes<COSer>(COSer & s);
extern template void registerTypes<CTypeList>(CTypeList & s);
extern template void registerTypes<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);
CTypeList typeList;
@ -50,8 +45,8 @@ void CConnection::init()
enableSmartPointerSerializatoin();
disableStackSendingByID();
registerTypes(static_cast<CISer<CConnection>&>(*this));
registerTypes(static_cast<COSer<CConnection>&>(*this));
registerTypes(iser);
registerTypes(oser);
#ifdef LIL_ENDIAN
myEndianess = true;
#else
@ -60,8 +55,8 @@ void CConnection::init()
connected = true;
std::string pom;
//we got connection
(*this) << std::string("Aiya!\n") << name << myEndianess; //identify ourselves
(*this) >> pom >> pom >> contactEndianess;
oser << std::string("Aiya!\n") << name << myEndianess; //identify ourselves
iser >> pom >> pom >> contactEndianess;
logNetwork->infoStream() << "Established connection with "<<pom;
wmx = new boost::mutex;
rmx = new boost::mutex;
@ -73,7 +68,7 @@ void CConnection::init()
}
CConnection::CConnection(std::string host, std::string port, std::string Name)
:io_service(new asio::io_service), name(Name)
:iser(this), oser(this), io_service(new asio::io_service), name(Name)
{
int i;
boost::system::error_code error = asio::error::host_not_found;
@ -128,12 +123,12 @@ connerror1:
throw std::runtime_error("Can't establish connection :(");
}
CConnection::CConnection(TSocket * Socket, std::string Name )
:socket(Socket),io_service(&Socket->get_io_service()), name(Name)//, send(this), rec(this)
:iser(this), oser(this), socket(Socket),io_service(&Socket->get_io_service()), name(Name)//, send(this), rec(this)
{
init();
}
CConnection::CConnection(TAcceptor * acceptor, boost::asio::io_service *Io_service, std::string Name)
: name(Name)//, send(this), rec(this)
: iser(this), oser(this), name(Name)//, send(this), rec(this)
{
boost::system::error_code error = asio::error::host_not_found;
socket = new tcp::socket(*io_service);
@ -229,7 +224,7 @@ CPack * CConnection::retreivePack()
CPack *ret = nullptr;
boost::unique_lock<boost::mutex> lock(*rmx);
logNetwork->traceStream() << "Listening... ";
*this >> ret;
iser >> ret;
logNetwork->traceStream() << "\treceived server message of type " << typeid(*ret).name() << ", data: " << ret;
return ret;
}
@ -238,37 +233,33 @@ void CConnection::sendPackToServer(const CPack &pack, PlayerColor player, ui32 r
{
boost::unique_lock<boost::mutex> lock(*wmx);
logNetwork->traceStream() << "Sending to server a pack of type " << typeid(pack).name();
*this << player << requestID << &pack; //packs has to be sent as polymorphic pointers!
oser << player << requestID << &pack; //packs has to be sent as polymorphic pointers!
}
void CConnection::disableStackSendingByID()
{
CISer<CConnection>::sendStackInstanceByIds = false;
COSer<CConnection>::sendStackInstanceByIds = false;
CSerializer::sendStackInstanceByIds = false;
}
void CConnection::enableStackSendingByID()
{
CISer<CConnection>::sendStackInstanceByIds = true;
COSer<CConnection>::sendStackInstanceByIds = true;
CSerializer::sendStackInstanceByIds = true;
}
void CConnection::disableSmartPointerSerialization()
{
CISer<CConnection>::smartPointerSerialization = false;
COSer<CConnection>::smartPointerSerialization = false;
iser.smartPointerSerialization = oser.smartPointerSerialization = false;
}
void CConnection::enableSmartPointerSerializatoin()
{
CISer<CConnection>::smartPointerSerialization = true;
COSer<CConnection>::smartPointerSerialization = true;
iser.smartPointerSerialization = oser.smartPointerSerialization = true;
}
void CConnection::prepareForSendingHeroes()
{
loadedPointers.clear();
savedPointers.clear();
iser.loadedPointers.clear();
oser.savedPointers.clear();
disableSmartVectorMemberSerialization();
enableSmartPointerSerializatoin();
disableStackSendingByID();
@ -276,25 +267,25 @@ void CConnection::prepareForSendingHeroes()
void CConnection::enterPregameConnectionMode()
{
loadedPointers.clear();
savedPointers.clear();
iser.loadedPointers.clear();
oser.savedPointers.clear();
disableSmartVectorMemberSerialization();
disableSmartPointerSerialization();
}
void CConnection::disableSmartVectorMemberSerialization()
{
smartVectorMembersSerialization = false;
CSerializer::smartVectorMembersSerialization = false;
}
void CConnection::enableSmartVectorMemberSerializatoin()
{
smartVectorMembersSerialization = true;
CSerializer::smartVectorMembersSerialization = true;
}
CSaveFile::CSaveFile( const std::string &fname )
CSaveFile::CSaveFile( const std::string &fname ): serializer(this)
{
registerTypes(*this);
registerTypes(serializer);
openNextFile(fname);
}
@ -320,7 +311,7 @@ void CSaveFile::openNextFile(const std::string &fname)
THROW_FORMAT("Error: cannot open to write %s!", fname);
sfile->write("VCMI",4); //write magic identifier
*this << version; //write format version
serializer << version; //write format version
}
catch(...)
{
@ -350,9 +341,9 @@ void CSaveFile::putMagicBytes( const std::string &text )
write(text.c_str(), text.length());
}
CLoadFile::CLoadFile(const boost::filesystem::path & fname, int minimalVersion /*= version*/)
CLoadFile::CLoadFile(const boost::filesystem::path & fname, int minimalVersion /*= version*/): serializer(this)
{
registerTypes(*this);
registerTypes(serializer);
openNextFile(fname, minimalVersion);
}
@ -368,7 +359,7 @@ int CLoadFile::read(void * data, unsigned size)
void CLoadFile::openNextFile(const boost::filesystem::path & fname, int minimalVersion)
{
assert(!reverseEndianess);
assert(!serializer.reverseEndianess);
assert(minimalVersion <= version);
try
@ -386,22 +377,22 @@ void CLoadFile::openNextFile(const boost::filesystem::path & fname, int minimalV
if(std::memcmp(buffer,"VCMI",4))
THROW_FORMAT("Error: not a VCMI file(%s)!", fName);
*this >> fileVersion;
if(fileVersion < minimalVersion)
serializer >> serializer.fileVersion;
if(serializer.fileVersion < minimalVersion)
THROW_FORMAT("Error: too old file format (%s)!", fName);
if(fileVersion > version)
if(serializer.fileVersion > version)
{
logGlobal->warnStream() << boost::format("Warning format version mismatch: found %d when current is %d! (file %s)\n") % fileVersion % version % fName;
logGlobal->warnStream() << boost::format("Warning format version mismatch: found %d when current is %d! (file %s)\n") % serializer.fileVersion % version % fName;
auto versionptr = (char*)&fileVersion;
auto versionptr = (char*)&serializer.fileVersion;
std::reverse(versionptr, versionptr + 4);
logGlobal->warnStream() << "Version number reversed is " << fileVersion << ", checking...";
logGlobal->warnStream() << "Version number reversed is " << serializer.fileVersion << ", checking...";
if(fileVersion == version)
if(serializer.fileVersion == version)
{
logGlobal->warnStream() << fname << " seems to have different endianness! Entering reversing mode.";
reverseEndianess = true;
serializer.reverseEndianess = true;
}
else
THROW_FORMAT("Error: too new file format (%s)!", fName);
@ -427,7 +418,7 @@ void CLoadFile::clear()
{
sfile = nullptr;
fName.clear();
fileVersion = 0;
serializer.fileVersion = 0;
}
void CLoadFile::checkMagicBytes( const std::string &text )
@ -579,14 +570,14 @@ void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib)
}
CLoadIntegrityValidator::CLoadIntegrityValidator( const std::string &primaryFileName, const std::string &controlFileName, int minimalVersion /*= version*/ )
: foundDesync(false)
: serializer(this), foundDesync(false)
{
registerTypes(*this);
registerTypes(serializer);
primaryFile = make_unique<CLoadFile>(primaryFileName, minimalVersion);
controlFile = make_unique<CLoadFile>(controlFileName, minimalVersion);
assert(primaryFile->fileVersion == controlFile->fileVersion);
fileVersion = primaryFile->fileVersion;
assert(primaryFile->serializer.fileVersion == controlFile->serializer.fileVersion);
serializer.fileVersion = primaryFile->serializer.fileVersion;
}
int CLoadIntegrityValidator::read( void * data, unsigned size )
@ -615,8 +606,8 @@ int CLoadIntegrityValidator::read( void * data, unsigned size )
unique_ptr<CLoadFile> CLoadIntegrityValidator::decay()
{
primaryFile->loadedPointers = this->loadedPointers;
primaryFile->loadedPointersTypes = this->loadedPointersTypes;
primaryFile->serializer.loadedPointers = this->serializer.loadedPointers;
primaryFile->serializer.loadedPointersTypes = this->serializer.loadedPointersTypes;
return std::move(primaryFile);
}
@ -647,10 +638,10 @@ int CMemorySerializer::write(const void * data, unsigned size)
return size;
}
CMemorySerializer::CMemorySerializer()
CMemorySerializer::CMemorySerializer(): iser(this), oser(this)
{
readPos = 0;
registerTypes(static_cast<CISer<CMemorySerializer>&>(*this));
registerTypes(static_cast<COSer<CMemorySerializer>&>(*this));
registerTypes(iser);
registerTypes(oser);
}

View File

@ -30,6 +30,8 @@
const ui32 version = 751;
const ui32 minSupportedVersion = version;
class CISer;
class COSer;
class CConnection;
class CGObjectInstance;
class CStackInstance;
@ -266,140 +268,6 @@ public:
extern DLL_LINKAGE CTypeList typeList;
template<typename Ser>
struct SaveBoolean
{
static void invoke(Ser &s, const bool &data)
{
s.saveBoolean(data);
}
};
template<typename Ser>
struct LoadBoolean
{
static void invoke(Ser &s, bool &data)
{
s.loadBoolean(data);
}
};
template<typename Ser>
struct SaveBooleanVector
{
static void invoke(Ser &s, const std::vector<bool> &data)
{
s.saveBooleanVector(data);
}
};
template<typename Ser>
struct LoadBooleanVector
{
static void invoke(Ser &s, std::vector<bool> &data)
{
s.loadBooleanVector(data);
}
};
template<typename Ser,typename T>
struct SavePrimitive
{
static void invoke(Ser &s, const T &data)
{
s.savePrimitive(data);
}
};
template<typename Ser,typename T>
struct SaveSerializable
{
static void invoke(Ser &s, const T &data)
{
s.saveSerializable(data);
}
};
template<typename Ser,typename T>
struct SaveEnum
{
static void invoke(Ser &s, const T &data)
{
s.saveEnum(data);
}
};
template<typename Ser,typename T>
struct LoadEnum
{
static void invoke(Ser &s, T &data)
{
s.loadEnum(data);
}
};
template<typename Ser,typename T>
struct LoadPrimitive
{
static void invoke(Ser &s, T &data)
{
s.loadPrimitive(data);
}
};
template<typename Ser,typename T>
struct SavePointer
{
static void invoke(Ser &s, const T &data)
{
s.savePointer(data);
}
};
template<typename Ser,typename T>
struct LoadPointer
{
static void invoke(Ser &s, T &data)
{
s.loadPointer(data);
}
};
template<typename Ser,typename T>
struct SaveArray
{
static void invoke(Ser &s, const T &data)
{
s.saveArray(data);
}
};
template<typename Ser,typename T>
struct LoadArray
{
static void invoke(Ser &s, T &data)
{
s.loadArray(data);
}
};
template<typename Ser,typename T>
struct LoadSerializable
{
static void invoke(Ser &s, T &data)
{
s.loadSerializable(data);
}
};
template<typename Ser,typename T>
struct SaveWrong
{
static void invoke(Ser &s, const T &data)
{
throw std::runtime_error("Wrong save serialization call!");
}
};
template<typename Ser,typename T>
struct LoadWrong
{
static void invoke(Ser &s, const T &data)
{
throw std::runtime_error("Wrong load serialization call!");
}
};
template<typename Variant, typename Source>
struct VariantLoaderHelper
{
@ -570,8 +438,23 @@ public:
void addStdVecItems(CGameState *gs, LibClasses *lib = VLC);
};
class DLL_LINKAGE CSaverBase : public virtual CSerializer
class IBinaryWriter : public virtual CSerializer
{
public:
virtual int write(const void * data, unsigned size) = 0;
};
class DLL_LINKAGE CSaverBase
{
protected:
IBinaryWriter * writer;
public:
CSaverBase(IBinaryWriter * w): writer(w){};
inline int write(const void * data, unsigned size)
{
return writer->write(data, size);
};
};
class CBasicPointerSaver
@ -581,19 +464,6 @@ public:
virtual ~CBasicPointerSaver(){}
};
template <typename Serializer, typename T> class CPointerSaver : public CBasicPointerSaver
{
public:
void savePtr(CSaverBase &ar, const void *data) const
{
Serializer &s = static_cast<Serializer&>(ar);
const T *ptr = static_cast<const T*>(data);
//T is most derived known type, it's time to call actual serialize
const_cast<T&>(*ptr).serialize(s,version);
}
};
template <typename T> //metafunction returning CGObjectInstance if T is its derivate or T elsewise
struct VectorisedTypeFor
{
@ -658,6 +528,7 @@ struct SaveIfStackInstance
return false;
}
};
template<typename Ser>
struct SaveIfStackInstance<Ser, CStackInstance *>
{
@ -676,6 +547,7 @@ struct SaveIfStackInstance<Ser, CStackInstance *>
return true;
}
};
template<typename Ser,typename T>
struct LoadIfStackInstance
{
@ -711,16 +583,101 @@ struct LoadIfStackInstance<Ser, CStackInstance *>
/// The class which manages saving objects.
template <typename Serializer> class DLL_LINKAGE COSer : public CSaverBase
class DLL_LINKAGE COSer : public CSaverBase
{
public:
struct SaveBoolean
{
static void invoke(COSer &s, const bool &data)
{
s.saveBoolean(data);
}
};
struct SaveBooleanVector
{
static void invoke(COSer &s, const std::vector<bool> &data)
{
s.saveBooleanVector(data);
}
};
template<typename T>
struct SavePrimitive
{
static void invoke(COSer &s, const T &data)
{
s.savePrimitive(data);
}
};
template<typename T>
struct SaveSerializable
{
static void invoke(COSer &s, const T &data)
{
s.saveSerializable(data);
}
};
template<typename T>
struct SaveEnum
{
static void invoke(COSer &s, const T &data)
{
s.saveEnum(data);
}
};
template<typename T>
struct SavePointer
{
static void invoke(COSer &s, const T &data)
{
s.savePointer(data);
}
};
template<typename T>
struct SaveArray
{
static void invoke(COSer &s, const T &data)
{
s.saveArray(data);
}
};
template<typename T>
struct SaveWrong
{
static void invoke(COSer &s, const T &data)
{
throw std::runtime_error("Wrong save serialization call!");
}
};
template <typename T>
class CPointerSaver : public CBasicPointerSaver
{
public:
void savePtr(CSaverBase &ar, const void *data) const override
{
COSer &s = static_cast<COSer&>(ar);
const T *ptr = static_cast<const T*>(data);
//T is most derived known type, it's time to call actual serialize
const_cast<T&>(*ptr).serialize(s,version);
}
};
bool saving;
std::map<ui16,CBasicPointerSaver*> savers; // typeID => CPointerSaver<serializer,type>
std::map<const void*, ui32> savedPointers;
bool smartPointerSerialization;
COSer()
COSer(IBinaryWriter * w): CSaverBase(w)
{
saving=true;
smartPointerSerialization = true;
@ -738,7 +695,7 @@ public:
{
auto ID = typeList.getTypeID(t);
if(!savers.count(ID))
savers[ID] = new CPointerSaver<COSer<Serializer>, T>;
savers[ID] = new CPointerSaver<T>;
}
template<typename Base, typename Derived> void registerType(const Base * b = nullptr, const Derived * d = nullptr)
@ -748,31 +705,23 @@ public:
addSaver(d);
}
Serializer * This()
{
return static_cast<Serializer*>(this);
}
template<class T>
Serializer & operator<<(const T &t)
COSer & operator<<(const T &t)
{
this->This()->save(t);
return * this->This();
this->save(t);
return * this;
}
template<class T>
COSer & operator&(const T & t)
{
return * this->This() << t;
return * this << t;
}
int write(const void * data, unsigned size);
template <typename T>
void savePrimitive(const T &data)
{
this->This()->write(&data,sizeof(data));
this->write(&data,sizeof(data));
}
template <typename T>
@ -786,23 +735,23 @@ public:
if(!hlp)
return;
if(smartVectorMembersSerialization)
if(writer->smartVectorMembersSerialization)
{
typedef typename boost::remove_const<typename boost::remove_pointer<T>::type>::type TObjectType;
typedef typename VectorisedTypeFor<TObjectType>::type VType;
typedef typename VectorizedIDType<TObjectType>::type IDType;
if(const auto *info = getVectorisedTypeInfo<VType, IDType>())
if(const auto *info = writer->getVectorisedTypeInfo<VType, IDType>())
{
IDType id = getIdFromVectorItem<VType>(*info, data);
IDType id = writer->getIdFromVectorItem<VType>(*info, data);
*this << id;
if(id != IDType(-1)) //vector id is enough
return;
}
}
if(sendStackInstanceByIds)
if(writer->sendStackInstanceByIds)
{
const bool gotSaved = SaveIfStackInstance<Serializer,T>::invoke(*This(), data);
const bool gotSaved = SaveIfStackInstance<COSer,T>::invoke(*this, data);
if(gotSaved)
return;
}
@ -830,7 +779,7 @@ public:
ui16 tid = typeList.getTypeID(data);
*this << tid;
This()->savePointerHlp(tid, data);
this->savePointerHlp(tid, data);
}
//that part of ptr serialization was extracted to allow customization of its behavior in derived classes
@ -856,27 +805,27 @@ public:
typedef
//if
typename mpl::eval_if< mpl::equal_to<SerializationLevel<T>,mpl::int_<Boolean> >,
mpl::identity<SaveBoolean<Serializer> >,
mpl::identity<SaveBoolean>,
//else if
typename mpl::eval_if< mpl::equal_to<SerializationLevel<T>,mpl::int_<BooleanVector> >,
mpl::identity<SaveBooleanVector<Serializer> >,
mpl::identity<SaveBooleanVector>,
//else if
typename mpl::eval_if< mpl::equal_to<SerializationLevel<T>,mpl::int_<Primitive> >,
mpl::identity<SavePrimitive<Serializer,T> >,
mpl::identity<SavePrimitive<T> >,
//else if
typename mpl::eval_if<mpl::equal_to<SerializationLevel<T>,mpl::int_<Enum> >,
mpl::identity<SaveEnum<Serializer,T> >,
mpl::identity<SaveEnum<T> >,
//else if
typename mpl::eval_if<mpl::equal_to<SerializationLevel<T>,mpl::int_<Pointer> >,
mpl::identity<SavePointer<Serializer,T> >,
mpl::identity<SavePointer<T> >,
//else if
typename mpl::eval_if<mpl::equal_to<SerializationLevel<T>,mpl::int_<Array> >,
mpl::identity<SaveArray<Serializer,T> >,
mpl::identity<SaveArray<T> >,
//else if
typename mpl::eval_if<mpl::equal_to<SerializationLevel<T>,mpl::int_<Serializable> >,
mpl::identity<SaveSerializable<Serializer,T> >,
mpl::identity<SaveSerializable<T> >,
//else
mpl::identity<SaveWrong<Serializer,T> >
mpl::identity<SaveWrong<T> >
>
>
>
@ -884,7 +833,7 @@ public:
>
>
>::type typex;
typex::invoke(* this->This(), data);
typex::invoke(* this, data);
}
template <typename T>
void saveSerializable(const T &data)
@ -947,7 +896,7 @@ public:
void saveSerializable(const std::string &data)
{
*this << ui32(data.length());
this->This()->write(data.c_str(),data.size());
this->write(data.c_str(),data.size());
}
template <typename T1, typename T2>
void saveSerializable(const std::pair<T1,T2> &data)
@ -974,7 +923,7 @@ public:
si32 which = data.which();
*this << which;
VariantVisitorSaver<Serializer> visitor(*this->This());
VariantVisitorSaver<COSer> visitor(*this);
boost::apply_visitor(visitor, data);
}
template <typename T>
@ -1009,10 +958,24 @@ public:
}
};
class IBinaryReader : public virtual CSerializer
{
public:
virtual int read(void * data, unsigned size) = 0;
};
class DLL_LINKAGE CLoaderBase : public virtual CSerializer
{};
class DLL_LINKAGE CLoaderBase
{
protected:
IBinaryReader * reader;
public:
CLoaderBase(IBinaryReader * r): reader(r){};
inline int read(void * data, unsigned size)
{
return reader->read(data, size);
};
};
class CBasicPointerLoader
{
@ -1040,28 +1003,99 @@ struct ClassObjectCreator<T, typename std::enable_if<std::is_abstract<T>::value>
}
};
template <typename Serializer, typename T> class CPointerLoader : public CBasicPointerLoader
{
public:
const std::type_info * loadPtr(CLoaderBase &ar, void *data, ui32 pid) const //data is pointer to the ACTUAL POINTER
{
Serializer &s = static_cast<Serializer&>(ar);
T *&ptr = *static_cast<T**>(data);
//create new object under pointer
typedef typename boost::remove_pointer<T>::type npT;
ptr = ClassObjectCreator<npT>::invoke(); //does new npT or throws for abstract classes
s.ptrAllocated(ptr, pid);
//T is most derived known type, it's time to call actual serialize
ptr->serialize(s,version);
return &typeid(T);
}
};
/// The class which manages loading of objects.
template <typename Serializer> class DLL_LINKAGE CISer : public CLoaderBase
class DLL_LINKAGE CISer : public CLoaderBase
{
public:
struct LoadBoolean
{
static void invoke(CISer &s, bool &data)
{
s.loadBoolean(data);
}
};
struct LoadBooleanVector
{
static void invoke(CISer &s, std::vector<bool> &data)
{
s.loadBooleanVector(data);
}
};
template<typename T>
struct LoadEnum
{
static void invoke(CISer &s, T &data)
{
s.loadEnum(data);
}
};
template<typename T>
struct LoadPrimitive
{
static void invoke(CISer &s, T &data)
{
s.loadPrimitive(data);
}
};
template<typename T>
struct LoadPointer
{
static void invoke(CISer &s, T &data)
{
s.loadPointer(data);
}
};
template<typename T>
struct LoadArray
{
static void invoke(CISer &s, T &data)
{
s.loadArray(data);
}
};
template<typename T>
struct LoadSerializable
{
static void invoke(CISer &s, T &data)
{
s.loadSerializable(data);
}
};
template<typename T>
struct LoadWrong
{
static void invoke(CISer &s, const T &data)
{
throw std::runtime_error("Wrong load serialization call!");
}
};
template <typename T> class CPointerLoader : public CBasicPointerLoader
{
public:
const std::type_info * loadPtr(CLoaderBase &ar, void *data, ui32 pid) const override //data is pointer to the ACTUAL POINTER
{
CISer &s = static_cast<CISer&>(ar);
T *&ptr = *static_cast<T**>(data);
//create new object under pointer
typedef typename boost::remove_pointer<T>::type npT;
ptr = ClassObjectCreator<npT>::invoke(); //does new npT or throws for abstract classes
s.ptrAllocated(ptr, pid);
//T is most derived known type, it's time to call actual serialize
ptr->serialize(s,version);
return &typeid(T);
}
};
bool saving;
std::map<ui16,CBasicPointerLoader*> loaders; // typeID => CPointerSaver<serializer,type>
si32 fileVersion;
@ -1073,7 +1107,7 @@ public:
bool smartPointerSerialization;
CISer()
CISer(IBinaryReader * r): CLoaderBase(r)
{
saving = false;
fileVersion = 0;
@ -1094,7 +1128,7 @@ public:
{
auto ID = typeList.getTypeID(t);
if(!loaders.count(ID))
loaders[ID] = new CPointerLoader<CISer<Serializer>, T>;
loaders[ID] = new CPointerLoader<T>;
}
template<typename Base, typename Derived> void registerType(const Base * b = nullptr, const Derived * d = nullptr)
@ -1104,22 +1138,17 @@ public:
addLoader(d);
}
Serializer * This()
{
return static_cast<Serializer*>(this);
}
template<class T>
Serializer & operator>>(T &t)
CISer & operator>>(T &t)
{
this->This()->load(t);
return * this->This();
this->load(t);
return * this;
}
template<class T>
CISer & operator&(T & t)
{
return * this->This() >> t;
return * this >> t;
}
int write(const void * data, unsigned size);
@ -1129,27 +1158,27 @@ public:
typedef
//if
typename mpl::eval_if< mpl::equal_to<SerializationLevel<T>,mpl::int_<Boolean> >,
mpl::identity<LoadBoolean<Serializer> >,
mpl::identity<LoadBoolean>,
//else if
typename mpl::eval_if< mpl::equal_to<SerializationLevel<T>,mpl::int_<BooleanVector> >,
mpl::identity<LoadBooleanVector<Serializer> >,
mpl::identity<LoadBooleanVector>,
//else if
typename mpl::eval_if< mpl::equal_to<SerializationLevel<T>,mpl::int_<Primitive> >,
mpl::identity<LoadPrimitive<Serializer,T> >,
mpl::identity<LoadPrimitive<T> >,
//else if
typename mpl::eval_if<mpl::equal_to<SerializationLevel<T>,mpl::int_<Enum> >,
mpl::identity<LoadEnum<Serializer,T> >,
mpl::identity<LoadEnum<T> >,
//else if
typename mpl::eval_if<mpl::equal_to<SerializationLevel<T>,mpl::int_<Pointer> >,
mpl::identity<LoadPointer<Serializer,T> >,
mpl::identity<LoadPointer<T> >,
//else if
typename mpl::eval_if<mpl::equal_to<SerializationLevel<T>,mpl::int_<Array> >,
mpl::identity<LoadArray<Serializer,T> >,
mpl::identity<LoadArray<T> >,
//else if
typename mpl::eval_if<mpl::equal_to<SerializationLevel<T>,mpl::int_<Serializable> >,
mpl::identity<LoadSerializable<Serializer,T> >,
mpl::identity<LoadSerializable<T> >,
//else
mpl::identity<LoadWrong<Serializer,T> >
mpl::identity<LoadWrong<T> >
>
>
>
@ -1157,20 +1186,20 @@ public:
>
>
>::type typex;
typex::invoke(* this->This(), data);
typex::invoke(* this, data);
}
template <typename T>
void loadPrimitive(T &data)
{
if(0) //for testing #989
{
this->This()->read(&data,sizeof(data));
this->read(&data,sizeof(data));
}
else
{
unsigned length = sizeof(data);
char* dataPtr = (char*)&data;
this->This()->read(dataPtr,length);
this->read(dataPtr,length);
if(reverseEndianess)
std::reverse(dataPtr, dataPtr + length);
}
@ -1209,26 +1238,26 @@ public:
return;
}
if(smartVectorMembersSerialization)
if(reader->smartVectorMembersSerialization)
{
typedef typename boost::remove_const<typename boost::remove_pointer<T>::type>::type TObjectType; //eg: const CGHeroInstance * => CGHeroInstance
typedef typename VectorisedTypeFor<TObjectType>::type VType; //eg: CGHeroInstance -> CGobjectInstance
typedef typename VectorizedIDType<TObjectType>::type IDType;
if(const auto *info = getVectorisedTypeInfo<VType, IDType>())
if(const auto *info = reader->getVectorisedTypeInfo<VType, IDType>())
{
IDType id;
*this >> id;
if(id != IDType(-1))
{
data = static_cast<T>(getVectorItemFromId<VType, IDType>(*info, id));
data = static_cast<T>(reader->getVectorItemFromId<VType, IDType>(*info, id));
return;
}
}
}
if(sendStackInstanceByIds)
if(reader->sendStackInstanceByIds)
{
bool gotLoaded = LoadIfStackInstance<Serializer,T>::invoke(*This(), data);
bool gotLoaded = LoadIfStackInstance<CISer,T>::invoke(* this, data);
if(gotLoaded)
return;
}
@ -1252,7 +1281,7 @@ public:
//get type id
ui16 tid;
*this >> tid;
This()->loadPointerHlp(tid, data, pid);
this->loadPointerHlp(tid, data, pid);
}
//that part of ptr deserialization was extracted to allow customization of its behavior in derived classes
@ -1290,7 +1319,7 @@ public:
if(length > 500000) \
{ \
logGlobal->warnStream() << "Warning: very big length: " << length;\
reportState(logGlobal); \
reader->reportState(logGlobal); \
};
@ -1437,7 +1466,7 @@ public:
{
READ_CHECK_U32(length);
data.resize(length);
this->This()->read((void*)data.c_str(),length);
this->read((void*)data.c_str(),length);
}
template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
@ -1506,52 +1535,71 @@ public:
};
class DLL_LINKAGE CSaveFile
: public COSer<CSaveFile>
:public IBinaryWriter
{
public:
COSer serializer;
std::string fName;
unique_ptr<std::ofstream> sfile;
CSaveFile(const std::string &fname); //throws!
~CSaveFile();
int write(const void * data, unsigned size);
int write(const void * data, unsigned size) override;
void openNextFile(const std::string &fname); //throws!
void clear();
void reportState(CLogger * out);
void putMagicBytes(const std::string &text);
template<class T>
CSaveFile & operator<<(const T &t)
{
serializer << t;
return * this;
}
};
class DLL_LINKAGE CLoadFile
: public CISer<CLoadFile>
: public IBinaryReader
{
public:
CISer serializer;
std::string fName;
unique_ptr<boost::filesystem::ifstream> sfile;
CLoadFile(const boost::filesystem::path & fname, int minimalVersion = version); //throws!
~CLoadFile();
int read(void * data, unsigned size); //throws!
int read(void * data, unsigned size) override; //throws!
void openNextFile(const boost::filesystem::path & fname, int minimalVersion); //throws!
void clear();
void reportState(CLogger * out);
void checkMagicBytes(const std::string & text);
template<class T>
CLoadFile & operator>>(T &t)
{
serializer >> t;
return * this;
}
};
class DLL_LINKAGE CLoadIntegrityValidator : public CISer<CLoadIntegrityValidator>
class DLL_LINKAGE CLoadIntegrityValidator
: public IBinaryReader
{
public:
CISer serializer;
unique_ptr<CLoadFile> primaryFile, controlFile;
bool foundDesync;
CLoadIntegrityValidator(const std::string &primaryFileName, const std::string &controlFileName, int minimalVersion = version); //throws!
int read( void * data, unsigned size); //throws!
int read( void * data, unsigned size) override; //throws!
void checkMagicBytes(const std::string &text);
unique_ptr<CLoadFile> decay(); //returns primary file. CLoadIntegrityValidator stops being usable anymore
@ -1561,7 +1609,7 @@ typedef boost::asio::basic_stream_socket < boost::asio::ip::tcp , boost::asio::s
typedef boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, boost::asio::socket_acceptor_service<boost::asio::ip::tcp> > TAcceptor;
class DLL_LINKAGE CConnection
: public CISer<CConnection>, public COSer<CConnection>
: public IBinaryReader, public IBinaryWriter
{
//CGameState *gs;
CConnection(void);
@ -1569,6 +1617,9 @@ class DLL_LINKAGE CConnection
void init();
void reportState(CLogger * out);
public:
CISer iser;
COSer oser;
boost::mutex *rmx, *wmx; // read/write mutexes
TSocket * socket;
bool logging;
@ -1586,8 +1637,8 @@ public:
CConnection(TAcceptor * acceptor, boost::asio::io_service *Io_service, std::string Name);
CConnection(TSocket * Socket, std::string Name); //use immediately after accepting connection into socket
int write(const void * data, unsigned size);
int read(void * data, unsigned size);
int write(const void * data, unsigned size) override;
int read(void * data, unsigned size) override;
void close();
bool isOpen() const;
template<class T>
@ -1606,6 +1657,20 @@ public:
void prepareForSendingHeroes(); //disables sending vectorised, enables smart pointer serialization, clears saved/loaded ptr cache
void enterPregameConnectionMode();
template<class T>
CConnection & operator>>(T &t)
{
iser >> t;
return * this;
}
template<class T>
CConnection & operator<<(const T &t)
{
oser << t;
return * this;
}
};
DLL_LINKAGE std::ostream &operator<<(std::ostream &str, const CConnection &cpc);
@ -1613,14 +1678,17 @@ DLL_LINKAGE std::ostream &operator<<(std::ostream &str, const CConnection &cpc);
// Serializer that stores objects in the dynamic buffer. Allows performing deep object copies.
class DLL_LINKAGE CMemorySerializer
: public CISer<CMemorySerializer>, public COSer<CMemorySerializer>
: public IBinaryReader, public IBinaryWriter
{
std::vector<ui8> buffer;
size_t readPos; //index of the next byte to be read
public:
int read(void * data, unsigned size); //throws!
int write(const void * data, unsigned size);
CISer iser;
COSer oser;
int read(void * data, unsigned size) override; //throws!
int write(const void * data, unsigned size) override;
CMemorySerializer();
@ -1628,10 +1696,10 @@ public:
static unique_ptr<T> deepCopy(const T &data)
{
CMemorySerializer mem;
mem << &data;
mem.oser << &data;
unique_ptr<T> ret;
mem >> ret;
mem.iser >> ret;
return ret;
}
};

View File

@ -149,16 +149,16 @@ void CPrivilagedInfoCallback::loadCommonState(Loader &in)
StartInfo *si;
logGlobal->infoStream() <<"\tReading header";
in >> dum;
in.serializer >> dum;
logGlobal->infoStream() << "\tReading options";
in >> si;
in.serializer >> si;
logGlobal->infoStream() <<"\tReading handlers";
in >> *VLC;
in.serializer >> *VLC;
logGlobal->infoStream() <<"\tReading gamestate";
in >> gs;
in.serializer >> gs;
}
template<typename Saver>
@ -167,13 +167,13 @@ void CPrivilagedInfoCallback::saveCommonState(Saver &out) const
logGlobal->infoStream() << "Saving lib part of game...";
out.putMagicBytes(SAVEGAME_MAGIC);
logGlobal->infoStream() <<"\tSaving header";
out << static_cast<CMapHeader&>(*gs->map);
out.serializer << static_cast<CMapHeader&>(*gs->map);
logGlobal->infoStream() << "\tSaving options";
out << gs->scenarioOps;
out.serializer << gs->scenarioOps;
logGlobal->infoStream() << "\tSaving handlers";
out << *VLC;
out.serializer << *VLC;
logGlobal->infoStream() << "\tSaving gamestate";
out << gs;
out.serializer << gs;
}
// hardly memory usage for `-gdwarf-4` flag

View File

@ -147,6 +147,7 @@
<LinkTimeCodeGeneration>
</LinkTimeCodeGeneration>
<AdditionalLibraryDirectories>..\..\libs</AdditionalLibraryDirectories>
<AdditionalOptions>/LTCG %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">

View File

@ -370,6 +370,29 @@ IObjectInfo::CArmyStructure CBankInfo::maxGuards() const
return *boost::range::max_element(armies);
}
TPossibleGuards CBankInfo::getPossibleGuards() const
{
TPossibleGuards out;
for (const JsonNode & configEntry : config)
{
const JsonNode & guardsInfo = configEntry["guards"];
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]);
IObjectInfo::CArmyStructure army;
for (auto stack : stacks)
{
army.totalStrength += stack.allowedCreatures.front()->AIValue * (stack.minAmount + stack.maxAmount) / 2;
//TODO: add fields for flyers, walkers etc...
}
ui8 chance = configEntry["chance"].Float();
out.push_back(std::make_pair(chance, army));
}
return out;
}
bool CBankInfo::givesResources() const
{
for (const JsonNode & node : config)

View File

@ -152,14 +152,20 @@ struct BankConfig
}
};
class CBankInfo : public IObjectInfo
typedef std::vector<std::pair<ui8, IObjectInfo::CArmyStructure>> TPossibleGuards;
class DLL_LINKAGE CBankInfo : public IObjectInfo
{
JsonVector config;
public:
CBankInfo(JsonVector config);
TPossibleGuards getPossibleGuards() const;
//I have no idea what do these functions do or were supposed to do - War
CArmyStructure minGuards() const;
CArmyStructure maxGuards() const;
bool givesResources() const;
bool givesArtifacts() const;
bool givesCreatures() const;

View File

@ -1445,7 +1445,8 @@ void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const
void CCartographer::onHeroVisit( const CGHeroInstance * h ) const
{
if (!wasVisited (h->getOwner()) ) //if hero has not visited yet this cartographer
//if player has not bought map of this subtype yet and underground exist for stalagmite cartographer
if (!wasVisited(h->getOwner()) && (subID != 2 || cb->gameState()->map->twoLevel))
{
if (cb->getResource(h->tempOwner, Res::GOLD) >= 1000) //if he can afford a map
{

View File

@ -27,15 +27,11 @@
// registerTypesServerPacks: 1.3 Gb
// registerTypes4: 1.3 Gb
#define DEFINE_EXTERNAL_METHOD(METHODNAME) \
extern template DLL_LINKAGE void METHODNAME<CISer<CConnection>>(CISer<CConnection>& s); \
extern template DLL_LINKAGE void METHODNAME<COSer<CConnection>>(COSer<CConnection>& s); \
extern template DLL_LINKAGE void METHODNAME<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s); \
extern template DLL_LINKAGE void METHODNAME<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s); \
extern template DLL_LINKAGE void METHODNAME<CSaveFile>(CSaveFile & s); \
extern template DLL_LINKAGE void METHODNAME<CLoadFile>(CLoadFile & s); \
extern template DLL_LINKAGE void METHODNAME<CISer>(CISer & s); \
extern template DLL_LINKAGE void METHODNAME<COSer>(COSer & s); \
extern template DLL_LINKAGE void METHODNAME<CTypeList>(CTypeList & s); \
extern template DLL_LINKAGE void METHODNAME<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);
//DEFINE_EXTERNAL_METHOD(registerTypesMapObjects)
DEFINE_EXTERNAL_METHOD(registerTypesMapObjects1)
@ -45,11 +41,6 @@ DEFINE_EXTERNAL_METHOD(registerTypesClientPacks2)
DEFINE_EXTERNAL_METHOD(registerTypesServerPacks)
DEFINE_EXTERNAL_METHOD(registerTypesPregamePacks)
template void registerTypes<CISer<CConnection>>(CISer<CConnection>& s);
template void registerTypes<COSer<CConnection>>(COSer<CConnection>& s);
template void registerTypes<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
template void registerTypes<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
template void registerTypes<CSaveFile>(CSaveFile & s);
template void registerTypes<CLoadFile>(CLoadFile & s);
template void registerTypes<CISer>(CISer & s);
template void registerTypes<COSer>(COSer & s);
template void registerTypes<CTypeList>(CTypeList & s);
template void registerTypes<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);

View File

@ -367,13 +367,10 @@ void registerTypes(Serializer &s)
}
#ifndef INSTANTIATE_REGISTER_TYPES_HERE
extern template DLL_LINKAGE void registerTypes<CISer<CConnection>>(CISer<CConnection>& s);
extern template DLL_LINKAGE void registerTypes<COSer<CConnection>>(COSer<CConnection>& s);
extern template DLL_LINKAGE void registerTypes<CSaveFile>(CSaveFile & s);
extern template DLL_LINKAGE void registerTypes<CLoadFile>(CLoadFile & s);
extern template DLL_LINKAGE void registerTypes<CISer>(CISer & s);
extern template DLL_LINKAGE void registerTypes<COSer>(COSer & s);
extern template DLL_LINKAGE void registerTypes<CTypeList>(CTypeList & s);
extern template DLL_LINKAGE void registerTypes<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);
extern template DLL_LINKAGE void registerTypes<CISer<CMemorySerializer>>(CISer<CMemorySerializer> & s);
extern template DLL_LINKAGE void registerTypes<COSer<CMemorySerializer>>(COSer<CMemorySerializer> & s);
#endif

View File

@ -19,11 +19,6 @@
#include "../mapObjects/CObjectClassesHandler.h"
template void registerTypesClientPacks1<CISer<CConnection>>(CISer<CConnection>& s);
template void registerTypesClientPacks1<COSer<CConnection>>(COSer<CConnection>& s);
template void registerTypesClientPacks1<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
template void registerTypesClientPacks1<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
template void registerTypesClientPacks1<CSaveFile>(CSaveFile & s);
template void registerTypesClientPacks1<CLoadFile>(CLoadFile & s);
template void registerTypesClientPacks1<CISer>(CISer & s);
template void registerTypesClientPacks1<COSer>(COSer & s);
template void registerTypesClientPacks1<CTypeList>(CTypeList & s);
template void registerTypesClientPacks1<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);

View File

@ -19,11 +19,8 @@
#include "../mapObjects/CObjectClassesHandler.h"
template void registerTypesClientPacks2<CISer<CConnection>>(CISer<CConnection>& s);
template void registerTypesClientPacks2<COSer<CConnection>>(COSer<CConnection>& s);
template void registerTypesClientPacks2<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
template void registerTypesClientPacks2<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
template void registerTypesClientPacks2<CSaveFile>(CSaveFile & s);
template void registerTypesClientPacks2<CLoadFile>(CLoadFile & s);
template void registerTypesClientPacks2<CISer>(CISer & s);
template void registerTypesClientPacks2<COSer>(COSer & s);
template void registerTypesClientPacks2<CTypeList>(CTypeList & s);
template void registerTypesClientPacks2<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);

View File

@ -18,12 +18,8 @@
#include "../NetPacks.h"
#include "../mapObjects/CObjectClassesHandler.h"
template void registerTypesMapObjects1<CISer<CConnection>>(CISer<CConnection>& s);
template void registerTypesMapObjects1<COSer<CConnection>>(COSer<CConnection>& s);
template void registerTypesMapObjects1<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
template void registerTypesMapObjects1<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
template void registerTypesMapObjects1<CSaveFile>(CSaveFile & s);
template void registerTypesMapObjects1<CLoadFile>(CLoadFile & s);
template void registerTypesMapObjects1<CISer>(CISer & s);
template void registerTypesMapObjects1<COSer>(COSer & s);
template void registerTypesMapObjects1<CTypeList>(CTypeList & s);
template void registerTypesMapObjects1<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);

View File

@ -19,12 +19,7 @@
#include "../mapObjects/CObjectClassesHandler.h"
template void registerTypesMapObjects2<CISer<CConnection>>(CISer<CConnection>& s);
template void registerTypesMapObjects2<COSer<CConnection>>(COSer<CConnection>& s);
template void registerTypesMapObjects2<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
template void registerTypesMapObjects2<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
template void registerTypesMapObjects2<CSaveFile>(CSaveFile & s);
template void registerTypesMapObjects2<CLoadFile>(CLoadFile & s);
template void registerTypesMapObjects2<CISer>(CISer & s);
template void registerTypesMapObjects2<COSer>(COSer & s);
template void registerTypesMapObjects2<CTypeList>(CTypeList & s);
template void registerTypesMapObjects2<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);

View File

@ -18,11 +18,6 @@
#include "../NetPacks.h"
#include "../mapObjects/CObjectClassesHandler.h"
template void registerTypesMapObjectTypes<CISer<CConnection>>(CISer<CConnection>& s);
template void registerTypesMapObjectTypes<COSer<CConnection>>(COSer<CConnection>& s);
template void registerTypesMapObjectTypes<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
template void registerTypesMapObjectTypes<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
template void registerTypesMapObjectTypes<CSaveFile>(CSaveFile & s);
template void registerTypesMapObjectTypes<CLoadFile>(CLoadFile & s);
template void registerTypesMapObjectTypes<CISer>(CISer & s);
template void registerTypesMapObjectTypes<COSer>(COSer & s);
template void registerTypesMapObjectTypes<CTypeList>(CTypeList & s);
template void registerTypesMapObjectTypes<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);

View File

@ -18,11 +18,7 @@
#include "../NetPacks.h"
#include "../mapObjects/CObjectClassesHandler.h"
template void registerTypesPregamePacks<CISer<CConnection>>(CISer<CConnection>& s);
template void registerTypesPregamePacks<COSer<CConnection>>(COSer<CConnection>& s);
template void registerTypesPregamePacks<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
template void registerTypesPregamePacks<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
template void registerTypesPregamePacks<CSaveFile>(CSaveFile & s);
template void registerTypesPregamePacks<CLoadFile>(CLoadFile & s);
template void registerTypesPregamePacks<CISer>(CISer & s);
template void registerTypesPregamePacks<COSer>(COSer & s);
template void registerTypesPregamePacks<CTypeList>(CTypeList & s);
template void registerTypesPregamePacks<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);

View File

@ -18,11 +18,6 @@
#include "../NetPacks.h"
#include "../mapObjects/CObjectClassesHandler.h"
template void registerTypesServerPacks<CISer<CConnection>>(CISer<CConnection>& s);
template void registerTypesServerPacks<COSer<CConnection>>(COSer<CConnection>& s);
template void registerTypesServerPacks<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
template void registerTypesServerPacks<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
template void registerTypesServerPacks<CSaveFile>(CSaveFile & s);
template void registerTypesServerPacks<CLoadFile>(CLoadFile & s);
template void registerTypesServerPacks<CISer>(CISer & s);
template void registerTypesServerPacks<COSer>(COSer & s);
template void registerTypesServerPacks<CTypeList>(CTypeList & s);
template void registerTypesServerPacks<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);

View File

@ -74,7 +74,7 @@ void CMapGenerator::initPrisonsRemaining()
if (isAllowed)
prisonsRemaining++;
}
prisonsRemaining = std::max<int> (0, prisonsRemaining - 16 * map->players.size()); //so at least 16 heroes will be available for every player
prisonsRemaining = std::max<int> (0, prisonsRemaining - 16 * mapGenOptions->getPlayerCount()); //so at least 16 heroes will be available for every player
}
std::unique_ptr<CMap> CMapGenerator::generate(CMapGenOptions * mapGenOptions, int randomSeed /*= std::time(nullptr)*/)

View File

@ -133,7 +133,6 @@ void CJsonRmgTemplateLoader::loadTemplates()
//treasures
if (!zoneNode["treasure"].isNull())
{
int totalDensity = 0;
//TODO: parse vector of different treasure settings
if (zoneNode["treasure"].getType() == JsonNode::DATA_STRUCT)
{
@ -143,8 +142,6 @@ void CJsonRmgTemplateLoader::loadTemplates()
ti.min = treasureInfo["min"].Float();
ti.max = treasureInfo["max"].Float();
ti.density = treasureInfo["density"].Float(); //TODO: use me
totalDensity += ti.density;
ti.threshold = totalDensity;
zone->addTreasureInfo(ti);
}
}
@ -156,12 +153,9 @@ void CJsonRmgTemplateLoader::loadTemplates()
ti.min = treasureInfo["min"].Float();
ti.max = treasureInfo["max"].Float();
ti.density = treasureInfo["density"].Float();
totalDensity += ti.density;
ti.threshold = totalDensity;
zone->addTreasureInfo(ti);
}
}
zone->setTotalDensity (totalDensity);
}
zones[zone->getId()] = zone;
@ -191,7 +185,6 @@ void CJsonRmgTemplateLoader::loadTemplates()
{
zone->addTreasureInfo(treasureInfo);
}
zone->setTotalDensity (zones[zoneNode["treasureLikeZone"].Float()]->getTotalDensity());
}
if (!zoneNode["minesLikeZone"].isNull())

View File

@ -138,8 +138,7 @@ CRmgTemplateZone::CRmgTemplateZone() :
matchTerrainToTown(true),
townType(ETownType::NEUTRAL),
terrainType (ETerrainType::GRASS),
zoneMonsterStrength(EMonsterStrength::ZONE_NORMAL),
totalDensity(0)
zoneMonsterStrength(EMonsterStrength::ZONE_NORMAL)
{
terrainTypes = getDefaultTerrainTypes();
}
@ -303,16 +302,6 @@ void CRmgTemplateZone::setMonsterStrength (EMonsterStrength::EMonsterStrength va
zoneMonsterStrength = val;
}
void CRmgTemplateZone::setTotalDensity (ui16 val)
{
totalDensity = val;
}
ui16 CRmgTemplateZone::getTotalDensity () const
{
return totalDensity;
}
void CRmgTemplateZone::addTreasureInfo(CTreasureInfo & info)
{
treasureInfo.push_back(info);
@ -420,7 +409,11 @@ void CRmgTemplateZone::fractalize(CMapGenerator* gen)
std::set<int3> tilesToIgnore; //will be erased in this iteration
//the more treasure density, the greater distance between paths. Scaling is experimental.
int totalDensity = 0;
for (auto ti : treasureInfo)
totalDensity =+ ti.density;
const float minDistance = std::sqrt(totalDensity * 3);
for (auto tile : tileinfo)
{
if (gen->isFree(tile))
@ -612,6 +605,10 @@ void CRmgTemplateZone::addRequiredObject(CGObjectInstance * obj, si32 strength)
{
requiredObjects.push_back(std::make_pair(obj, strength));
}
void CRmgTemplateZone::addCloseObject(CGObjectInstance * obj, si32 strength)
{
closeObjects.push_back(std::make_pair(obj, strength));
}
bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength, bool clearSurroundingTiles, bool zoneGuard)
{
@ -686,7 +683,7 @@ bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength,
return true;
}
bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos, float minDistance)
bool CRmgTemplateZone::createTreasurePile(CMapGenerator* gen, int3 &pos, float minDistance, const CTreasureInfo& treasureInfo)
{
CTreasurePileInfo info;
@ -695,25 +692,9 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos, float
int3 guardPos (-1,-1,-1);
info.nextTreasurePos = pos;
//default values
int maxValue = 5000;
int minValue = 1500;
int maxValue = treasureInfo.max;
int minValue = treasureInfo.min;
if (treasureInfo.size())
{
//roulette wheel
int r = gen->rand.nextInt (1, totalDensity);
for (auto t : treasureInfo)
{
if (r <= t.threshold)
{
maxValue = t.max;
minValue = t.min;
break;
}
}
}
ui32 desiredValue = gen->rand.nextInt(minValue, maxValue);
//quantize value to let objects with value equal to max spawn too
@ -1132,11 +1113,8 @@ bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
static const Res::ERes woodOre[] = {Res::ERes::WOOD, Res::ERes::ORE};
static const Res::ERes preciousResources[] = {Res::ERes::GEMS, Res::ERes::CRYSTAL, Res::ERes::MERCURY, Res::ERes::SULFUR};
//TODO: factory / copy constructor?
for (const auto & res : woodOre)
{
//TODO: these 2 should be close to town (within 12 tiles radius)
for (int i = 0; i < mines[res]; i++)
{
auto mine = new CGMine();
@ -1144,7 +1122,7 @@ bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
mine->subID = static_cast<si32>(res);
mine->producedResource = res;
mine->producedQuantity = mine->defaultResProduction();
addRequiredObject(mine, 1500);
addCloseObject(mine, 1500);
}
}
for (const auto & res : preciousResources)
@ -1189,35 +1167,91 @@ bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen)
guardObject (gen, obj.first, obj.second, (obj.first->ID == Obj::MONOLITH_TWO_WAY), true);
//paths to required objects constitute main paths of zone. otherwise they just may lead to middle and create dead zones
}
for (const auto &obj : closeObjects)
{
std::vector<int3> tiles(possibleTiles.begin(), possibleTiles.end()); //new tiles vector after each object has been placed
// smallest distance to zone center, greatest distance to nearest object
auto isCloser = [this, gen](const int3 & lhs, const int3 & rhs) -> bool
{
return (this->pos.dist2dSQ(lhs) * 0.5f - gen->getNearestObjectDistance(lhs)) < (this->pos.dist2dSQ(rhs) * 0.5f - gen->getNearestObjectDistance(rhs));
};
boost::sort (tiles, isCloser);
setTemplateForObject(gen, obj.first);
auto tilesBlockedByObject = obj.first->getBlockedOffsets();
bool result = false;
for (auto tile : tiles)
{
//object must be accessible from at least one surounding tile
if (!isAccessibleFromAnywhere(gen, obj.first->appearance, tile, tilesBlockedByObject))
continue;
//avoid borders
if (gen->isPossible(tile))
{
if (areAllTilesAvailable(gen, obj.first, tile, tilesBlockedByObject))
{
placeObject(gen, obj.first, tile);
guardObject(gen, obj.first, obj.second, (obj.first->ID == Obj::MONOLITH_TWO_WAY), true);
result = true;
break;
}
}
}
if (!result)
{
logGlobal->errorStream() << boost::format("Failed to fill zone %d due to lack of space") % id;
//TODO CLEANUP!
return false;
}
}
return true;
}
void CRmgTemplateZone::createTreasures(CMapGenerator* gen)
{
//treasure density is proportional to map siz,e but must be scaled bakc to map size
//also, normalize it to zone count - higher count means relative smaller zones
auto valueComparator = [](const CTreasureInfo & lhs, const CTreasureInfo & rhs) -> bool
{
return lhs.max > rhs.max;
};
//this is squared distance for optimization purposes
const double minDistance = std::max<float>((600.f * size * size * gen->getZones().size()) /
(gen->mapGenOptions->getWidth() * gen->mapGenOptions->getHeight() * totalDensity * (gen->map->twoLevel ? 2 : 1)), 2);
//distance lower than 2 causes objects to overlap and crash
//place biggest treasures first at large distance, place smaller ones inbetween
boost::sort(treasureInfo, valueComparator);
do {
//optimization - don't check tiles which are not allowed
vstd::erase_if (possibleTiles, [gen](const int3 &tile) -> bool
{
return !gen->isPossible(tile);
});
int totalDensity = 0;
for (auto t : treasureInfo)
{
totalDensity += t.density;
int3 pos;
if ( ! findPlaceForTreasurePile(gen, minDistance, pos))
{
break;
}
createTreasurePile (gen, pos, minDistance);
//treasure density is inversely proportional to zone size but must be scaled back to map size
//also, normalize it to zone count - higher count means relatively smaller zones
} while(true);
//this is squared distance for optimization purposes
const double minDistance = std::max<float>((450.f * size * size * gen->getZones().size()) /
(gen->mapGenOptions->getWidth() * gen->mapGenOptions->getHeight() * totalDensity * (gen->map->twoLevel ? 2 : 1)), 2);
//distance lower than 2 causes objects to overlap and crash
do {
//optimization - don't check tiles which are not allowed
vstd::erase_if(possibleTiles, [gen](const int3 &tile) -> bool
{
return !gen->isPossible(tile);
});
int3 pos;
if (!findPlaceForTreasurePile(gen, minDistance, pos))
{
break;
}
createTreasurePile(gen, pos, minDistance, t);
} while (true);
}
}
void CRmgTemplateZone::createObstacles(CMapGenerator* gen)
@ -1451,17 +1485,36 @@ bool CRmgTemplateZone::isAccessibleFromAnywhere (CMapGenerator* gen, ObjectTempl
return accessible;
}
bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance* obj, si32 min_dist, int3 &pos)
void CRmgTemplateZone::setTemplateForObject(CMapGenerator* gen, CGObjectInstance* obj)
{
//we need object apperance to deduce free tiles
if (obj->appearance.id == Obj::NO_OBJ)
{
auto templates = VLC->objtypeh->getHandlerFor(obj->ID, obj->subID)->getTemplates(gen->map->getTile(getPos()).terType);
if (templates.empty())
throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s") %obj->ID %obj->subID %pos));
throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s") % obj->ID %obj->subID %pos));
obj->appearance = templates.front();
}
}
bool CRmgTemplateZone::areAllTilesAvailable(CMapGenerator* gen, CGObjectInstance* obj, int3& tile, std::set<int3>& tilesBlockedByObject) const
{
for (auto blockingTile : tilesBlockedByObject)
{
int3 t = tile + blockingTile;
if (!gen->map->isInTheMap(t) || !gen->isPossible(t))
{
//if at least one tile is not possible, object can't be placed here
return false;
}
}
return true;
}
bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance* obj, si32 min_dist, int3 &pos)
{
//we need object apperance to deduce free tile
setTemplateForObject(gen, obj);
//si32 min_dist = sqrt(tileinfo.size()/density);
int best_distance = 0;
@ -1484,17 +1537,7 @@ bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance*
//avoid borders
if (gen->isPossible(tile) && (dist >= min_dist) && (dist > best_distance))
{
bool allTilesAvailable = true;
for (auto blockingTile : tilesBlockedByObject)
{
int3 t = tile + blockingTile;
if (!gen->map->isInTheMap(t) || !gen->isPossible(t))
{
allTilesAvailable = false; //if at least one tile is not possible, object can't be placed here
break;
}
}
if (allTilesAvailable)
if (areAllTilesAvailable(gen, obj, tile, tilesBlockedByObject))
{
best_distance = dist;
pos = tile;

View File

@ -63,7 +63,6 @@ public:
ui32 min;
ui32 max;
ui16 density;
ui16 threshold; //how much must RNG roll to pick that zone
};
struct DLL_LINKAGE ObjectInfo
@ -151,8 +150,9 @@ public:
void discardDistantTiles (CMapGenerator* gen, float distance);
void addRequiredObject(CGObjectInstance * obj, si32 guardStrength=0);
void addCloseObject(CGObjectInstance * obj, si32 guardStrength = 0);
bool addMonster(CMapGenerator* gen, int3 &pos, si32 strength, bool clearSurroundingTiles = true, bool zoneGuard = false);
bool createTreasurePile(CMapGenerator* gen, int3 &pos, float minDistance);
bool createTreasurePile(CMapGenerator* gen, int3 &pos, float minDistance, const CTreasureInfo& treasureInfo);
bool fill (CMapGenerator* gen);
bool placeMines (CMapGenerator* gen);
void initTownType (CMapGenerator* gen);
@ -166,8 +166,6 @@ public:
bool crunchPath (CMapGenerator* gen, const int3 &src, const int3 &dst, TRmgTemplateZoneId zone, std::set<int3>* clearedTiles = nullptr);
std::vector<int3> getAccessibleOffsets (CMapGenerator* gen, CGObjectInstance* object);
void setTotalDensity (ui16 val);
ui16 getTotalDensity () const;
void addConnection(TRmgTemplateZoneId otherZone);
std::vector<TRmgTemplateZoneId> getConnections() const;
void addTreasureInfo(CTreasureInfo & info);
@ -196,12 +194,12 @@ private:
ETerrainType terrainType;
EMonsterStrength::EMonsterStrength zoneMonsterStrength;
ui16 totalDensity;
std::vector<CTreasureInfo> treasureInfo;
std::vector<ObjectInfo> possibleObjects;
//content info
std::vector<std::pair<CGObjectInstance*, ui32>> requiredObjects;
std::vector<std::pair<CGObjectInstance*, ui32>> closeObjects;
std::vector<CGObjectInstance*> objects;
//placement info
@ -218,6 +216,8 @@ private:
bool findPlaceForObject(CMapGenerator* gen, CGObjectInstance* obj, si32 min_dist, int3 &pos);
bool findPlaceForTreasurePile(CMapGenerator* gen, float min_dist, int3 &pos);
bool canObstacleBePlacedHere(CMapGenerator* gen, ObjectTemplate &temp, int3 &pos);
void setTemplateForObject(CMapGenerator* gen, CGObjectInstance* obj);
bool areAllTilesAvailable(CMapGenerator* gen, CGObjectInstance* obj, int3& tile, std::set<int3>& tilesBlockedByObject) const;
void checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos);
void placeObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, bool updateDistance = true);
bool guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str, bool zoneGuard = false, bool addToFreePaths = false);

View File

@ -40,12 +40,6 @@ int3 CZonePlacer::cords (const float3 f) const
void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenerator * rand)
{
//gravity-based algorithm
float gravityConstant = 1e-2;
float zoneScale = 0.5f; //zones starts small and then inflate
const float inflateModifier = 1.02;
logGlobal->infoStream() << "Starting zone placement";
int width = mapGenOptions->getWidth();
@ -54,6 +48,13 @@ void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenera
auto zones = gen->getZones();
bool underground = mapGenOptions->getHasTwoLevels();
//gravity-based algorithm
const float gravityConstant = 4e-3;
const float stiffnessConstant = 4e-3;
float zoneScale = 1.0f / std::sqrt(zones.size()); //zones starts small and then inflate. placing more zones is more difficult
const float inflateModifier = 1.02;
/*
let's assume we try to fit N circular zones with radius = size on a map
@ -69,6 +70,10 @@ void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenera
bool undergroundFlag = false;
std::vector<float> totalSize = { 0, 0 }; //make sure that sum of zone sizes on surface and uderground match size of the map
const float radius = 0.4f;
const float pi2 = 6.28f;
for (auto zone : zonesVector)
{
//even distribution for surface / underground zones. Surface zones always have priority.
@ -87,7 +92,8 @@ void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenera
}
totalSize[level] += (zone.second->getSize() * zone.second->getSize());
zone.second->setCenter (float3(rand->nextDouble(0.2, 0.8), rand->nextDouble(0.2, 0.8), level)); //start away from borders
float randomAngle = rand->nextDouble(0, pi2);
zone.second->setCenter(float3(0.5f + std::sin(randomAngle) * radius, 0.5f + std::cos(randomAngle) * radius, level)); //place zones around circle
}
//prescale zones
std::vector<float> prescaler = { 0, 0 };
@ -101,6 +107,14 @@ void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenera
//gravity-based algorithm. connected zones attract, intersceting zones and map boundaries push back
//remember best solution
float bestTotalDistance = 1e10;
float bestTotalOverlap = 1e10;
//float bestRatio = 1e10;
std::map<CRmgTemplateZone *, float3> bestSolution;
const int maxDistanceMovementRatio = zones.size() * zones.size(); //experimental - the more zones, the greater total distance expected
auto getDistance = [](float distance) -> float
{
return (distance ? distance * distance : 1e-6);
@ -108,6 +122,7 @@ void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenera
std::map <CRmgTemplateZone *, float3> forces;
std::map <CRmgTemplateZone *, float> distances;
std::map <CRmgTemplateZone *, float> overlaps;
while (zoneScale < 1) //until zones reach their desired size and fill the map tightly
{
for (auto zone : zones)
@ -126,11 +141,13 @@ void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenera
if (distance > minDistance)
{
//WARNING: compiler used to 'optimize' that line so it never actually worked
forceVector += (((otherZoneCenter - pos)*(pos.z != otherZoneCenter.z ? (distance - minDistance) : 1)/ getDistance(distance))); //positive value
totalDistance += distance;
forceVector += (((otherZoneCenter - pos)*(pos.z == otherZoneCenter.z ? (minDistance/distance) : 1)/ getDistance(distance))) * gravityConstant; //positive value
totalDistance += (distance - minDistance);
}
}
distances[zone.second] = totalDistance;
float totalOverlap = 0;
//separate overlaping zones
for (auto otherZone : zones)
{
@ -143,7 +160,8 @@ void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenera
float minDistance = (zone.second->getSize() + otherZone.second->getSize())/mapSize * zoneScale;
if (distance < minDistance)
{
forceVector -= (((otherZoneCenter - pos)*(minDistance - distance)) / getDistance(distance)); //negative value
forceVector -= (((otherZoneCenter - pos)*(minDistance/(distance ? distance : 1e-3))) / getDistance(distance)) * stiffnessConstant; //negative value
totalOverlap += (minDistance - distance) / (zoneScale * zoneScale); //overlapping of small zones hurts us more
}
}
@ -151,11 +169,12 @@ void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenera
//do not scale boundary distance - zones tend to get squashed
float size = zone.second->getSize() / mapSize;
auto pushAwayFromBoundary = [&forceVector, pos, &getDistance, size](float x, float y)
auto pushAwayFromBoundary = [&forceVector, pos, &getDistance, size, stiffnessConstant, &totalOverlap](float x, float y)
{
float3 boundary = float3 (x, y, pos.z);
float distance = pos.dist2d(boundary);
forceVector -= (boundary - pos) * (size - distance) / getDistance(distance); //negative value
totalOverlap += distance; //overlapping map boundaries is wrong as well
forceVector -= (boundary - pos) * (size - distance) / getDistance(distance) * stiffnessConstant; //negative value
};
if (pos.x < size)
{
@ -173,9 +192,9 @@ void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenera
{
pushAwayFromBoundary(pos.x, 1);
}
overlaps[zone.second] = totalOverlap;
forceVector.z = 0; //operator - doesn't preserve z coordinate :/
forces[zone.second] = forceVector * gravityConstant;
forces[zone.second] = forceVector;
}
//update positions
for (auto zone : forces)
@ -185,53 +204,99 @@ void CZonePlacer::placeZones(const CMapGenOptions * mapGenOptions, CRandomGenera
//now perform drastic movement of zone that is completely not linked
float maxRatio = 0;
CRmgTemplateZone * distantZone = nullptr;
CRmgTemplateZone * misplacedZone = nullptr;
float totalDistance = 0;
float totalOverlap = 0;
for (auto zone : distances) //find most misplaced zone
{
totalDistance += zone.second;
float ratio = zone.second / forces[zone.first].mag(); //if distance to actual movement is long, the zone is misplaced
float overlap = overlaps[zone.first];
totalOverlap += overlap;
float ratio = (zone.second + overlap) / forces[zone.first].mag(); //if distance to actual movement is long, the zone is misplaced
if (ratio > maxRatio)
{
maxRatio = ratio;
distantZone = zone.first;
misplacedZone = zone.first;
}
}
logGlobal->traceStream() << boost::format("Total distance between zones in this iteration: %2.2f, Worst distance/movement ratio: %3.2f") % totalDistance % maxRatio;
if (maxRatio > 100) //TODO: scale?
{
//find most distant zone that should be attracted and move inside it
CRmgTemplateZone * targetZone = nullptr;
float maxDistance = 0;
float3 ourCenter = distantZone->getCenter();
for (auto con : distantZone->getConnections())
{
auto otherZone = zones[con];
float distance = otherZone->getCenter().dist2dSQ(ourCenter);
if (distance > maxDistance)
{
maxDistance = distance;
targetZone = otherZone;
}
}
float3 vec = targetZone->getCenter() - ourCenter;
float newDistanceBetweenZones = (std::max (distantZone->getSize(),targetZone->getSize())) * zoneScale / mapSize;
logGlobal->traceStream() << boost::format("Trying to move zone %d %s towards %d %s. Old distance %f") %
distantZone->getId() % ourCenter() % targetZone->getId() % targetZone->getCenter()() % maxDistance;
logGlobal->traceStream() << boost::format("direction is %s") % vec();
logGlobal->traceStream() << boost::format("Total distance between zones in this iteration: %2.4f, Total overlap: %2.4f, Worst misplacement/movement ratio: %3.2f") % totalDistance % totalOverlap % maxRatio;
distantZone->setCenter(targetZone->getCenter() - vec.unitVector() * newDistanceBetweenZones); //zones should now overlap by half size
logGlobal->traceStream() << boost::format("New distance %f") % targetZone->getCenter().dist2d(distantZone->getCenter());
//save best solution before drastic jump
if (totalDistance + totalOverlap < bestTotalDistance + bestTotalOverlap)
{
bestTotalDistance = totalDistance;
bestTotalOverlap = totalOverlap;
//if (maxRatio < bestRatio)
//{
// bestRatio = maxRatio;
for (auto zone : zones)
bestSolution[zone.second] = zone.second->getCenter();
}
if (maxRatio > maxDistanceMovementRatio)
{
CRmgTemplateZone * targetZone = nullptr;
float3 ourCenter = misplacedZone->getCenter();
if (totalDistance > totalOverlap)
{
//find most distant zone that should be attracted and move inside it
float maxDistance = 0;
for (auto con : misplacedZone->getConnections())
{
auto otherZone = zones[con];
float distance = otherZone->getCenter().dist2dSQ(ourCenter);
if (distance > maxDistance)
{
maxDistance = distance;
targetZone = otherZone;
}
}
float3 vec = targetZone->getCenter() - ourCenter;
float newDistanceBetweenZones = (std::max(misplacedZone->getSize(), targetZone->getSize())) * zoneScale / mapSize;
logGlobal->traceStream() << boost::format("Trying to move zone %d %s towards %d %s. Old distance %f") %
misplacedZone->getId() % ourCenter() % targetZone->getId() % targetZone->getCenter()() % maxDistance;
logGlobal->traceStream() << boost::format("direction is %s") % vec();
misplacedZone->setCenter(targetZone->getCenter() - vec.unitVector() * newDistanceBetweenZones); //zones should now overlap by half size
logGlobal->traceStream() << boost::format("New distance %f") % targetZone->getCenter().dist2d(misplacedZone->getCenter());
}
else
{
float maxOverlap = 0;
for (auto otherZone : zones)
{
float3 otherZoneCenter = otherZone.second->getCenter();
if (otherZone.second == misplacedZone || otherZoneCenter.z != ourCenter.z)
continue;
float distance = otherZoneCenter.dist2dSQ(ourCenter);
if (distance > maxOverlap)
{
maxOverlap = distance;
targetZone = otherZone.second;
}
}
float3 vec = ourCenter - targetZone->getCenter();
float newDistanceBetweenZones = (misplacedZone->getSize() + targetZone->getSize()) * zoneScale / mapSize;
logGlobal->traceStream() << boost::format("Trying to move zone %d %s away from %d %s. Old distance %f") %
misplacedZone->getId() % ourCenter() % targetZone->getId() % targetZone->getCenter()() % maxOverlap;
logGlobal->traceStream() << boost::format("direction is %s") % vec();
misplacedZone->setCenter(targetZone->getCenter() + vec.unitVector() * newDistanceBetweenZones); //zones should now be just separated
logGlobal->traceStream() << boost::format("New distance %f") % targetZone->getCenter().dist2d(misplacedZone->getCenter());
}
}
zoneScale *= inflateModifier; //increase size of zones so they
}
logGlobal->traceStream() << boost::format("Best fitness reached: total distance %2.4f, total overlap %2.4f") % bestTotalDistance % bestTotalOverlap;
for (auto zone : zones) //finalize zone positions
{
zone.second->setPos(cords(zone.second->getCenter()));
zone.second->setPos (cords (bestSolution[zone.second]));
logGlobal->traceStream() << boost::format ("Placed zone %d at relative position %s and coordinates %s") % zone.first % zone.second->getCenter() % zone.second->getPos();
}
}

View File

@ -2919,7 +2919,7 @@ bool CGameHandler::buyArtifact( ObjectInstanceID hid, ArtifactID aid )
return false;
giveResource(hero->getOwner(),Res::GOLD,-GameConstants::SPELLBOOK_GOLD_COST);
giveHeroNewArtifact(hero, VLC->arth->artifacts[0], ArtifactPosition::SPELLBOOK);
giveHeroNewArtifact(hero, VLC->arth->artifacts[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
assert(hero->getArt(ArtifactPosition::SPELLBOOK));
giveSpells(town,hero);
return true;

View File

@ -142,6 +142,7 @@
</LinkTimeCodeGeneration>
<ShowProgress>NotSet</ShowProgress>
<AdditionalLibraryDirectories>..\..\libs;..</AdditionalLibraryDirectories>
<AdditionalOptions>/LTCG %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">

View File

@ -209,17 +209,13 @@ fi
if [[ -n "$data_dir" ]]
then
cp -r "$data_dir"/Data "$dest_dir"
cp -r "$data_dir"/Maps "$dest_dir"
# this folder is named differently from time to time
# vcmi can handle any case but script can't
if [ -d "$data_dir"/MP3 ]
then
cp -r "$data_dir"/MP3 "$dest_dir"
else
cp -r "$data_dir"/Mp3 "$dest_dir"
fi
# bash also has `shopt -s nocaseglob` but we don't want this to
# accidentally influence other parts of this script
# since the directory names are short, we use this pattern matching
cp -r "$data_dir"/[Dd][Aa][Tt][Aa] "$dest_dir"
cp -r "$data_dir"/[Mm][Aa][Pp][Ss] "$dest_dir"
cp -r "$data_dir"/[Mm][Pp]3 "$dest_dir"
fi
if [[ -n "$download" ]]