TrinityCore
Loading...
Searching...
No Matches
ConditionMgr.h
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef TRINITY_CONDITIONMGR_H
19#define TRINITY_CONDITIONMGR_H
20
21#include "Define.h"
22#include "Hash.h"
23#include "StringFormatFwd.h"
24#include <array>
25#include <memory>
26#include <string>
27#include <unordered_map>
28#include <unordered_set>
29#include <vector>
30
31class Creature;
32class Map;
33class Player;
34class Unit;
35class WorldObject;
36class LootTemplate;
37struct Condition;
42
61{ // value1 value2 value3
62 CONDITION_NONE = 0, // 0 0 0 always true
63 CONDITION_AURA = 1, // spell_id effindex 0 true if target has aura of spell_id with effect effindex
64 CONDITION_ITEM = 2, // item_id count bank true if has #count of item_ids (if 'bank' is set it searches in bank slots too)
65 CONDITION_ITEM_EQUIPPED = 3, // item_id 0 0 true if has item_id equipped
66 CONDITION_ZONEID = 4, // zone_id 0 0 true if in zone_id
67 CONDITION_REPUTATION_RANK = 5, // faction_id rankMask 0 true if has min_rank for faction_id
68 CONDITION_TEAM = 6, // player_team 0, 0 469 - Alliance, 67 - Horde)
69 CONDITION_SKILL = 7, // skill_id skill_value 0 true if has skill_value for skill_id
70 CONDITION_QUESTREWARDED = 8, // quest_id 0 0 true if quest_id was rewarded before
71 CONDITION_QUESTTAKEN = 9, // quest_id 0, 0 true while quest active
72 CONDITION_DRUNKENSTATE = 10, // DrunkenState 0, 0 true if player is drunk enough
73 CONDITION_WORLD_STATE = 11, // index value 0 true if world has the value for the index
74 CONDITION_ACTIVE_EVENT = 12, // event_id 0 0 true if event is active
75 CONDITION_INSTANCE_INFO = 13, // entry data type true if the instance info defined by type (enum InstanceInfo) equals data.
76 CONDITION_QUEST_NONE = 14, // quest_id 0 0 true if doesn't have quest saved
77 CONDITION_CLASS = 15, // class 0 0 true if player's class is equal to class
78 CONDITION_RACE = 16, // race 0 0 true if player's race is equal to race
79 CONDITION_ACHIEVEMENT = 17, // achievement_id 0 0 true if achievement is complete
80 CONDITION_TITLE = 18, // title id 0 0 true if player has title
82 CONDITION_GENDER = 20, // gender 0 0 true if player's gender is equal to gender
83 CONDITION_UNIT_STATE = 21, // unitState 0 0 true if unit has unitState
84 CONDITION_MAPID = 22, // map_id 0 0 true if in map_id
85 CONDITION_AREAID = 23, // area_id 0 0 true if in area_id
86 CONDITION_CREATURE_TYPE = 24, // cinfo.type 0 0 true if creature_template.type = value1
87 CONDITION_SPELL = 25, // spell_id 0 0 true if player has learned spell
88 CONDITION_PHASEID = 26, // phaseid 0 0 true if object is in phaseid
89 CONDITION_LEVEL = 27, // level ComparisonType 0 true if unit's level is equal to param1 (param2 can modify the statement)
90 CONDITION_QUEST_COMPLETE = 28, // quest_id 0 0 true if player has quest_id with all objectives complete, but not yet rewarded
91 CONDITION_NEAR_CREATURE = 29, // creature entry distance dead (0/1) true if there is a creature of entry in range
92 CONDITION_NEAR_GAMEOBJECT = 30, // gameobject entry distance 0 true if there is a gameobject of entry in range
93 CONDITION_OBJECT_ENTRY_GUID_LEGACY = 31, // LEGACY_TypeID entry guid true if object is type TypeID and the entry is 0 or matches entry of the object or matches guid of the object
94 CONDITION_TYPE_MASK_LEGACY = 32, // LEGACY_TypeMask 0 0 true if object is type object's TypeMask matches provided TypeMask
95 CONDITION_RELATION_TO = 33, // ConditionTarget RelationType 0 true if object is in given relation with object specified by ConditionTarget
96 CONDITION_REACTION_TO = 34, // ConditionTarget rankMask 0 true if object's reaction matches rankMask object specified by ConditionTarget
97 CONDITION_DISTANCE_TO = 35, // ConditionTarget distance ComparisonType true if object and ConditionTarget are within distance given by parameters
98 CONDITION_ALIVE = 36, // 0 0 0 true if unit is alive
99 CONDITION_HP_VAL = 37, // hpVal ComparisonType 0 true if unit's hp matches given value
100 CONDITION_HP_PCT = 38, // hpPct ComparisonType 0 true if unit's hp matches given pct
101 CONDITION_REALM_ACHIEVEMENT = 39, // achievement_id 0 0 true if realm achievement is complete
102 CONDITION_IN_WATER = 40, // 0 0 0 true if unit in water
103 CONDITION_TERRAIN_SWAP = 41, // terrainSwap 0 0 true if object is in terrainswap
104 CONDITION_STAND_STATE = 42, // stateType state 0 true if unit matches specified sitstate (0,x: has exactly state x; 1,0: any standing state; 1,1: any sitting state;)
105 CONDITION_DAILY_QUEST_DONE = 43, // quest id 0 0 true if daily quest has been completed for the day
106 CONDITION_CHARMED = 44, // 0 0 0 true if unit is currently charmed
107 CONDITION_PET_TYPE = 45, // mask 0 0 true if player has a pet of given type(s)
108 CONDITION_TAXI = 46, // 0 0 0 true if player is on taxi
109 CONDITION_QUESTSTATE = 47, // quest_id state_mask 0 true if player is in any of the provided quest states for the quest (1 = not taken, 2 = completed, 8 = in progress, 32 = failed, 64 = rewarded)
110 CONDITION_QUEST_OBJECTIVE_PROGRESS = 48, // ID 0 progressValue true if player has ID objective progress equal to ConditionValue3 (and quest is in quest log)
111 CONDITION_DIFFICULTY_ID = 49, // Difficulty 0 0 true is map has difficulty id
112 CONDITION_GAMEMASTER = 50, // canBeGM 0 0 true if player is gamemaster (or can be gamemaster)
113 CONDITION_OBJECT_ENTRY_GUID = 51, // TypeID entry guid true if object is type TypeID and the entry is 0 or matches entry of the object or matches guid of the object
114 CONDITION_TYPE_MASK = 52, // TypeMask 0 0 true if object is type object's TypeMask matches provided TypeMask
115 CONDITION_BATTLE_PET_COUNT = 53, // SpecieId count ComparisonType true if player has `count` of battle pet species
116 CONDITION_SCENARIO_STEP = 54, // ScenarioStepId 0 0 true if player is at scenario with current step equal to ScenarioStepID
117 CONDITION_SCENE_IN_PROGRESS = 55, // SceneScriptPackageId 0 0 true if player is playing a scene with ScriptPackageId equal to given value
118 CONDITION_PLAYER_CONDITION = 56, // PlayerConditionId 0 0 true if player satisfies PlayerCondition
119 CONDITION_PRIVATE_OBJECT = 57, // 0 0 0 true if entity is private object
121 CONDITION_LABEL = 59, // Label 0 0 true if creature/gameobject has specified Label in CreatureLabel.db2/GameObjectLabel.db2
124
155{
176 // Condition source type 20 unused
193
198
209
217
222
224{
225 std::array<WorldObject const*, MAX_CONDITION_TARGETS> mConditionTargets; // an array of targets available for conditions
228 ConditionSourceInfo(WorldObject const* target0, WorldObject const* target1 = nullptr, WorldObject const* target2 = nullptr);
229 ConditionSourceInfo(Map const* map);
230};
231
233{
234 uint32 SourceGroup = 0;
235 int32 SourceEntry = 0;
236 uint32 SourceId = 0;
237
238 std::size_t GetHash() const;
239 bool operator==(ConditionId const& right) const = default;
240 std::strong_ordering operator<=>(ConditionId const& right) const = default;
241};
242
243template<>
244struct std::hash<ConditionId>
245{
246 std::size_t operator()(ConditionId const& id) const noexcept { return id.GetHash(); }
247};
248
250{
251 ConditionSourceType SourceType; //SourceTypeOrReferenceId
254 uint32 SourceId; // So far, only used in CONDITION_SOURCE_TYPE_SMART_EVENT
256 ConditionTypes ConditionType; //ConditionTypeOrReference
267
269 {
270 SourceType = CONDITION_SOURCE_TYPE_NONE;
271 SourceGroup = 0;
272 SourceEntry = 0;
273 SourceId = 0;
274 ElseGroup = 0;
275 ConditionType = CONDITION_NONE;
276 ConditionTarget = 0;
277 ConditionValue1 = 0;
278 ConditionValue2 = 0;
279 ConditionValue3 = 0;
280 ReferenceId = 0;
281 ErrorType = 0;
282 ErrorTextId = 0;
283 ScriptId = 0;
284 NegativeCondition = false;
285 }
286
287 bool Meets(ConditionSourceInfo& sourceInfo) const;
288 uint32 GetSearcherTypeMaskForCondition() const;
289 bool isLoaded() const { return ConditionType > CONDITION_NONE || ReferenceId || ScriptId; }
290 uint32 GetMaxAvailableConditionTargets() const;
291};
292
293template <>
295{
296 template <typename FormatContext>
297 static auto format(Condition const& condition, FormatContext& ctx) -> decltype(ctx.out());
298};
299
300typedef std::vector<Condition> ConditionContainer;
301typedef std::unordered_map<ConditionId, std::shared_ptr<ConditionContainer>> ConditionsByEntryMap; // stored as shared_ptr to give out weak_ptrs to hold by other code (ownership not shared)
302typedef std::array<ConditionsByEntryMap, CONDITION_SOURCE_TYPE_MAX> ConditionEntriesByTypeArray;
303
305{
306 private:
309
310 public:
311 ConditionMgr(ConditionMgr const&) = delete;
315
316 static ConditionMgr* instance();
317
318 void LoadConditions(bool isReload = false);
319 bool isConditionTypeValid(Condition* cond) const;
320
321 uint32 GetSearcherTypeMaskForConditionList(ConditionContainer const& conditions) const;
322 bool IsObjectMeetToConditions(WorldObject const* object, ConditionContainer const& conditions) const;
323 bool IsObjectMeetToConditions(WorldObject const* object1, WorldObject const* object2, ConditionContainer const& conditions) const;
324 bool IsObjectMeetToConditions(ConditionSourceInfo& sourceInfo, ConditionContainer const& conditions) const;
325 static bool CanHaveSourceGroupSet(ConditionSourceType sourceType);
326 static bool CanHaveSourceIdSet(ConditionSourceType sourceType);
327 static bool CanHaveConditionType(ConditionSourceType sourceType, ConditionTypes conditionType);
328 bool IsObjectMeetingNotGroupedConditions(ConditionSourceType sourceType, uint32 entry, ConditionSourceInfo& sourceInfo) const;
329 bool IsObjectMeetingNotGroupedConditions(ConditionSourceType sourceType, uint32 entry, WorldObject const* target0, WorldObject const* target1 = nullptr, WorldObject const* target2 = nullptr) const;
330 bool IsMapMeetingNotGroupedConditions(ConditionSourceType sourceType, uint32 entry, Map const* map) const;
331 bool HasConditionsForNotGroupedEntry(ConditionSourceType sourceType, uint32 entry) const;
332 bool IsObjectMeetingSpellClickConditions(uint32 creatureId, uint32 spellId, WorldObject const* clicker, WorldObject const* target) const;
333 bool HasConditionsForSpellClickEvent(uint32 creatureId, uint32 spellId) const;
334 bool IsObjectMeetingVehicleSpellConditions(uint32 creatureId, uint32 spellId, Player const* player, Unit const* vehicle) const;
335 bool IsObjectMeetingSmartEventConditions(int64 entryOrGuid, uint32 eventId, uint32 sourceType, Unit const* unit, WorldObject const* baseObject) const;
336 bool IsObjectMeetingVendorItemConditions(uint32 creatureId, uint32 itemId, Player const* player, Creature const* vendor) const;
337 bool IsObjectMeetingPlayerChoiceResponseConditions(uint32 playerChoiceId, int32 playerChoiceResponseId, Player const* player) const;
338
339 bool IsSpellUsedInSpellClickConditions(uint32 spellId) const;
340
341 ConditionContainer const* GetConditionsForAreaTrigger(uint32 areaTriggerId, bool isServerSide) const;
342 bool IsObjectMeetingTrainerSpellConditions(uint32 trainerId, uint32 spellId, Player* player) const;
343 bool IsObjectMeetingVisibilityByObjectIdConditions(WorldObject const* obj, WorldObject const* seer) const;
344
345 static uint32 GetPlayerConditionLfgValue(Player const* player, PlayerConditionLfgStatus status);
346 static bool IsPlayerMeetingCondition(Player const* player, uint32 conditionId);
347 static bool IsPlayerMeetingCondition(Player const* player, PlayerConditionEntry const* condition);
348 static bool IsMeetingWorldStateExpression(Map const* map, WorldStateExpressionEntry const* expression);
349 static bool IsUnitMeetingCondition(Unit const* unit, Unit const* otherUnit, UnitConditionEntry const* condition);
350
359 static char const* const StaticSourceTypeData[CONDITION_SOURCE_TYPE_MAX_DB_ALLOWED];
360 static ConditionTypeInfo const StaticConditionTypeData[CONDITION_MAX];
361
362 private:
363 bool isSourceTypeValid(Condition* cond) const;
364 void addToLootTemplate(ConditionId const& id, std::shared_ptr<std::vector<Condition>> conditions, LootTemplate* loot) const;
365 void addToGossipMenus(ConditionId const& id, std::shared_ptr<std::vector<Condition>> conditions) const;
366 void addToGossipMenuItems(ConditionId const& id, std::shared_ptr<std::vector<Condition>> conditions) const;
367 void addToSpellImplicitTargetConditions(Condition const& cond) const;
368 void addToPhases(ConditionId const& id, std::shared_ptr<std::vector<Condition>> conditions) const;
369 void addToGraveyardData(ConditionId const& id, std::shared_ptr<std::vector<Condition>> conditions) const;
370 bool IsObjectMeetToConditionList(ConditionSourceInfo& sourceInfo, ConditionContainer const& conditions) const;
371
372 static void LogUselessConditionValue(Condition const* cond, uint8 index, uint32 value);
373 static void LogUselessConditionValue(Condition const* cond, uint8 index, std::string const& value);
374
375 void Clean(); // free up resources
376
378
379 std::unordered_set<uint32> SpellsUsedInSpellClickConditions;
380};
381
382#define sConditionMgr ConditionMgr::instance()
383
385{
386 bool Meets(WorldObject const* object) const
387 {
388 if (std::shared_ptr<std::vector<Condition>> conditions = Conditions.lock())
389 return sConditionMgr->IsObjectMeetToConditions(object, *conditions);
390 return true;
391 }
392
393 bool Meets(WorldObject const* object1, WorldObject const* object2) const
394 {
395 if (std::shared_ptr<std::vector<Condition>> conditions = Conditions.lock())
396 return sConditionMgr->IsObjectMeetToConditions(object1, object2, *conditions);
397 return true;
398 }
399
400 bool Meets(ConditionSourceInfo& sourceInfo) const
401 {
402 if (std::shared_ptr<std::vector<Condition>> conditions = Conditions.lock())
403 return sConditionMgr->IsObjectMeetToConditions(sourceInfo, *conditions);
404 return true;
405 }
406
407 bool IsEmpty() const
408 {
409 if (std::shared_ptr<std::vector<Condition>> conditions = Conditions.lock())
410 return conditions->empty();
411 return true;
412 }
413
414 std::weak_ptr<ConditionContainer> Conditions;
415};
416
417#endif
std::vector< Condition > ConditionContainer
std::unordered_map< ConditionId, std::shared_ptr< ConditionContainer > > ConditionsByEntryMap
#define sConditionMgr
ConditionSourceType
@ CONDITION_SOURCE_TYPE_MAX
@ CONDITION_SOURCE_TYPE_CONVERSATION_LINE
@ CONDITION_SOURCE_TYPE_VEHICLE_SPELL
@ CONDITION_SOURCE_TYPE_SKILL_LINE_ABILITY
@ CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE
@ CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE
@ CONDITION_SOURCE_TYPE_TERRAIN_SWAP
@ CONDITION_SOURCE_TYPE_NPC_VENDOR
@ CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION
@ CONDITION_SOURCE_TYPE_SPELL_CLICK_EVENT
@ CONDITION_SOURCE_TYPE_REFERENCE_CONDITION
@ CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE
@ CONDITION_SOURCE_TYPE_PHASE
@ CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE
@ CONDITION_SOURCE_TYPE_SMART_EVENT
@ CONDITION_SOURCE_TYPE_PLAYER_CHOICE_RESPONSE
@ CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE
@ CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE
@ CONDITION_SOURCE_TYPE_AREATRIGGER_CLIENT_TRIGGERED
@ CONDITION_SOURCE_TYPE_AREATRIGGER
@ CONDITION_SOURCE_TYPE_PLAYER_CONDITION
@ CONDITION_SOURCE_TYPE_SPAWN_GROUP
@ CONDITION_SOURCE_TYPE_SPELL
@ CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE
@ CONDITION_SOURCE_TYPE_OBJECT_ID_VISIBILITY
@ CONDITION_SOURCE_TYPE_GOSSIP_MENU
@ CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE
@ CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE
@ CONDITION_SOURCE_TYPE_SPELL_IMPLICIT_TARGET
@ CONDITION_SOURCE_TYPE_GRAVEYARD
@ CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE
@ CONDITION_SOURCE_TYPE_TRAINER_SPELL
@ CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE
@ CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE
@ CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE
@ CONDITION_SOURCE_TYPE_SPELL_PROC
@ CONDITION_SOURCE_TYPE_QUEST_AVAILABLE
@ CONDITION_SOURCE_TYPE_NONE
@ CONDITION_SOURCE_TYPE_MAX_DB_ALLOWED
std::array< ConditionsByEntryMap, CONDITION_SOURCE_TYPE_MAX > ConditionEntriesByTypeArray
InstanceInfo
@ INSTANCE_INFO_DATA64
@ INSTANCE_INFO_DATA
@ INSTANCE_INFO_BOSS_STATE
@ INSTANCE_INFO_GUID_DATA
MaxConditionTargets
@ MAX_CONDITION_TARGETS
ConditionTypes
@ CONDITION_TAXI
@ CONDITION_MAPID
@ CONDITION_SKILL
@ CONDITION_RACE
@ CONDITION_STRING_ID
@ CONDITION_PHASEID
@ CONDITION_REACTION_TO
@ CONDITION_NEAR_GAMEOBJECT
@ CONDITION_QUESTREWARDED
@ CONDITION_REALM_ACHIEVEMENT
@ CONDITION_QUEST_OBJECTIVE_PROGRESS
@ CONDITION_DAILY_QUEST_DONE
@ CONDITION_ACTIVE_EVENT
@ CONDITION_SPAWNMASK_DEPRECATED
@ CONDITION_INSTANCE_INFO
@ CONDITION_RELATION_TO
@ CONDITION_PRIVATE_OBJECT
@ CONDITION_STAND_STATE
@ CONDITION_LABEL
@ CONDITION_DRUNKENSTATE
@ CONDITION_AURA
@ CONDITION_ACHIEVEMENT
@ CONDITION_OBJECT_ENTRY_GUID
@ CONDITION_PET_TYPE
@ CONDITION_DIFFICULTY_ID
@ CONDITION_DISTANCE_TO
@ CONDITION_SCENARIO_STEP
@ CONDITION_HP_VAL
@ CONDITION_BATTLE_PET_COUNT
@ CONDITION_GENDER
@ CONDITION_GAMEMASTER
@ CONDITION_TERRAIN_SWAP
@ CONDITION_REPUTATION_RANK
@ CONDITION_HP_PCT
@ CONDITION_QUEST_COMPLETE
@ CONDITION_MAX
@ CONDITION_SPELL
@ CONDITION_ZONEID
@ CONDITION_OBJECT_ENTRY_GUID_LEGACY
@ CONDITION_CHARMED
@ CONDITION_TYPE_MASK
@ CONDITION_AREAID
@ CONDITION_IN_WATER
@ CONDITION_ITEM
@ CONDITION_WORLD_STATE
@ CONDITION_CLASS
@ CONDITION_TEAM
@ CONDITION_NONE
@ CONDITION_QUEST_NONE
@ CONDITION_QUESTSTATE
@ CONDITION_ITEM_EQUIPPED
@ CONDITION_SCENE_IN_PROGRESS
@ CONDITION_LEVEL
@ CONDITION_QUESTTAKEN
@ CONDITION_PLAYER_CONDITION
@ CONDITION_NEAR_CREATURE
@ CONDITION_TITLE
@ CONDITION_ALIVE
@ CONDITION_CREATURE_TYPE
@ CONDITION_TYPE_MASK_LEGACY
@ CONDITION_UNIT_STATE
RelationType
@ RELATION_IN_PARTY
@ RELATION_IN_RAID_OR_PARTY
@ RELATION_CREATED_BY
@ RELATION_MAX
@ RELATION_SELF
@ RELATION_PASSENGER_OF
@ RELATION_OWNED_BY
PlayerConditionLfgStatus
Definition DBCEnums.h:2193
#define TC_GAME_API
Definition Define.h:129
uint8_t uint8
Definition Define.h:156
int64_t int64
Definition Define.h:149
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
std::unordered_set< uint32 > SpellsUsedInSpellClickConditions
ConditionMgr(ConditionMgr &&)=delete
ConditionMgr & operator=(ConditionMgr &&)=delete
ConditionEntriesByTypeArray ConditionStore
ConditionMgr(ConditionMgr const &)=delete
ConditionMgr & operator=(ConditionMgr const &)=delete
Definition Map.h:225
Definition Unit.h:635
bool operator==(ConditionId const &right) const =default
std::strong_ordering operator<=>(ConditionId const &right) const =default
Condition const * mLastFailedCondition
std::array< WorldObject const *, MAX_CONDITION_TARGETS > mConditionTargets
Map const * mConditionMap
uint32 SourceGroup
ConditionTypes ConditionType
std::string ConditionStringValue1
bool isLoaded() const
uint32 ErrorType
uint32 SourceId
int32 SourceEntry
uint32 ElseGroup
uint32 ScriptId
bool NegativeCondition
ConditionSourceType SourceType
uint32 ConditionValue2
uint8 ConditionTarget
uint32 ReferenceId
uint32 ErrorTextId
uint32 ConditionValue3
uint32 ConditionValue1
bool Meets(WorldObject const *object1, WorldObject const *object2) const
std::weak_ptr< ConditionContainer > Conditions
bool IsEmpty() const
bool Meets(ConditionSourceInfo &sourceInfo) const
bool Meets(WorldObject const *object) const
static auto format(Condition const &condition, FormatContext &ctx) -> decltype(ctx.out())
std::size_t operator()(ConditionId const &id) const noexcept