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

Win/loss conditions now use VariantIdentifier. Removed non-implemented

options
This commit is contained in:
Ivan Savenko
2023-11-08 17:35:17 +02:00
parent 13763cad8e
commit 96c81be68e
15 changed files with 108 additions and 229 deletions

View File

@@ -173,11 +173,6 @@ TSubgoal Win::whatToDoToAchieve()
case EventCondition::CONST_VALUE: case EventCondition::CONST_VALUE:
break; break;
case EventCondition::HAVE_0:
case EventCondition::HAVE_BUILDING_0:
case EventCondition::DESTROY_0:
//TODO: support new condition format
return sptr(Conquer());
default: default:
assert(0); assert(0);
} }

View File

@@ -417,6 +417,21 @@ class BuildingID : public IdentifierWithEnum<BuildingID, BuildingIDBase>
{ {
public: public:
using IdentifierWithEnum<BuildingID, BuildingIDBase>::IdentifierWithEnum; using IdentifierWithEnum<BuildingID, BuildingIDBase>::IdentifierWithEnum;
static BuildingID TOWN_HALL_LEVEL(uint level)
{
assert(level < 4);
return BuildingID(Type::TOWN_HALL + level);
}
static BuildingID FORT_LEVEL(uint level)
{
assert(level < 3);
return BuildingID(Type::TOWN_HALL + level);
}
static std::string encode(int32_t index);
static si32 decode(const std::string & identifier);
}; };
class MapObjectBaseID : public IdentifierBase class MapObjectBaseID : public IdentifierBase

View File

