浏览代码

Moved all once-per-hero visitable (sans Tree) to config

Ivan Savenko 2 年之前
父节点
当前提交
bfd6c40f25

+ 9 - 0
AI/Nullkiller/Engine/AIMemory.cpp

@@ -70,6 +70,15 @@ void AIMemory::markObjectVisited(const CGObjectInstance * obj)
 		return;
 	
 	// TODO: maybe this logic belongs to CaptureObjects::shouldVisit
+	if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj))
+	{
+		if (rewardable->getVisitMode() == CRewardableObject::VISIT_HERO) //we may want to visit it with another hero
+			return;
+
+		if (rewardable->getVisitMode() == CRewardableObject::VISIT_BONUS) //or another time
+			return;
+	}
+
 	if(dynamic_cast<const CGVisitableOPH *>(obj)) //we may want to visit it with another hero
 		return;
 	

+ 10 - 0
AI/VCAI/VCAI.cpp

@@ -1606,6 +1606,16 @@ void VCAI::markObjectVisited(const CGObjectInstance * obj)
 {
 	if(!obj)
 		return;
+
+	if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj)) //we may want to visit it with another hero
+	{
+		if (rewardable->getVisitMode() == CRewardableObject::VISIT_HERO) //we may want to visit it with another hero
+			return;
+
+		if (rewardable->getVisitMode() == CRewardableObject::VISIT_BONUS) //or another time
+			return;
+	}
+
 	if(dynamic_cast<const CGVisitableOPH *>(obj)) //we may want to visit it with another hero
 		return;
 	if(dynamic_cast<const CGBonusingObject *>(obj)) //or another time

+ 2 - 1
config/gameConfig.json

@@ -53,7 +53,8 @@
 		"config/objects/dwellings.json",
 		"config/objects/rewardable.json",
 		"config/objects/rewardablePickable.json",
-		"config/objects/rewardableOnceVisitable.json"
+		"config/objects/rewardableOnceVisitable.json",
+		"config/objects/rewardableOncePerHero.json"
 	],
 
 	"artifacts" :

+ 0 - 201
config/objects/rewardable.json

@@ -84,207 +84,6 @@
 			}
 		}
 	},
