1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-27 22:49:25 +02:00

* WIP on event condition format

* Hero portrait serialization
* Fix town spells serialization

* Added support for float exponential part in Json
* Added support for int64 in Json
* Added basic Hero definitions serialization
* Added rumors serialization
* Advanced player info serialization.
* Added Disposed heroes serialization, (!) not covered with tests yet
* Added Local event serialization
* Added Pandoras box serialization
* Added Seer hut reward serialization
* Added CQuest serialization
* Added API for map object instance names serialization.
* Added random dwelling options serialization
* Advanced town options serialization
* Advanced hero options serialization
* More map format tests
* A lot of fixes, cleanup and refactoring
This commit is contained in:
AlexVinS
2016-11-13 13:38:42 +03:00
parent 5127061e28
commit a85b4cf2a5
70 changed files with 18728 additions and 687 deletions

View File

@@ -20,6 +20,12 @@
#include "MiscObjects.h"
#include "../IGameCallback.h"
#include "../CGameState.h"
#include "../serializer/JsonSerializeFormat.h"
#include "../CModHandler.h"
#include "../GameConstants.h"
#include "../StringConstants.h"
#include "../spells/CSpellHandler.h"
std::map <PlayerColor, std::set <ui8> > CGKeys::playerKeyMap;
@@ -395,6 +401,86 @@ void CQuest::getCompletionText(MetaString &iwText, std::vector<Component> &compo
}
}
void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName)
{
auto q = handler.enterStruct(fieldName);
handler.serializeString("firstVisitText", firstVisitText);
handler.serializeString("nextVisitText", nextVisitText);
handler.serializeString("completedText", completedText);
if(!handler.saving)
{
isCustomFirst = firstVisitText.size() > 0;
isCustomNext = nextVisitText.size() > 0;
isCustomComplete = completedText.size() > 0;
}
static const std::vector<std::string> MISSION_TYPE_JSON =
{
"None", "Level", "PrimaryStat", "KillHero", "KillCreature", "Artifact", "Army", "Resources", "Hero", "Player"
};
handler.serializeEnum("missionType", missionType, Emission::MISSION_NONE, MISSION_TYPE_JSON);
handler.serializeInt("timeLimit", lastDay, -1);
switch (missionType)
{
case MISSION_NONE:
break;
case MISSION_LEVEL:
handler.serializeInt("heroLevel", m13489val, -1);
break;
case MISSION_PRIMARY_STAT:
{
auto primarySkills = handler.enterStruct("primarySkills");
if(!handler.saving)
m2stats.resize(GameConstants::PRIMARY_SKILLS);
for(int i = 0; i < GameConstants::PRIMARY_SKILLS; ++i)
handler.serializeInt(PrimarySkill::names[i], m2stats[i], 0);
}
break;
case MISSION_KILL_HERO:
case MISSION_KILL_CREATURE:
handler.serializeInstance<ui32>("killTarget", m13489val, ui32(-1));
break;
case MISSION_ART:
//todo: ban artifacts
handler.serializeIdArray("artifacts", m5arts, &CArtHandler::decodeArfifact, &CArtHandler::encodeArtifact);
break;
case MISSION_ARMY:
{
auto a = handler.enterArray("creatures");
a.serializeStruct(m6creatures);
}
break;
case MISSION_RESOURCES:
{
auto r = handler.enterStruct("resources");
if(!handler.saving)
m7resources.resize(GameConstants::RESOURCE_QUANTITY-1);
for(size_t idx = 0; idx < (GameConstants::RESOURCE_QUANTITY - 1); idx++)
{
handler.serializeInt(GameConstants::RESOURCE_NAMES[idx], m7resources[idx], 0);
}
}
break;
case MISSION_HERO:
handler.serializeId<ui32>("hero", m13489val, 0, &CHeroHandler::decodeHero, &CHeroHandler::encodeHero);
break;
case MISSION_PLAYER:
handler.serializeEnum("player", m13489val, PlayerColor::CANNOT_DETERMINE.getNum(), GameConstants::PLAYER_COLOR_NAMES);
break;
default:
logGlobal->error("Invalid quest mission type");
break;
}
}
CGSeerHut::CGSeerHut() : IQuestObject(),
rewardType(NOTHING), rID(-1), rVal(-1)
{
@@ -763,6 +849,165 @@ void CGSeerHut::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
finishQuest(hero, answer);
}
void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler)
{
static const std::map<ERewardType, std::string> REWARD_MAP =
{
{NOTHING, ""},
{EXPERIENCE, "experience"},
{MANA_POINTS, "mana"},
{MORALE_BONUS, "morale"},
{LUCK_BONUS, "luck"},
{RESOURCES, "resource"},
{PRIMARY_SKILL, "primarySkill"},
{SECONDARY_SKILL,"secondarySkill"},
{ARTIFACT, "artifact"},
{SPELL, "spell"},
{CREATURE, "creature"}
};
static const std::map<std::string, ERewardType> REWARD_RMAP =
{
{"experience", EXPERIENCE},
{"mana", MANA_POINTS},
{"morale", MORALE_BONUS},
{"luck", LUCK_BONUS},
{"resource", RESOURCES},
{"primarySkill", PRIMARY_SKILL},
{"secondarySkill",SECONDARY_SKILL},
{"artifact", ARTIFACT},
{"spell", SPELL},
{"creature", CREATURE}
};
//quest and reward
quest->serializeJson(handler, "quest");
//only one reward is supported
//todo: full reward format support after CRewardInfo integration
auto s = handler.enterStruct("reward");
std::string fullIdentifier, metaTypeName, scope, identifier;
if(handler.saving)
{
si32 amount = rVal;
metaTypeName = REWARD_MAP.at(rewardType);
switch (rewardType)
{
case NOTHING:
break;
case EXPERIENCE:
case MANA_POINTS:
case MORALE_BONUS:
case LUCK_BONUS:
identifier = "";
break;
case RESOURCES:
identifier = GameConstants::RESOURCE_NAMES[rID];
break;
case PRIMARY_SKILL:
identifier = PrimarySkill::names[rID];
break;
case SECONDARY_SKILL:
identifier = NSecondarySkill::names[rID];
break;
case ARTIFACT:
identifier = ArtifactID(rID).toArtifact()->identifier;
amount = 1;
break;
case SPELL:
identifier = SpellID(rID).toSpell()->identifier;
amount = 1;
break;
case CREATURE:
identifier = CreatureID(rID).toCreature()->identifier;
break;
default:
assert(false);
break;
}
if(rewardType != NOTHING)
{
fullIdentifier = CModHandler::makeFullIdentifier(scope, metaTypeName, identifier);
handler.serializeInt(fullIdentifier, amount);
}
}
else
{
rewardType = NOTHING;
const JsonNode & rewardsJson = handler.getCurrent();
fullIdentifier = "";
if(rewardsJson.Struct().empty())
return;
else
{
auto iter = rewardsJson.Struct().begin();
fullIdentifier = iter->first;
}
CModHandler::parseIdentifier(fullIdentifier, scope, metaTypeName, identifier);
auto it = REWARD_RMAP.find(metaTypeName);
if(it == REWARD_RMAP.end())
{
logGlobal->errorStream() << instanceName << ": invalid metatype in reward item " << fullIdentifier;
return;
}
else
{
rewardType = it->second;
}
bool doRequest = false;
switch (rewardType)
{
case NOTHING:
return;
case EXPERIENCE:
case MANA_POINTS:
case MORALE_BONUS:
case LUCK_BONUS:
break;
case PRIMARY_SKILL:
doRequest = true;
break;
case RESOURCES:
case SECONDARY_SKILL:
case ARTIFACT:
case SPELL:
case CREATURE:
doRequest = true;
break;
default:
assert(false);
break;
}
if(doRequest)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", fullIdentifier, false);
if(rawId)
{
rID = rawId.get();
}
else
{
rewardType = NOTHING;//fallback in case of error
return;
}
}
handler.serializeInt(fullIdentifier, rVal);
}
}
void CGQuestGuard::init(CRandomGenerator & rand)
{
blockVisit = true;
@@ -775,6 +1020,12 @@ void CGQuestGuard::completeQuest(const CGHeroInstance *h) const
cb->removeObject(this);
}
void CGQuestGuard::serializeJsonOptions(JsonSerializeFormat & handler)
{
//quest only, do not call base class
quest->serializeJson(handler, "quest");
}
void CGKeys::reset()
{
playerKeyMap.clear();