@@ -17,9 +17,10 @@ VCMI_LIB_NAMESPACE_BEGIN
template<typename... Types> template<typename... Types>
class VariantIdentifier class VariantIdentifier
{ {
std::variant<Types...> value; using Type = std::variant<Types...>;
public: Type value;
public:
VariantIdentifier() VariantIdentifier()
{} {}

View File

@@ -1380,7 +1380,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
case EventCondition::HAVE_ARTIFACT: //check if any hero has winning artifact case EventCondition::HAVE_ARTIFACT: //check if any hero has winning artifact
{ {
for(const auto & elem : p->heroes) for(const auto & elem : p->heroes)
if(elem->hasArt(ArtifactID(condition.objectType))) if(elem->hasArt(condition.objectType.as<ArtifactID>()))
return true; return true;
return false; return false;
} }
@@ -1396,7 +1396,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
&& (ai = dynamic_cast<const CArmedInstance *>(object.get()))) //contains army && (ai = dynamic_cast<const CArmedInstance *>(object.get()))) //contains army
{ {
for(const auto & elem : ai->Slots()) //iterate through army for(const auto & elem : ai->Slots()) //iterate through army
if(elem.second->type->getIndex() == condition.objectType) //it's searched creature if(elem.second->getId() == condition.objectType.as<CreatureID>()) //it's searched creature
total += elem.second->count; total += elem.second->count;
} }
} }
@@ -1404,20 +1404,20 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
} }
case EventCondition::HAVE_RESOURCES: case EventCondition::HAVE_RESOURCES:
{ {
return p->resources[condition.objectType] >= condition.value; return p->resources[condition.objectType.as<GameResID>()] >= condition.value;
} }
case EventCondition::HAVE_BUILDING: case EventCondition::HAVE_BUILDING:
{ {
if (condition.object) // specific town if (condition.object) // specific town
{ {
const auto * t = dynamic_cast<const CGTownInstance *>(condition.object); const auto * t = dynamic_cast<const CGTownInstance *>(condition.object);
return (t->tempOwner == player && t->hasBuilt(BuildingID(condition.objectType))); return (t->tempOwner == player && t->hasBuilt(condition.objectType.as<BuildingID>()));
} }
else // any town else // any town
{ {
for (const CGTownInstance * t : p->towns) for (const CGTownInstance * t : p->towns)
{ {
if (t->hasBuilt(BuildingID(condition.objectType))) if (t->hasBuilt(condition.objectType.as<BuildingID>()))
return true; return true;
} }
return false; return false;
@@ -1436,7 +1436,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
{ {
for(const auto & elem : map->objects) // mode B - destroy all objects of this type for(const auto & elem : map->objects) // mode B - destroy all objects of this type
{ {
if(elem && elem->ID.getNum() == condition.objectType) if(elem && elem->ID == condition.objectType.as<MapObjectID>())
return false; return false;
} }
return true; return true;
@@ -1457,7 +1457,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
for(const auto & elem : map->objects) // mode B - flag all objects of this type for(const auto & elem : map->objects) // mode B - flag all objects of this type
{ {
//check not flagged objs //check not flagged objs
if ( elem && elem->ID.getNum() == condition.objectType && team.count(elem->tempOwner) == 0 ) if ( elem && elem->ID == condition.objectType.as<MapObjectID>() && team.count(elem->tempOwner) == 0 )
return false; return false;
} }
return true; return true;
@@ -1466,8 +1466,8 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
case EventCondition::TRANSPORT: case EventCondition::TRANSPORT:
{ {
const auto * t = dynamic_cast<const CGTownInstance *>(condition.object); const auto * t = dynamic_cast<const CGTownInstance *>(condition.object);
return (t->visitingHero && t->visitingHero->hasArt(ArtifactID(condition.objectType))) || return (t->visitingHero && t->visitingHero->hasArt(condition.objectType.as<ArtifactID>())) ||
(t->garrisonHero && t->garrisonHero->hasArt(ArtifactID(condition.objectType))); (t->garrisonHero && t->garrisonHero->hasArt(condition.objectType.as<ArtifactID>()));
} }
case EventCondition::DAYS_PASSED: case EventCondition::DAYS_PASSED:
{ {
@@ -1488,24 +1488,6 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
{ {
return condition.value; // just convert to bool return condition.value; // just convert to bool
} }
case EventCondition::HAVE_0:
{
logGlobal->debug("Not implemented event condition type: %d", (int)condition.condition);
//TODO: support new condition format
return false;
}
case EventCondition::HAVE_BUILDING_0:
{
logGlobal->debug("Not implemented event condition type: %d", (int)condition.condition);
//TODO: support new condition format
return false;
}
case EventCondition::DESTROY_0:
{
logGlobal->debug("Not implemented event condition type: %d", (int)condition.condition);
//TODO: support new condition format
return false;
}
default: default:
logGlobal->error("Invalid event condition type: %d", (int)condition.condition); logGlobal->error("Invalid event condition type: %d", (int)condition.condition);
return false; return false;
@@ -1789,13 +1771,13 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
{ {
if(playerInactive(player.second.color)) //do nothing for neutral player if(playerInactive(player.second.color)) //do nothing for neutral player
continue; continue;
int bestCre = -1; //best creature's ID CreatureID bestCre; //best creature's ID
for(const auto & elem : player.second.heroes) for(const auto & elem : player.second.heroes)
{ {
for(const auto & it : elem->Slots()) for(const auto & it : elem->Slots())
{ {
int toCmp = it.second->type->getId(); //ID of creature we should compare with the best one CreatureID toCmp = it.second->type->getId(); //ID of creature we should compare with the best one
if(bestCre == -1 || VLC->creh->objects[bestCre]->getAIValue() < VLC->creh->objects[toCmp]->getAIValue()) if(bestCre == -1 || bestCre.toEntity(VLC)->getAIValue() < toCmp.toEntity(VLC)->getAIValue())
{ {
bestCre = toCmp; bestCre = toCmp;
} }

View File

@@ -1778,7 +1778,7 @@ bool CGHeroInstance::isMissionCritical() const
auto const & testFunctor = [&](const EventCondition & condition) auto const & testFunctor = [&](const EventCondition & condition)
{ {
if ((condition.condition == EventCondition::CONTROL || condition.condition == EventCondition::HAVE_0) && condition.object) if ((condition.condition == EventCondition::CONTROL) && condition.object)
{ {
const auto * hero = dynamic_cast<const CGHeroInstance *>(condition.object); const auto * hero = dynamic_cast<const CGHeroInstance *>(condition.object);
return (hero != this); return (hero != this);

View File

@@ -1149,7 +1149,7 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
for(const BuildingID & id : forbiddenBuildings) for(const BuildingID & id : forbiddenBuildings)
{ {
buildingsLIC.none.insert(id); buildingsLIC.none.insert(id.getNum());
customBuildings = true; customBuildings = true;
} }
@@ -1166,7 +1166,7 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
if(id == BuildingID::FORT) if(id == BuildingID::FORT)
hasFort = true; hasFort = true;
buildingsLIC.all.insert(id); buildingsLIC.all.insert(id.getNum());
customBuildings = true; customBuildings = true;
} }

View File

@@ -79,7 +79,7 @@ void CCastleEvent::serializeJson(JsonSerializeFormat & handler)
a.syncSize(temp); a.syncSize(temp);
for(int i = 0; i < temp.size(); ++i) for(int i = 0; i < temp.size(); ++i)
{ {
a.serializeInt(i, temp[i]); a.serializeInt(i, temp[i].getNum());
buildings.insert(temp[i]); buildings.insert(temp[i]);
} }
} }
@@ -429,16 +429,16 @@ void CMap::checkForObjectives()
switch (cond.condition) switch (cond.condition)
{ {
case EventCondition::HAVE_ARTIFACT: case EventCondition::HAVE_ARTIFACT:
event.onFulfill.replaceTextID(VLC->artifacts()->getByIndex(cond.objectType)->getNameTextID()); event.onFulfill.replaceTextID(cond.objectType.as<ArtifactID>().toEntity(VLC)->getNameTextID());
break; break;
case EventCondition::HAVE_CREATURES: case EventCondition::HAVE_CREATURES:
event.onFulfill.replaceTextID(VLC->creatures()->getByIndex(cond.objectType)->getNameSingularTextID()); event.onFulfill.replaceTextID(cond.objectType.as<CreatureID>().toEntity(VLC)->getNameSingularTextID());
event.onFulfill.replaceNumber(cond.value); event.onFulfill.replaceNumber(cond.value);
break; break;
case EventCondition::HAVE_RESOURCES: case EventCondition::HAVE_RESOURCES:
event.onFulfill.replaceLocalString(EMetaText::RES_NAMES, cond.objectType); event.onFulfill.replaceName(cond.objectType.as<GameResID>());
event.onFulfill.replaceNumber(cond.value); event.onFulfill.replaceNumber(cond.value);
break; break;
@@ -449,7 +449,7 @@ void CMap::checkForObjectives()
case EventCondition::CONTROL: case EventCondition::CONTROL:
if (isInTheMap(cond.position)) if (isInTheMap(cond.position))
cond.object = getObjectiveObjectFrom(cond.position, static_cast<Obj>(cond.objectType)); cond.object = getObjectiveObjectFrom(cond.position, cond.objectType.as<MapObjectID>());
if (cond.object) if (cond.object)
{ {
@@ -464,7 +464,7 @@ void CMap::checkForObjectives()
case EventCondition::DESTROY: case EventCondition::DESTROY:
if (isInTheMap(cond.position)) if (isInTheMap(cond.position))
cond.object = getObjectiveObjectFrom(cond.position, static_cast<Obj>(cond.objectType)); cond.object = getObjectiveObjectFrom(cond.position, cond.objectType.as<MapObjectID>());
if (cond.object) if (cond.object)
{ {
@@ -480,14 +480,6 @@ void CMap::checkForObjectives()
//break; case EventCondition::IS_HUMAN: //break; case EventCondition::IS_HUMAN:
//break; case EventCondition::DAYS_WITHOUT_TOWN: //break; case EventCondition::DAYS_WITHOUT_TOWN:
//break; case EventCondition::STANDARD_WIN: //break; case EventCondition::STANDARD_WIN:
//TODO: support new condition format
case EventCondition::HAVE_0:
break;
case EventCondition::DESTROY_0:
break;
case EventCondition::HAVE_BUILDING_0:
break;
} }
return cond; return cond;
}; };

View File

@@ -69,21 +69,16 @@ bool PlayerInfo::hasCustomMainHero() const
EventCondition::EventCondition(EWinLoseType condition): EventCondition::EventCondition(EWinLoseType condition):
object(nullptr), object(nullptr),
metaType(EMetaclass::INVALID),
value(-1), value(-1),
objectType(-1),
objectSubtype(-1),
position(-1, -1, -1), position(-1, -1, -1),
condition(condition) condition(condition)
{ {
} }
EventCondition::EventCondition(EWinLoseType condition, si32 value, si32 objectType, const int3 & position): EventCondition::EventCondition(EWinLoseType condition, si32 value, TargetTypeID objectType, const int3 & position):
object(nullptr), object(nullptr),
metaType(EMetaclass::INVALID),
value(value), value(value),
objectType(objectType), objectType(objectType),
objectSubtype(-1),
position(position), position(position),
condition(condition) condition(condition)
{} {}

View File

@@ -10,6 +10,7 @@
#pragma once #pragma once
#include "../constants/VariantIdentifier.h"
#include "../modding/CModInfo.h" #include "../modding/CModInfo.h"
#include "../LogicalExpression.h" #include "../LogicalExpression.h"
#include "../int3.h" #include "../int3.h"
@@ -99,7 +100,6 @@ struct DLL_LINKAGE PlayerInfo
struct DLL_LINKAGE EventCondition struct DLL_LINKAGE EventCondition
{ {
enum EWinLoseType { enum EWinLoseType {
//internal use, deprecated
HAVE_ARTIFACT, // type - required artifact HAVE_ARTIFACT, // type - required artifact
HAVE_CREATURES, // type - creatures to collect, value - amount to collect HAVE_CREATURES, // type - creatures to collect, value - amount to collect
HAVE_RESOURCES, // type - resource ID, value - amount to collect HAVE_RESOURCES, // type - resource ID, value - amount to collect
@@ -108,27 +108,21 @@ struct DLL_LINKAGE EventCondition
DESTROY, // position - position of object, optional, type - type of object DESTROY, // position - position of object, optional, type - type of object
TRANSPORT, // position - where artifact should be transported, type - type of artifact TRANSPORT, // position - where artifact should be transported, type - type of artifact
//map format version pre 1.0
DAYS_PASSED, // value - number of days from start of the game DAYS_PASSED, // value - number of days from start of the game
IS_HUMAN, // value - 0 = player is AI, 1 = player is human IS_HUMAN, // value - 0 = player is AI, 1 = player is human
DAYS_WITHOUT_TOWN, // value - how long player can live without town, 0=instakill DAYS_WITHOUT_TOWN, // value - how long player can live without town, 0=instakill
STANDARD_WIN, // normal defeat all enemies condition STANDARD_WIN, // normal defeat all enemies condition
CONST_VALUE, // condition that always evaluates to "value" (0 = false, 1 = true) CONST_VALUE, // condition that always evaluates to "value" (0 = false, 1 = true)
//map format version 1.0+
HAVE_0,
HAVE_BUILDING_0,
DESTROY_0
}; };
using TargetTypeID = VariantIdentifier<ArtifactID, CreatureID, GameResID, BuildingID, MapObjectID>;
EventCondition(EWinLoseType condition = STANDARD_WIN); EventCondition(EWinLoseType condition = STANDARD_WIN);
EventCondition(EWinLoseType condition, si32 value, si32 objectType, const int3 & position = int3(-1, -1, -1)); EventCondition(EWinLoseType condition, si32 value, TargetTypeID objectType, const int3 & position = int3(-1, -1, -1));
const CGObjectInstance * object; // object that was at specified position or with instance name on start const CGObjectInstance * object; // object that was at specified position or with instance name on start
EMetaclass metaType;
si32 value; si32 value;
si32 objectType; TargetTypeID objectType;
si32 objectSubtype;
std::string objectInstanceName; std::string objectInstanceName;
int3 position; int3 position;
EWinLoseType condition; EWinLoseType condition;
@@ -141,9 +135,7 @@ struct DLL_LINKAGE EventCondition
h & objectType; h & objectType;
h & position; h & position;
h & condition; h & condition;
h & objectSubtype;
h & objectInstanceName; h & objectInstanceName;
h & metaType;
} }
}; };

View File

@@ -268,7 +268,7 @@ void CMapLoaderH3M::readPlayerInfo()
{ {
SHeroName vv; SHeroName vv;
vv.heroId = reader->readHero(); vv.heroId = reader->readHero();
vv.heroName = readLocalizedString(TextIdentifier("header", "heroNames", vv.heroId)); vv.heroName = readLocalizedString(TextIdentifier("header", "heroNames", vv.heroId.getNum()));
playerInfo.heroesNames.push_back(vv); playerInfo.heroesNames.push_back(vv);
} }
@@ -381,7 +381,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
case EVictoryConditionType::GATHERRESOURCE: case EVictoryConditionType::GATHERRESOURCE:
{ {
EventCondition cond(EventCondition::HAVE_RESOURCES); EventCondition cond(EventCondition::HAVE_RESOURCES);
cond.objectType = reader->readUInt8(); cond.objectType = reader->readGameResID();
cond.value = reader->readInt32(); cond.value = reader->readInt32();
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.279"); specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.279");
@@ -397,9 +397,9 @@ void CMapLoaderH3M::readVictoryLossConditions()
EventExpression::OperatorAll oper; EventExpression::OperatorAll oper;
EventCondition cond(EventCondition::HAVE_BUILDING); EventCondition cond(EventCondition::HAVE_BUILDING);
cond.position = reader->readInt3(); cond.position = reader->readInt3();
cond.objectType = BuildingID::TOWN_HALL + reader->readUInt8(); cond.objectType = BuildingID::TOWN_HALL_LEVEL(reader->readUInt8());
oper.expressions.emplace_back(cond); oper.expressions.emplace_back(cond);
cond.objectType = BuildingID::FORT + reader->readUInt8(); cond.objectType = BuildingID::FORT_LEVEL(reader->readUInt8());
oper.expressions.emplace_back(cond); oper.expressions.emplace_back(cond);
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.283"); specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.283");
@@ -414,7 +414,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
assert(allowNormalVictory == true); // not selectable in editor assert(allowNormalVictory == true); // not selectable in editor
assert(appliesToAI == 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(BuildingID::GRAIL);
cond.position = reader->readInt3(); cond.position = reader->readInt3();
if(cond.position.z > 2) if(cond.position.z > 2)
cond.position = int3(-1, -1, -1); cond.position = int3(-1, -1, -1);
@@ -433,7 +433,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
allowNormalVictory = true; // H3 behavior allowNormalVictory = true; // H3 behavior
assert(appliesToAI == false); // not selectable in editor assert(appliesToAI == false); // not selectable in editor
EventCondition cond(EventCondition::DESTROY); EventCondition cond(EventCondition::DESTROY);
cond.objectType = Obj::HERO; cond.objectType = MapObjectID(MapObjectID::HERO);
cond.position = reader->readInt3(); cond.position = reader->readInt3();
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.253"); specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.253");
@@ -446,7 +446,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
case EVictoryConditionType::CAPTURECITY: case EVictoryConditionType::CAPTURECITY:
{ {
EventCondition cond(EventCondition::CONTROL); EventCondition cond(EventCondition::CONTROL);
cond.objectType = Obj::TOWN; cond.objectType = MapObjectID(MapObjectID::TOWN);
cond.position = reader->readInt3(); cond.position = reader->readInt3();
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.250"); specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.250");
@@ -460,7 +460,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
{ {
assert(appliesToAI == true); // not selectable in editor assert(appliesToAI == true); // not selectable in editor
EventCondition cond(EventCondition::DESTROY); EventCondition cond(EventCondition::DESTROY);
cond.objectType = Obj::MONSTER; cond.objectType = MapObjectID(MapObjectID::MONSTER);
cond.position = reader->readInt3(); cond.position = reader->readInt3();
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.287"); specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.287");
@@ -473,8 +473,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
case EVictoryConditionType::TAKEDWELLINGS: case EVictoryConditionType::TAKEDWELLINGS:
{ {
EventExpression::OperatorAll oper; EventExpression::OperatorAll oper;
oper.expressions.emplace_back(EventCondition(EventCondition::CONTROL, 0, Obj::CREATURE_GENERATOR1)); oper.expressions.emplace_back(EventCondition(EventCondition::CONTROL, 0, Obj(Obj::CREATURE_GENERATOR1)));
oper.expressions.emplace_back(EventCondition(EventCondition::CONTROL, 0, Obj::CREATURE_GENERATOR4)); oper.expressions.emplace_back(EventCondition(EventCondition::CONTROL, 0, Obj(Obj::CREATURE_GENERATOR4)));
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.289"); specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.289");
specialVictory.onFulfill.appendTextID("core.genrltxt.288"); specialVictory.onFulfill.appendTextID("core.genrltxt.288");
@@ -486,7 +486,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
case EVictoryConditionType::TAKEMINES: case EVictoryConditionType::TAKEMINES:
{ {
EventCondition cond(EventCondition::CONTROL); EventCondition cond(EventCondition::CONTROL);
cond.objectType = Obj::MINE; cond.objectType = MapObjectID(MapObjectID::MINE);
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.291"); specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.291");
specialVictory.onFulfill.appendTextID("core.genrltxt.290"); specialVictory.onFulfill.appendTextID("core.genrltxt.290");
@@ -499,7 +499,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
{ {
assert(allowNormalVictory == true); // not selectable in editor assert(allowNormalVictory == true); // not selectable in editor
EventCondition cond(EventCondition::TRANSPORT); EventCondition cond(EventCondition::TRANSPORT);
cond.objectType = reader->readUInt8(); cond.objectType = reader->readArtifact8();
cond.position = reader->readInt3(); cond.position = reader->readInt3();
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.293"); specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.293");
@@ -513,7 +513,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
{ {
assert(appliesToAI == false); // not selectable in editor assert(appliesToAI == false); // not selectable in editor
EventCondition cond(EventCondition::DESTROY); EventCondition cond(EventCondition::DESTROY);
cond.objectType = Obj::MONSTER; cond.objectType = MapObjectID(MapObjectID::MONSTER);
specialVictory.effect.toOtherMessage.appendTextID("vcmi.map.victoryCondition.eliminateMonsters.toOthers"); specialVictory.effect.toOtherMessage.appendTextID("vcmi.map.victoryCondition.eliminateMonsters.toOthers");
specialVictory.onFulfill.appendTextID("vcmi.map.victoryCondition.eliminateMonsters.toSelf"); specialVictory.onFulfill.appendTextID("vcmi.map.victoryCondition.eliminateMonsters.toSelf");
@@ -602,7 +602,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
{ {
EventExpression::OperatorNone noneOf; EventExpression::OperatorNone noneOf;
EventCondition cond(EventCondition::CONTROL); EventCondition cond(EventCondition::CONTROL);
cond.objectType = Obj::TOWN; cond.objectType = Obj(Obj::TOWN);
cond.position = reader->readInt3(); cond.position = reader->readInt3();
noneOf.expressions.emplace_back(cond); noneOf.expressions.emplace_back(cond);
@@ -616,7 +616,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
{ {
EventExpression::OperatorNone noneOf; EventExpression::OperatorNone noneOf;
EventCondition cond(EventCondition::CONTROL); EventCondition cond(EventCondition::CONTROL);
cond.objectType = Obj::HERO; cond.objectType = Obj(Obj::HERO);
cond.position = reader->readInt3(); cond.position = reader->readInt3();
noneOf.expressions.emplace_back(cond); noneOf.expressions.emplace_back(cond);
@@ -708,7 +708,7 @@ void CMapLoaderH3M::readDisposedHeroes()
{ {
map->disposedHeroes[g].heroId = reader->readHero(); map->disposedHeroes[g].heroId = reader->readHero();
map->disposedHeroes[g].portrait = reader->readHeroPortrait(); map->disposedHeroes[g].portrait = reader->readHeroPortrait();
map->disposedHeroes[g].name = readLocalizedString(TextIdentifier("header", "heroes", map->disposedHeroes[g].heroId)); map->disposedHeroes[g].name = readLocalizedString(TextIdentifier("header", "heroes", map->disposedHeroes[g].heroId.getNum()));
reader->readBitmaskPlayers(map->disposedHeroes[g].players, false); reader->readBitmaskPlayers(map->disposedHeroes[g].players, false);
} }
} }
@@ -780,7 +780,7 @@ void CMapLoaderH3M::readAllowedArtifacts()
{ {
if(cond.condition == EventCondition::HAVE_ARTIFACT || cond.condition == EventCondition::TRANSPORT) if(cond.condition == EventCondition::HAVE_ARTIFACT || cond.condition == EventCondition::TRANSPORT)
{ {
map->allowedArtifact.erase(cond.objectType); map->allowedArtifact.erase(cond.objectType.as<ArtifactID>());
} }
return cond; return cond;
}; };

View File

@@ -139,63 +139,6 @@ namespace TriggeredEventsDetail
static const std::array<std::string, 2> typeNames = { "victory", "defeat" }; static const std::array<std::string, 2> typeNames = { "victory", "defeat" };
static EMetaclass decodeMetaclass(const std::string & source)
{
if(source.empty())
return EMetaclass::INVALID;
auto rawId = vstd::find_pos(NMetaclass::names, source);
if(rawId >= 0)
return static_cast<EMetaclass>(rawId);
else
return EMetaclass::INVALID;
}
static std::string encodeIdentifier(EMetaclass metaType, si32 type)
{
std::string metaclassName = NMetaclass::names[static_cast<int>(metaType)];
std::string identifier;
switch(metaType)
{
case EMetaclass::ARTIFACT:
{
identifier = ArtifactID::encode(type);
}
break;
case EMetaclass::CREATURE:
{
identifier = CreatureID::encode(type);
}
break;
case EMetaclass::OBJECT:
{
//TODO
auto subtypes = VLC->objtypeh->knownSubObjects(type);
if(!subtypes.empty())
{
auto subtype = *subtypes.begin();
auto handler = VLC->objtypeh->getHandlerFor(type, subtype);
identifier = handler->getTypeName();
}
}
break;
case EMetaclass::RESOURCE:
{
identifier = GameConstants::RESOURCE_NAMES[type];
}
break;
default:
{
logGlobal->error("Unsupported metaclass %s for event condition", metaclassName);
return "";
}
break;
}
return ModUtility::makeFullIdentifier("", metaclassName, identifier);
}
static EventCondition JsonToCondition(const JsonNode & node) static EventCondition JsonToCondition(const JsonNode & node)
{ {
EventCondition event; EventCondition event;
@@ -210,54 +153,28 @@ namespace TriggeredEventsDetail
{ {
const JsonNode & data = node.Vector()[1]; const JsonNode & data = node.Vector()[1];
event.objectInstanceName = data["object"].String();
event.value = data["value"].Integer();
switch (event.condition) switch (event.condition)
{ {
case EventCondition::HAVE_0: case EventCondition::HAVE_ARTIFACT:
case EventCondition::DESTROY_0: case EventCondition::TRANSPORT:
{ event.objectType = ArtifactID(ArtifactID::decode(data["type"].String()));
//todo: support subtypes break;
case EventCondition::HAVE_CREATURES:
std::string fullIdentifier = data["type"].String(); event.objectType = CreatureID(CreatureID::decode(data["type"].String()));
std::string metaTypeName; break;
std::string scope; case EventCondition::HAVE_RESOURCES:
std::string identifier; event.objectType = GameResID(GameResID::decode(data["type"].String()));
ModUtility::parseIdentifier(fullIdentifier, scope, metaTypeName, identifier); break;
case EventCondition::HAVE_BUILDING:
event.metaType = decodeMetaclass(metaTypeName); event.objectType = BuildingID(BuildingID::decode(data["type"].String()));
break;
auto type = VLC->identifiers()->getIdentifier(ModScope::scopeBuiltin(), fullIdentifier, false); case EventCondition::CONTROL:
case EventCondition::DESTROY:
if(type) event.objectType = MapObjectID(MapObjectID::decode(data["type"].String()));
event.objectType = type.value(); break;
event.objectInstanceName = data["object"].String();
if(data["value"].isNumber())
event.value = static_cast<si32>(data["value"].Integer());
}
break;
case EventCondition::HAVE_BUILDING_0:
{
//todo: support of new condition format HAVE_BUILDING_0
}
break;
default:
{
//old format
if (data["type"].getType() == JsonNode::JsonType::DATA_STRING)
{
auto identifier = VLC->identifiers()->getIdentifier(data["type"]);
if(identifier)
event.objectType = identifier.value();
else
throw std::runtime_error("Identifier resolution failed in event condition");
}
if (data["type"].isNumber())
event.objectType = static_cast<si32>(data["type"].Float());
if (!data["value"].isNull())
event.value = static_cast<si32>(data["value"].Float());
}
break;
} }
if (!data["position"].isNull()) if (!data["position"].isNull())
@@ -283,39 +200,11 @@ namespace TriggeredEventsDetail
JsonNode data; JsonNode data;
switch (event.condition) if(!event.objectInstanceName.empty())
{ data["object"].String() = event.objectInstanceName;
case EventCondition::HAVE_0:
case EventCondition::DESTROY_0:
{
//todo: support subtypes
if(event.metaType != EMetaclass::INVALID) data["type"].String() = event.objectType.toString();
data["type"].String() = encodeIdentifier(event.metaType, event.objectType); data["value"].Integer() = event.value;
if(event.value > 0)
data["value"].Integer() = event.value;
if(!event.objectInstanceName.empty())
data["object"].String() = event.objectInstanceName;
}
break;
case EventCondition::HAVE_BUILDING_0:
{
//todo: support of new condition format HAVE_BUILDING_0
}
break;
default:
{
//old format
if(event.objectType != -1)
data["type"].Integer() = event.objectType;
if(event.value != -1)
data["value"].Integer() = event.value;
}
break;
}
if(event.position != int3(-1, -1, -1)) if(event.position != int3(-1, -1, -1))
{ {

View File

@@ -81,6 +81,17 @@ ArtifactID MapReaderH3M::readArtifact()
return ArtifactID::NONE; return ArtifactID::NONE;
} }
ArtifactID MapReaderH3M::readArtifact8()
{
ArtifactID result(reader->readInt8());
if (result.getNum() < features.artifactsCount)
return remapIdentifier(result);
logGlobal->warn("Map contains invalid artifact %d. Will be removed!", result.getNum());
return ArtifactID::NONE;
}
ArtifactID MapReaderH3M::readArtifact32() ArtifactID MapReaderH3M::readArtifact32()
{ {
ArtifactID result(reader->readInt32()); ArtifactID result(reader->readInt32());
@@ -197,6 +208,13 @@ SpellID MapReaderH3M::readSpell32()
return result; return result;
} }
GameResID MapReaderH3M::readGameResID()
{
GameResID result(readInt8());
assert(result.getNum() < features.resourcesCount);
return result;
}
PlayerColor MapReaderH3M::readPlayer() PlayerColor MapReaderH3M::readPlayer()
{ {
uint8_t value = readUInt8(); uint8_t value = readUInt8();

View File

@@ -32,6 +32,7 @@ public:
void setIdentifierRemapper(const MapIdentifiersH3M & remapper); void setIdentifierRemapper(const MapIdentifiersH3M & remapper);
ArtifactID readArtifact(); ArtifactID readArtifact();
ArtifactID readArtifact8();
ArtifactID readArtifact32(); ArtifactID readArtifact32();
CreatureID readCreature(); CreatureID readCreature();
HeroTypeID readHero(); HeroTypeID readHero();
@@ -42,6 +43,7 @@ public:
SecondarySkill readSkill(); SecondarySkill readSkill();
SpellID readSpell(); SpellID readSpell();
SpellID readSpell32(); SpellID readSpell32();
GameResID readGameResID();
PlayerColor readPlayer(); PlayerColor readPlayer();
PlayerColor readPlayer32(); PlayerColor readPlayer32();

View File

@@ -1218,12 +1218,12 @@ void RemoveObject::applyGs(CGameState *gs)
{ {
if (cond.object == obj) if (cond.object == obj)
{ {
if (cond.condition == EventCondition::DESTROY || cond.condition == EventCondition::DESTROY_0) if (cond.condition == EventCondition::DESTROY)
{ {
cond.condition = EventCondition::CONST_VALUE; cond.condition = EventCondition::CONST_VALUE;
cond.value = 1; // destroyed object, from now on always fulfilled cond.value = 1; // destroyed object, from now on always fulfilled
} }
else if (cond.condition == EventCondition::CONTROL || cond.condition == EventCondition::HAVE_0) else if (cond.condition == EventCondition::CONTROL)
{ {
cond.condition = EventCondition::CONST_VALUE; cond.condition = EventCondition::CONST_VALUE;
cond.value = 0; // destroyed object, from now on can not be fulfilled cond.value = 0; // destroyed object, from now on can not be fulfilled

View File

@@ -129,9 +129,7 @@ JsonNode AbstractSettings::conditionToJson(const EventCondition & event)
result["condition"].Integer() = event.condition; result["condition"].Integer() = event.condition;
result["value"].Integer() = event.value; result["value"].Integer() = event.value;
result["objectType"].Integer() = event.objectType; result["objectType"].Integer() = event.objectType;
result["objectSubytype"].Integer() = event.objectSubtype;
result["objectInstanceName"].String() = event.objectInstanceName; result["objectInstanceName"].String() = event.objectInstanceName;
result["metaType"].Integer() = (ui8)event.metaType;
{ {
auto & position = result["position"].Vector(); auto & position = result["position"].Vector();
position.resize(3); position.resize(3);