-	"arena" : {
-		"index" : 4,
-		"handler": "oncePerHero",
-		"base" : {
-			"sounds" : {
-				"ambient" : ["LOOPAREN"],
-				"visit" : ["NOMAD"]
-			}
-		},
-		"types" : {
-			"object" : {
-				"index" : 0,
-				"aiValue" : 3000,
-				"rmg" : {
-					"value"		: 3000,
-					"rarity"	: 50
-				}
-			}
-		}
-	},
-	"marlettoTower" : {
-		"index" : 23,
-		"handler": "oncePerHero",
-		"base" : {
-			"sounds" : {
-				"ambient" : ["LOOPSWAR"],
-				"visit" : ["NOMAD"]
-			}
-		},
-		"types" : {
-			"object" : {
-				"index" : 0,
-				"aiValue" : 1500,
-				"rmg" : {
-					"value"		: 1500,
-					"rarity"	: 100
-				}
-			}
-		}
-	},
-	"gardenOfRevelation" : {
-		"index" : 32,
-		"handler": "oncePerHero",
-		"base" : {
-			"sounds" : {
-				"ambient" : ["LOOPGARD"],
-				"visit" : ["GETPROTECTION"]
-			}
-		},
-		"types" : {
-			"object" : {
-				"index" : 0,
-				"aiValue" : 1500,
-				"rmg" : {
-					"value"		: 1500,
-					"rarity"	: 100
-				}
-			}
-		}
-	},
-	"libraryOfEnlightenment" : {
-		"index" : 41,
-		"handler": "oncePerHero",
-		"base" : {
-			"sounds" : {
-				"visit" : ["GAZEBO"]
-			}
-		},
-		"types" : {
-			"object" : {
-				"index" : 0,
-				"aiValue" : 12000,
-				"rmg" : {
-					"value"		: 12000,
-					"rarity"	: 20
-				}
-			}
-		}
-	},
-	"mercenaryCamp" : {
-		"index" : 51,
-		"handler": "oncePerHero",
-		"base" : {
-			"sounds" : {
-				"ambient" : ["LOOPMERC"],
-				"visit" : ["NOMAD"]
-			}
-		},
-		"types" : {
-			"object" : {
-				"index" : 0,
-				"aiValue" : 1500,
-				"rmg" : {
-					"value"		: 1500,
-					"rarity"	: 100
-				}
-			}
-		}
-	},
-	"starAxis" :{
-		"index" : 61,
-		"handler": "oncePerHero",
-		"base" : {
-			"sounds" : {
-				"ambient" : ["LOOPSTAR"],
-				"visit" : ["GAZEBO"]
-			}
-		},
-		"types" : {
-			"object" : {
-				"index" : 0,
-				"aiValue" : 1500,
-				"rmg" : {
-					"value"		: 1500,
-					"rarity"	: 100
-				}
-			}
-		}
-	},
-	"treeOfKnowledge" : {
-		"index" : 102,
-		"handler": "oncePerHero",
-		"base" : {
-			"sounds" : {
-				"visit" : ["GAZEBO"]
-			}
-		},
-		"types" : {
-			"object" : {
-				"index" : 0,
-				"aiValue" : 2500,
-				"rmg" : {
-					"mapLimit"	: 100,
-					"value"		: 2500,
-					"rarity"	: 50
-				}
-			}
-		}
-	},
-	"schoolOfMagic" : {
-		"index" : 47,
-		"handler": "oncePerHero",
-		"base" : {
-			"sounds" : {
-				"ambient" : ["LOOPMAGI"],
-				"visit" : ["FAERIE"]
-			}
-		},
-		"types" : {
-			"object" : {
-				"index" : 0,
-				"aiValue" : 1000,
-				"rmg" : {
-					"value"		: 1000,
-					"rarity"	: 50
-				}
-			}
-		}
-	},
-	"schoolOfWar" : {
-		"index" : 107,
-		"handler": "oncePerHero",
-		"base" : {
-			"sounds" : {
-				"ambient" : ["LOOPSWAR"],
-				"visit" : ["MILITARY"]
-			}
-		},
-		"types" : {
-			"object" : {
-				"index" : 0,
-				"aiValue" : 1000,
-				"rmg" : {
-					"value"		: 1000,
-					"rarity"	: 50
-				}
-			}
-		}
-	},
-	"learningStone" : {
-		"index" : 100,
-		"handler": "oncePerHero",
-		"base" : {
-			"sounds" : {
-				"ambient" : ["LOOPLEAR"],
-				"visit" : ["GAZEBO"]
-			}
-		},
-		"types" : {
-			"object" : { 
-				"index" : 0,
-				"aiValue" : 1500,
-				"rmg" : {
-					"value"		: 1500,
-					"rarity"	: 200
-				} 
-			},
-			"objectWoG" : { "index" : 1 } // WoG object? Present on VCMI_Tests 2011
-		}
-	},
-
 	"buoy" : {
 		"index" : 11,
 		"handler": "bonusingObject",

+ 362 - 0
config/objects/rewardableOncePerHero.json

@@ -0,0 +1,362 @@
+{
+	/// These are objects that covered by concept of "configurable object" and have their entire configuration in this config
+
+	"arena" : {
+		"index" : 4,
+		"handler": "configurable",
+		"base" : {
+			"sounds" : {
+				"ambient" : ["LOOPAREN"],
+				"visit" : ["NOMAD"]
+			}
+		},
+		"types" : {
+			"object" : {
+				"index" : 0,
+				"aiValue" : 3000,
+				"rmg" : {
+					"value"		: 3000,
+					"rarity"	: 50
+				},
+				
+				"onSelectMessage" : 0,
+				"onVisitedMessage" : 1,
+				"visitMode" : "hero",
+				"selectMode" : "selectPlayer",
+				"rewards" : [
+					{
+						"primary" : { "attack" : 2 }
+					},
+					{
+						"primary" : { "defence" : 2 }
+					}
+				]
+			}
+		}
+	},
+	"marlettoTower" : {
+		"index" : 23,
+		"handler": "configurable",
+		"base" : {
+			"sounds" : {
+				"ambient" : ["LOOPSWAR"],
+				"visit" : ["NOMAD"]
+			}
+		},
+		"types" : {
+			"object" : {
+				"index" : 0,
+				"aiValue" : 1500,
+				"rmg" : {
+					"value"		: 1500,
+					"rarity"	: 100
+				},
+
+				"onVisitedMessage" : 40,
+				"visitMode" : "hero",
+				"selectMode" : "selectPlayer",
+				"rewards" : [
+					{
+						"message" : 39,
+						"primary" : { "defence" : 1 }
+					}
+				]
+			}
+		}
+	},
+	"gardenOfRevelation" : {
+		"index" : 32,
+		"handler": "configurable",
+		"base" : {
+			"sounds" : {
+				"ambient" : ["LOOPGARD"],
+				"visit" : ["GETPROTECTION"]
+			}
+		},
+		"types" : {
+			"object" : {
+				"index" : 0,
+				"aiValue" : 1500,
+				"rmg" : {
+					"value"		: 1500,
+					"rarity"	: 100
+				},
+				
+				"onVisitedMessage" : 60,
+				"visitMode" : "hero",
+				"selectMode" : "selectPlayer",
+				"rewards" : [
+					{
+						"message" : 59,
+						"primary" : { "knowledge" : 1 }
+					}
+				]
+			}
+		}
+	},
+	"libraryOfEnlightenment" : {
+		"index" : 41,
+		"handler": "configurable",
+		"base" : {
+			"sounds" : {
+				"visit" : ["GAZEBO"]
+			}
+		},
+		"types" : {
+			"object" : {
+				"index" : 0,
+				"aiValue" : 12000,
+				"rmg" : {
+					"value"		: 12000,
+					"rarity"	: 20
+				},
+				
+				"onVisitedMessage" : 67,
+				"onEmptyMessage" : 68,
+				"visitMode" : "hero",
+				"selectMode" : "selectFirst",
+				"rewards" : [
+					{
+						"limiter" : {
+							"minLevel" : 10
+						},
+						"message" : 59,
+						"primary" : { 
+							"attack" : 2,
+							"defence" : 2,
+							"spellpower" : 2,
+							"knowledge" : 2
+						}
+					},
+					{
+						"limiter" : {
+							"minLevel" : 8,
+							"secondary" : { "diplomacy" : 1 }
+						},
+						"message" : 59,
+						"primary" : { 
+							"attack" : 2,
+							"defence" : 2,
+							"spellpower" : 2,
+							"knowledge" : 2
+						}
+					},
+					{
+						"limiter" : {
+							"minLevel" : 6,
+							"secondary" : { "diplomacy" : 2 }
+						},
+						"message" : 59,
+						"primary" : { 
+							"attack" : 2,
+							"defence" : 2,
+							"spellpower" : 2,
+							"knowledge" : 2
+						}
+					},
+					{
+						"limiter" : {
+							"minLevel" : 4,
+							"secondary" : { "diplomacy" : 3 }
+						},
+						"message" : 59,
+						"primary" : { 
+							"attack" : 2,
+							"defence" : 2,
+							"spellpower" : 2,
+							"knowledge" : 2
+						}
+					}
+				]
+			}
+		}
+	},
+	"mercenaryCamp" : {
+		"index" : 51,
+		"handler": "configurable",
+		"base" : {
+			"sounds" : {
+				"ambient" : ["LOOPMERC"],
+				"visit" : ["NOMAD"]
+			}
+		},
+		"types" : {
+			"object" : {
+				"index" : 0,
+				"aiValue" : 1500,
+				"rmg" : {
+					"value"		: 1500,
+					"rarity"	: 100
+				},
+			
+				"onVisitedMessage" : 81,
+				"visitMode" : "hero",
+				"selectMode" : "selectPlayer",
+				"rewards" : [
+					{
+						"message" : 80,
+						"primary" : { "attack" : 1 }
+					}
+				]
+			}
+		}
+	},
+	"starAxis" :{
+		"index" : 61,
+		"handler": "configurable",
+		"base" : {
+			"sounds" : {
+				"ambient" : ["LOOPSTAR"],
+				"visit" : ["GAZEBO"]
+			}
+		},
+		"types" : {
+			"object" : {
+				"index" : 0,
+				"aiValue" : 1500,
+				"rmg" : {
+					"value"		: 1500,
+					"rarity"	: 100
+				},
+				
+				"onVisitedMessage" : 101,
+				"visitMode" : "hero",
+				"selectMode" : "selectPlayer",
+				"rewards" : [
+					{
+						"message" : 100,
+						"primary" : { "spellpower" : 1 }
+					}
+				]
+			}
+		}
+	},
+	"treeOfKnowledge" : {
+		"index" : 102,
+		"handler": "oncePerHero", //TODO: configurable
+		"base" : {
+			"sounds" : {
+				"visit" : ["GAZEBO"]
+			}
+		},
+		"types" : {
+			"object" : {
+				"index" : 0,
+				"aiValue" : 2500,
+				"rmg" : {
+					"mapLimit"	: 100,
+					"value"		: 2500,
+					"rarity"	: 50
+				}
+			}
+		}
+	},
+	"schoolOfMagic" : {
+		"index" : 47,
+		"handler": "configurable",
+		"base" : {
+			"sounds" : {
+				"ambient" : ["LOOPMAGI"],
+				"visit" : ["FAERIE"]
+			}
+		},
+		"types" : {
+			"object" : {
+				"index" : 0,
+				"aiValue" : 1000,
+				"rmg" : {
+					"value"		: 1000,
+					"rarity"	: 50
+				},
+				
+				"onSelectMessage" : 71,
+				"onVisitedMessage" : 72,
+				"onEmptyMessage" : 73,
+				"visitMode" : "hero",
+				"selectMode" : "selectPlayer",
+				"canRefuse" : true,
+				"rewards" : [
+					{
+						"limiter" : { "resources" : { "gold" : 1000 } },
+						"resources" : { "gold" : -1000 },
+						"primary" : { "spellpower" : 2 }
+					},
+					{
+						"limiter" : { "resources" : { "gold" : 1000 } },
+						"resources" : { "gold" : -1000 },
+						"primary" : { "knowledge" : 2 }
+					}
+				]
+			}
+		}
+	},
+	"schoolOfWar" : {
+		"index" : 107,
+		"handler": "configurable",
+		"base" : {
+			"sounds" : {
+				"ambient" : ["LOOPSWAR"],
+				"visit" : ["MILITARY"]
+			}
+		},
+		"types" : {
+			"object" : {
+				"index" : 0,
+				"aiValue" : 1000,
+				"rmg" : {
+					"value"		: 1000,
+					"rarity"	: 50
+				},
+
+				"onSelectMessage" : 158,
+				"onVisitedMessage" : 159,
+				"onEmptyMessage" : 160,
+				"visitMode" : "hero",
+				"selectMode" : "selectPlayer",
+				"canRefuse" : true,
+				"rewards" : [
+					{
+						"limiter" : { "resources" : { "gold" : 1000 } },
+						"resources" : { "gold" : -1000 },
+						"primary" : { "attack" : 1 }
+					},
+					{
+						"limiter" : { "resources" : { "gold" : 1000 } },
+						"resources" : { "gold" : -1000 },
+						"primary" : { "defence" : 1 }
+					}
+				]	
+			}
+		}
+	},
+	"learningStone" : {
+		"index" : 100,
+		"handler": "configurable",
+		"base" : {
+			"sounds" : {
+				"ambient" : ["LOOPLEAR"],
+				"visit" : ["GAZEBO"]
+			}
+		},
+		"types" : {
+			"object" : { 
+				"index" : 0,
+				"aiValue" : 1500,
+				"rmg" : {
+					"value"		: 1500,
+					"rarity"	: 200
+				},
+
+				"onVisitedMessage" : 144,
+				"visitMode" : "hero",
+				"selectMode" : "selectFirst",
+				"rewards" : [
+					{
+						"message" : 143,
+						"gainedExp" : 1000
+					}
+				]
+			}
+		}
+	},
+}

+ 0 - 1
config/objects/rewardableOnceVisitable.json

@@ -69,7 +69,6 @@
 						"artifacts" : [ { "class" : "MINOR" } ]
 					}
 				]
