mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-12 02:28:11 +02:00
Merge pull request #2886 from IvanSavenko/h3m_parser_fixes
H3m parser fixes
This commit is contained in:
commit
455c13164e
@ -75,6 +75,46 @@
|
|||||||
"victoryIconIndex" : 11,
|
"victoryIconIndex" : 11,
|
||||||
"victoryString" : "core.vcdesc.0"
|
"victoryString" : "core.vcdesc.0"
|
||||||
},
|
},
|
||||||
|
"data/evil2:0" : { // A Gryphon's Heart
|
||||||
|
"defeatIconIndex" : 2,
|
||||||
|
"defeatString" : "core.lcdesc.3",
|
||||||
|
"triggeredEvents" : {
|
||||||
|
"specialDefeat" : {
|
||||||
|
"condition" : [
|
||||||
|
"allOf",
|
||||||
|
[ "isHuman", { "value" : 1 } ],
|
||||||
|
[ "daysPassed", { "value" : 84 } ]
|
||||||
|
],
|
||||||
|
"effect" : {
|
||||||
|
"messageToSend" : "core.genrltxt.5",
|
||||||
|
"type" : "defeat"
|
||||||
|
},
|
||||||
|
"message" : "core.genrltxt.254"
|
||||||
|
},
|
||||||
|
"specialVictory" : {
|
||||||
|
"condition" : [
|
||||||
|
"allOf",
|
||||||
|
[ "isHuman", { "value" : 1 } ],
|
||||||
|
[ "transport", { "position" : [ 16, 23, 0 ], "type" : 84 } ]
|
||||||
|
],
|
||||||
|
"effect" : {
|
||||||
|
"messageToSend" : "core.genrltxt.293",
|
||||||
|
"type" : "victory"
|
||||||
|
},
|
||||||
|
"message" : "core.genrltxt.292"
|
||||||
|
},
|
||||||
|
"standardDefeat" : {
|
||||||
|
"condition" : [ "daysWithoutTown", { "value" : 7 } ],
|
||||||
|
"effect" : {
|
||||||
|
"messageToSend" : "core.genrltxt.8",
|
||||||
|
"type" : "defeat"
|
||||||
|
},
|
||||||
|
"message" : "core.genrltxt.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"victoryIconIndex" : 10,
|
||||||
|
"victoryString" : "core.vcdesc.11"
|
||||||
|
},
|
||||||
"data/secret1:0" : { // The Grail
|
"data/secret1:0" : { // The Grail
|
||||||
"defeatIconIndex" : 2,
|
"defeatIconIndex" : 2,
|
||||||
"defeatString" : "core.lcdesc.3",
|
"defeatString" : "core.lcdesc.3",
|
||||||
|
@ -388,12 +388,11 @@ void MetaString::jsonDeserialize(const JsonNode & source)
|
|||||||
|
|
||||||
void MetaString::serializeJson(JsonSerializeFormat & handler)
|
void MetaString::serializeJson(JsonSerializeFormat & handler)
|
||||||
{
|
{
|
||||||
JsonNode attr;
|
|
||||||
if(handler.saving)
|
if(handler.saving)
|
||||||
jsonSerialize(attr);
|
jsonSerialize(const_cast<JsonNode&>(handler.getCurrent()));
|
||||||
handler.serializeRaw("attributes", attr, std::nullopt);
|
|
||||||
if(!handler.saving)
|
if(!handler.saving)
|
||||||
jsonDeserialize(attr);
|
jsonDeserialize(handler.getCurrent());
|
||||||
}
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -49,7 +49,7 @@ MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesROE()
|
|||||||
|
|
||||||
result.factionsCount = 8;
|
result.factionsCount = 8;
|
||||||
result.heroesCount = 128;
|
result.heroesCount = 128;
|
||||||
result.heroesPortraitsCount = 128;
|
result.heroesPortraitsCount = 130; // +General Kendal, +Catherine (portrait-only in RoE)
|
||||||
result.artifactsCount = 127;
|
result.artifactsCount = 127;
|
||||||
result.resourcesCount = 7;
|
result.resourcesCount = 7;
|
||||||
result.creaturesCount = 118;
|
result.creaturesCount = 118;
|
||||||
|
@ -44,6 +44,7 @@ static std::string convertMapName(std::string input)
|
|||||||
{
|
{
|
||||||
boost::algorithm::to_lower(input);
|
boost::algorithm::to_lower(input);
|
||||||
boost::algorithm::trim(input);
|
boost::algorithm::trim(input);
|
||||||
|
boost::algorithm::erase_all(input, ".");
|
||||||
|
|
||||||
size_t slashPos = input.find_last_of('/');
|
size_t slashPos = input.find_last_of('/');
|
||||||
|
|
||||||
@ -345,27 +346,11 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
|||||||
bool allowNormalVictory = reader->readBool();
|
bool allowNormalVictory = reader->readBool();
|
||||||
bool appliesToAI = reader->readBool();
|
bool appliesToAI = reader->readBool();
|
||||||
|
|
||||||
if(allowNormalVictory)
|
|
||||||
{
|
|
||||||
size_t playersOnMap = boost::range::count_if(
|
|
||||||
mapHeader->players,
|
|
||||||
[](const PlayerInfo & info)
|
|
||||||
{
|
|
||||||
return info.canAnyonePlay();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if(playersOnMap == 1)
|
|
||||||
{
|
|
||||||
logGlobal->warn("Map %s: Only one player exists, but normal victory allowed!", mapName);
|
|
||||||
allowNormalVictory = false; // makes sense? Not much. Works as H3? Yes!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(vicCondition)
|
switch(vicCondition)
|
||||||
{
|
{
|
||||||
case EVictoryConditionType::ARTIFACT:
|
case EVictoryConditionType::ARTIFACT:
|
||||||
{
|
{
|
||||||
|
assert(allowNormalVictory == true); // not selectable in editor
|
||||||
EventCondition cond(EventCondition::HAVE_ARTIFACT);
|
EventCondition cond(EventCondition::HAVE_ARTIFACT);
|
||||||
cond.objectType = reader->readArtifact();
|
cond.objectType = reader->readArtifact();
|
||||||
|
|
||||||
@ -404,6 +389,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
|||||||
}
|
}
|
||||||
case EVictoryConditionType::BUILDCITY:
|
case EVictoryConditionType::BUILDCITY:
|
||||||
{
|
{
|
||||||
|
assert(appliesToAI == true); // not selectable in editor
|
||||||
EventExpression::OperatorAll oper;
|
EventExpression::OperatorAll oper;
|
||||||
EventCondition cond(EventCondition::HAVE_BUILDING);
|
EventCondition cond(EventCondition::HAVE_BUILDING);
|
||||||
cond.position = reader->readInt3();
|
cond.position = reader->readInt3();
|
||||||
@ -421,6 +407,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
|||||||
}
|
}
|
||||||
case EVictoryConditionType::BUILDGRAIL:
|
case EVictoryConditionType::BUILDGRAIL:
|
||||||
{
|
{
|
||||||
|
assert(allowNormalVictory == true); // not selectable in editor
|
||||||
|
assert(appliesToAI == true); // not selectable in editor
|
||||||
EventCondition cond(EventCondition::HAVE_BUILDING);
|
EventCondition cond(EventCondition::HAVE_BUILDING);
|
||||||
cond.objectType = BuildingID::GRAIL;
|
cond.objectType = BuildingID::GRAIL;
|
||||||
cond.position = reader->readInt3();
|
cond.position = reader->readInt3();
|
||||||
@ -436,6 +424,10 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
|||||||
}
|
}
|
||||||
case EVictoryConditionType::BEATHERO:
|
case EVictoryConditionType::BEATHERO:
|
||||||
{
|
{
|
||||||
|
if (!allowNormalVictory)
|
||||||
|
logGlobal->warn("Map %s: Has 'beat hero' as victory condition, but 'allow normal victory' not set. Ignoring", mapName);
|
||||||
|
allowNormalVictory = true; // H3 behavior
|
||||||
|
assert(appliesToAI == false); // not selectable in editor
|
||||||
EventCondition cond(EventCondition::DESTROY);
|
EventCondition cond(EventCondition::DESTROY);
|
||||||
cond.objectType = Obj::HERO;
|
cond.objectType = Obj::HERO;
|
||||||
cond.position = reader->readInt3();
|
cond.position = reader->readInt3();
|
||||||
@ -462,6 +454,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
|||||||
}
|
}
|
||||||
case EVictoryConditionType::BEATMONSTER:
|
case EVictoryConditionType::BEATMONSTER:
|
||||||
{
|
{
|
||||||
|
assert(appliesToAI == true); // not selectable in editor
|
||||||
EventCondition cond(EventCondition::DESTROY);
|
EventCondition cond(EventCondition::DESTROY);
|
||||||
cond.objectType = Obj::MONSTER;
|
cond.objectType = Obj::MONSTER;
|
||||||
cond.position = reader->readInt3();
|
cond.position = reader->readInt3();
|
||||||
@ -500,6 +493,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
|||||||
}
|
}
|
||||||
case EVictoryConditionType::TRANSPORTITEM:
|
case EVictoryConditionType::TRANSPORTITEM:
|
||||||
{
|
{
|
||||||
|
assert(allowNormalVictory == true); // not selectable in editor
|
||||||
EventCondition cond(EventCondition::TRANSPORT);
|
EventCondition cond(EventCondition::TRANSPORT);
|
||||||
cond.objectType = reader->readUInt8();
|
cond.objectType = reader->readUInt8();
|
||||||
cond.position = reader->readInt3();
|
cond.position = reader->readInt3();
|
||||||
@ -513,6 +507,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
|||||||
}
|
}
|
||||||
case EVictoryConditionType::HOTA_ELIMINATE_ALL_MONSTERS:
|
case EVictoryConditionType::HOTA_ELIMINATE_ALL_MONSTERS:
|
||||||
{
|
{
|
||||||
|
assert(appliesToAI == false); // not selectable in editor
|
||||||
EventCondition cond(EventCondition::DESTROY);
|
EventCondition cond(EventCondition::DESTROY);
|
||||||
cond.objectType = Obj::MONSTER;
|
cond.objectType = Obj::MONSTER;
|
||||||
|
|
||||||
@ -526,6 +521,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
|||||||
}
|
}
|
||||||
case EVictoryConditionType::HOTA_SURVIVE_FOR_DAYS:
|
case EVictoryConditionType::HOTA_SURVIVE_FOR_DAYS:
|
||||||
{
|
{
|
||||||
|
assert(appliesToAI == false); // not selectable in editor
|
||||||
EventCondition cond(EventCondition::DAYS_PASSED);
|
EventCondition cond(EventCondition::DAYS_PASSED);
|
||||||
cond.value = reader->readUInt32();
|
cond.value = reader->readUInt32();
|
||||||
|
|
||||||
@ -541,6 +537,24 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
|||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(allowNormalVictory)
|
||||||
|
{
|
||||||
|
size_t playersOnMap = boost::range::count_if(
|
||||||
|
mapHeader->players,
|
||||||
|
[](const PlayerInfo & info)
|
||||||
|
{
|
||||||
|
return info.canAnyonePlay();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(playersOnMap > 1);
|
||||||
|
if(playersOnMap == 1)
|
||||||
|
{
|
||||||
|
logGlobal->warn("Map %s: Only one player exists, but normal victory allowed!", mapName);
|
||||||
|
allowNormalVictory = false; // makes sense? Not much. Works as H3? Yes!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if condition is human-only turn it into following construction: AllOf(human, condition)
|
// if condition is human-only turn it into following construction: AllOf(human, condition)
|
||||||
if(!appliesToAI)
|
if(!appliesToAI)
|
||||||
{
|
{
|
||||||
@ -883,7 +897,7 @@ void CMapLoaderH3M::loadArtifactsOfHero(CGHeroInstance * hero)
|
|||||||
|
|
||||||
if(!hero->artifactsWorn.empty() || !hero->artifactsInBackpack.empty())
|
if(!hero->artifactsWorn.empty() || !hero->artifactsInBackpack.empty())
|
||||||
{
|
{
|
||||||
logGlobal->warn("Hero %s at %s has set artifacts twice (in map properties and on adventure map instance). Using the latter set...", hero->getNameTranslated(), hero->pos.toString());
|
logGlobal->debug("Hero %s at %s has set artifacts twice (in map properties and on adventure map instance). Using the latter set...", hero->getNameTranslated(), hero->pos.toString());
|
||||||
|
|
||||||
hero->artifactsInBackpack.clear();
|
hero->artifactsInBackpack.clear();
|
||||||
while(!hero->artifactsWorn.empty())
|
while(!hero->artifactsWorn.empty())
|
||||||
@ -1551,6 +1565,9 @@ void CMapLoaderH3M::readObjects()
|
|||||||
newObject->appearance = objectTemplate;
|
newObject->appearance = objectTemplate;
|
||||||
assert(objectInstanceID == ObjectInstanceID((si32)map->objects.size()));
|
assert(objectInstanceID == ObjectInstanceID((si32)map->objects.size()));
|
||||||
|
|
||||||
|
if (newObject->isVisitable() && !map->isInTheMap(newObject->visitablePos()))
|
||||||
|
logGlobal->error("Map '%s': Object at %s - outside of map borders!", mapName, mapPosition.toString());
|
||||||
|
|
||||||
{
|
{
|
||||||
//TODO: define valid typeName and subtypeName for H3M maps
|
//TODO: define valid typeName and subtypeName for H3M maps
|
||||||
//boost::format fmt("%s_%d");
|
//boost::format fmt("%s_%d");
|
||||||
@ -1703,7 +1720,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
|
|||||||
if(!object->secSkills.empty())
|
if(!object->secSkills.empty())
|
||||||
{
|
{
|
||||||
if(object->secSkills[0].first != SecondarySkill::DEFAULT)
|
if(object->secSkills[0].first != SecondarySkill::DEFAULT)
|
||||||
logGlobal->warn("Hero %s subID=%d has set secondary skills twice (in map properties and on adventure map instance). Using the latter set...", object->getNameTextID(), object->subID);
|
logGlobal->debug("Map '%s': Hero %s subID=%d has set secondary skills twice (in map properties and on adventure map instance). Using the latter set...", mapName, object->getNameTextID(), object->subID);
|
||||||
object->secSkills.clear();
|
object->secSkills.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1775,7 +1792,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
|
|||||||
auto ps = object->getAllBonuses(Selector::type()(BonusType::PRIMARY_SKILL).And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL)), nullptr);
|
auto ps = object->getAllBonuses(Selector::type()(BonusType::PRIMARY_SKILL).And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL)), nullptr);
|
||||||
if(ps->size())
|
if(ps->size())
|
||||||
{
|
{
|
||||||
logGlobal->warn("Hero %s subID=%d has set primary skills twice (in map properties and on adventure map instance). Using the latter set...", object->getNameTranslated(), object->subID );
|
logGlobal->debug("Hero %s subID=%d has set primary skills twice (in map properties and on adventure map instance). Using the latter set...", object->getNameTranslated(), object->subID );
|
||||||
for(const auto & b : *ps)
|
for(const auto & b : *ps)
|
||||||
object->removeBonus(b);
|
object->removeBonus(b);
|
||||||
}
|
}
|
||||||
@ -1907,8 +1924,7 @@ void CMapLoaderH3M::readSeerHutQuest(CGSeerHut * hut, const int3 & position, con
|
|||||||
auto rVal = reader->readUInt32();
|
auto rVal = reader->readUInt32();
|
||||||
|
|
||||||
assert(rId < features.resourcesCount);
|
assert(rId < features.resourcesCount);
|
||||||
assert((rVal & 0x00ffffff) == rVal);
|
|
||||||
|
|
||||||
reward.resources[rId] = rVal;
|
reward.resources[rId] = rVal;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2172,7 +2188,7 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
|
|||||||
|
|
||||||
uint8_t alignment = reader->readUInt8();
|
uint8_t alignment = reader->readUInt8();
|
||||||
|
|
||||||
if(alignment != PlayerColor::NEUTRAL.getNum())
|
if(alignment != 255)
|
||||||
{
|
{
|
||||||
if(alignment < PlayerColor::PLAYER_LIMIT.getNum())
|
if(alignment < PlayerColor::PLAYER_LIMIT.getNum())
|
||||||
{
|
{
|
||||||
|
@ -870,6 +870,11 @@ void CMapPatcher::readPatchData()
|
|||||||
{
|
{
|
||||||
JsonDeserializer handler(mapObjectResolver.get(), input);
|
JsonDeserializer handler(mapObjectResolver.get(), input);
|
||||||
readTriggeredEvents(handler);
|
readTriggeredEvents(handler);
|
||||||
|
|
||||||
|
handler.serializeInt("defeatIconIndex", mapHeader->defeatIconIndex);
|
||||||
|
handler.serializeInt("victoryIconIndex", mapHeader->victoryIconIndex);
|
||||||
|
handler.serializeStruct("victoryString", mapHeader->victoryMessage);
|
||||||
|
handler.serializeStruct("defeatString", mapHeader->defeatMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
///CMapLoaderJson
|
///CMapLoaderJson
|
||||||
|
@ -53,11 +53,11 @@ void MapIdentifiersH3M::loadMapping(const JsonNode & mapping)
|
|||||||
|
|
||||||
for (auto entryTemplate : mapping["templates"].Struct())
|
for (auto entryTemplate : mapping["templates"].Struct())
|
||||||
{
|
{
|
||||||
std::string h3mName = boost::to_lower_copy(entryTemplate.second.String());
|
AnimationPath h3mName = AnimationPath::builtinTODO(entryTemplate.second.String());
|
||||||
std::string vcmiName = boost::to_lower_copy(entryTemplate.first);
|
AnimationPath vcmiName = AnimationPath::builtinTODO(entryTemplate.first);
|
||||||
|
|
||||||
if (!CResourceHandler::get()->existsResource(AnimationPath::builtinTODO("SPRITES/" + vcmiName)))
|
if (!CResourceHandler::get()->existsResource(vcmiName.addPrefix("SPRITES/")))
|
||||||
logMod->warn("Template animation file %s was not found!", vcmiName);
|
logMod->warn("Template animation file %s was not found!", vcmiName.getOriginalName());
|
||||||
|
|
||||||
mappingObjectTemplate[h3mName] = vcmiName;
|
mappingObjectTemplate[h3mName] = vcmiName;
|
||||||
}
|
}
|
||||||
@ -108,10 +108,10 @@ void MapIdentifiersH3M::loadMapping(const JsonNode & mapping)
|
|||||||
|
|
||||||
void MapIdentifiersH3M::remapTemplate(ObjectTemplate & objectTemplate)
|
void MapIdentifiersH3M::remapTemplate(ObjectTemplate & objectTemplate)
|
||||||
{
|
{
|
||||||
std::string name = boost::to_lower_copy(objectTemplate.animationFile.getName());
|
auto name = objectTemplate.animationFile;
|
||||||
|
|
||||||
if (mappingObjectTemplate.count(name))
|
if (mappingObjectTemplate.count(name))
|
||||||
objectTemplate.animationFile = AnimationPath::builtinTODO(mappingObjectTemplate.at(name));
|
objectTemplate.animationFile = mappingObjectTemplate.at(name);
|
||||||
|
|
||||||
ObjectTypeIdentifier objectType{ objectTemplate.id, objectTemplate.subid};
|
ObjectTypeIdentifier objectType{ objectTemplate.id, objectTemplate.subid};
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../GameConstants.h"
|
#include "../GameConstants.h"
|
||||||
|
#include "../filesystem/ResourcePath.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ class MapIdentifiersH3M
|
|||||||
std::map<ArtifactID, ArtifactID> mappingArtifact;
|
std::map<ArtifactID, ArtifactID> mappingArtifact;
|
||||||
std::map<SecondarySkill, SecondarySkill> mappingSecondarySkill;
|
std::map<SecondarySkill, SecondarySkill> mappingSecondarySkill;
|
||||||
|
|
||||||
std::map<std::string, std::string> mappingObjectTemplate;
|
std::map<AnimationPath, AnimationPath> mappingObjectTemplate;
|
||||||
std::map<ObjectTypeIdentifier, ObjectTypeIdentifier> mappingObjectIndex;
|
std::map<ObjectTypeIdentifier, ObjectTypeIdentifier> mappingObjectIndex;
|
||||||
|
|
||||||
template<typename IdentifierID>
|
template<typename IdentifierID>
|
||||||
|
@ -113,7 +113,12 @@ int32_t MapReaderH3M::readHeroPortrait()
|
|||||||
if(result.getNum() == features.heroIdentifierInvalid)
|
if(result.getNum() == features.heroIdentifierInvalid)
|
||||||
return int32_t(-1);
|
return int32_t(-1);
|
||||||
|
|
||||||
assert(result.getNum() < features.heroesPortraitsCount);
|
if (result.getNum() >= features.heroesPortraitsCount)
|
||||||
|
{
|
||||||
|
logGlobal->warn("Map contains invalid hero portrait ID %d. Will be reset!", result.getNum() );
|
||||||
|
return int32_t(-1);
|
||||||
|
}
|
||||||
|
|
||||||
return remapper.remapPortrrait(result);
|
return remapper.remapPortrrait(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +204,12 @@ PlayerColor MapReaderH3M::readPlayer()
|
|||||||
if (value == 255)
|
if (value == 255)
|
||||||
return PlayerColor::NEUTRAL;
|
return PlayerColor::NEUTRAL;
|
||||||
|
|
||||||
assert(value < PlayerColor::PLAYER_LIMIT_I);
|
if (value >= PlayerColor::PLAYER_LIMIT_I)
|
||||||
|
{
|
||||||
|
logGlobal->warn("Map contains invalid player ID %d. Will be reset!", value );
|
||||||
|
return PlayerColor::NEUTRAL;
|
||||||
|
}
|
||||||
|
|
||||||
return PlayerColor(value);
|
return PlayerColor(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +220,12 @@ PlayerColor MapReaderH3M::readPlayer32()
|
|||||||
if (value == 255)
|
if (value == 255)
|
||||||
return PlayerColor::NEUTRAL;
|
return PlayerColor::NEUTRAL;
|
||||||
|
|
||||||
assert(value < PlayerColor::PLAYER_LIMIT_I);
|
if (value >= PlayerColor::PLAYER_LIMIT_I)
|
||||||
|
{
|
||||||
|
logGlobal->warn("Map contains invalid player ID %d. Will be reset!", value );
|
||||||
|
return PlayerColor::NEUTRAL;
|
||||||
|
}
|
||||||
|
|
||||||
return PlayerColor(value);
|
return PlayerColor(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user