-
 			}
 		}
 	},

+ 5 - 86
lib/mapObjects/CRewardableObject.cpp

@@ -359,6 +359,11 @@ bool CRewardableObject::wasVisited(const CGHeroInstance * h) const
 	}
 }
 
+CRewardableObject::EVisitMode CRewardableObject::getVisitMode() const
+{
+	return EVisitMode(visitMode);
+}
+
 void CRewardInfo::loadComponents(std::vector<Component> & comps,
                                  const CGHeroInstance * h) const
 {
@@ -663,44 +668,6 @@ void CGVisitableOPH::initObj(CRandomGenerator & rand)
 {
 	switch(ID)
 	{
-		case Obj::ARENA:
-			info.resize(2);
-			info[0].reward.primary[PrimarySkill::ATTACK] = 2;
-			info[1].reward.primary[PrimarySkill::DEFENSE] = 2;
-			onSelect.addTxt(MetaString::ADVOB_TXT, 0);
-			onVisited.addTxt(MetaString::ADVOB_TXT, 1);
-			canRefuse = true;
-			break;
-		case Obj::MERCENARY_CAMP:
-			info.resize(1);
-			info[0].reward.primary[PrimarySkill::ATTACK] = 1;
-			info[0].message.addTxt(MetaString::ADVOB_TXT, 80);
-			onVisited.addTxt(MetaString::ADVOB_TXT, 81);
-			break;
-		case Obj::MARLETTO_TOWER:
-			info.resize(1);
-			info[0].reward.primary[PrimarySkill::DEFENSE] = 1;
-			info[0].message.addTxt(MetaString::ADVOB_TXT, 39);
-			onVisited.addTxt(MetaString::ADVOB_TXT, 40);
-			break;
-		case Obj::STAR_AXIS:
-			info.resize(1);
-			info[0].reward.primary[PrimarySkill::SPELL_POWER] = 1;
-			info[0].message.addTxt(MetaString::ADVOB_TXT, 100);
-			onVisited.addTxt(MetaString::ADVOB_TXT, 101);
-			break;
-		case Obj::GARDEN_OF_REVELATION:
-			info.resize(1);
-			info[0].reward.primary[PrimarySkill::KNOWLEDGE] = 1;
-			info[0].message.addTxt(MetaString::ADVOB_TXT, 59);
-			onVisited.addTxt(MetaString::ADVOB_TXT, 60);
-			break;
-		case Obj::LEARNING_STONE:
-			info.resize(1);
-			info[0].reward.gainedExp = 1000;
-			info[0].message.addTxt(MetaString::ADVOB_TXT, 143);
-			onVisited.addTxt(MetaString::ADVOB_TXT, 144);
-			break;
 		case Obj::TREE_OF_KNOWLEDGE:
 			info.resize(1);
 			canRefuse = true;
@@ -726,54 +693,6 @@ void CGVisitableOPH::initObj(CRandomGenerator & rand)
 				break;
 			}
 			break;
-		case Obj::LIBRARY_OF_ENLIGHTENMENT:
-		{
-			selectMode = SELECT_FIRST;
-			onVisited.addTxt(MetaString::ADVOB_TXT, 67);
-			onEmpty.addTxt(MetaString::ADVOB_TXT, 68);
-
-			CVisitInfo visit;
-			visit.reward.primary[PrimarySkill::ATTACK] = 2;
-			visit.reward.primary[PrimarySkill::DEFENSE] = 2;
-			visit.reward.primary[PrimarySkill::KNOWLEDGE] = 2;
-			visit.reward.primary[PrimarySkill::SPELL_POWER] = 2;
-			visit.message.addTxt(MetaString::ADVOB_TXT, 66);
-
-			static_assert(SecSkillLevel::LEVELS_SIZE == 4, "Behavior of Library of Enlignment may not be correct");
-			for (int i=0; i<SecSkillLevel::LEVELS_SIZE; i++)
-			{
-				visit.limiter.minLevel = 10 - i * 2;
-				visit.limiter.secondary[SecondarySkill::DIPLOMACY] = i;
-				info.push_back(visit);
-			}
-			break;
-		}
-		case Obj::SCHOOL_OF_MAGIC:
-			info.resize(2);
-			info[0].reward.primary[PrimarySkill::SPELL_POWER] = 1;
-			info[1].reward.primary[PrimarySkill::KNOWLEDGE] = 1;
-			info[0].limiter.resources[Res::GOLD] = 1000;
-			info[0].reward.resources[Res::GOLD] = -1000;
-			info[1].limiter.resources[Res::GOLD] = 1000;
-			info[1].reward.resources[Res::GOLD] = -1000;
-			onSelect.addTxt(MetaString::ADVOB_TXT, 71);
-			onVisited.addTxt(MetaString::ADVOB_TXT, 72);
-			onEmpty.addTxt(MetaString::ADVOB_TXT, 73);
-			canRefuse = true;
-			break;
-		case Obj::SCHOOL_OF_WAR:
-			info.resize(2);
-			info[0].reward.primary[PrimarySkill::ATTACK] = 1;
-			info[1].reward.primary[PrimarySkill::DEFENSE] = 1;
-			info[0].limiter.resources[Res::GOLD] = 1000;
-			info[0].reward.resources[Res::GOLD] = -1000;
-			info[1].limiter.resources[Res::GOLD] = 1000;
-			info[1].reward.resources[Res::GOLD] = -1000;
-			onSelect.addTxt(MetaString::ADVOB_TXT, 158);
-			onVisited.addTxt(MetaString::ADVOB_TXT, 159);
-			onEmpty.addTxt(MetaString::ADVOB_TXT, 160);
-			canRefuse = true;
-			break;
 	}
 }
 

+ 12 - 9
lib/mapObjects/CRewardableObject.h

@@ -198,15 +198,7 @@ class DLL_LINKAGE CRewardableObject : public CArmedInstance
 	/// grants reward to hero
 	void grantRewardBeforeLevelup(const CVisitInfo & reward, const CGHeroInstance * hero) const;
 
-protected:
-	/// controls selection of reward granted to player
-	enum ESelectMode
-	{
-		SELECT_FIRST,  // first reward that matches limiters
-		SELECT_PLAYER, // player can select from all allowed rewards
-		SELECT_RANDOM  // reward will be selected from allowed randomly
-	};
-
+public:
 	enum EVisitMode
 	{
 		VISIT_UNLIMITED, // any number of times. Side effect - object hover text won't contain visited/not visited text
@@ -216,6 +208,15 @@ protected:
 		VISIT_PLAYER     // every player can visit object once
 	};
 
+protected:
+	/// controls selection of reward granted to player
+	enum ESelectMode
+	{
+		SELECT_FIRST,  // first reward that matches limiters
+		SELECT_PLAYER, // player can select from all allowed rewards
+		SELECT_RANDOM  // reward will be selected from allowed randomly
+	};
+
 	/// filters list of visit info and returns rewards that can be granted to current hero
 	virtual std::vector<ui32> getAvailableRewards(const CGHeroInstance * hero) const;
 
@@ -247,6 +248,8 @@ protected:
 	bool canRefuse;
 
 public:
+	EVisitMode getVisitMode() const;
+
 	void setPropertyDer(ui8 what, ui32 val) override;
 	std::string getHoverText(PlayerColor player) const override;
 	std::string getHoverText(const CGHeroInstance * hero) const override;