1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-10-31 00:07:39 +02:00

Merge pull request #1486 from IvanSavenko/configurable_rewardables

Well, it works for my brand new objects with arbitrary bonus, so likely will work with everything else.
This commit is contained in:
DjWarmonger
2023-01-26 17:01:01 +01:00
committed by GitHub
28 changed files with 2038 additions and 1822 deletions

View File

@@ -314,8 +314,9 @@ bool isWeeklyRevisitable(const CGObjectInstance * obj)
return false;
//TODO: allow polling of remaining creatures in dwelling
if(dynamic_cast<const CGVisitableOPW *>(obj)) // ensures future compatibility, unlike IDs
return true;
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj))
return rewardable->getResetDuration() == 7;
if(dynamic_cast<const CGDwelling *>(obj))
return true;
if(dynamic_cast<const CBank *>(obj)) //banks tend to respawn often in mods

View File

@@ -70,12 +70,15 @@ void AIMemory::markObjectVisited(const CGObjectInstance * obj)
return;
// TODO: maybe this logic belongs to CaptureObjects::shouldVisit
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
return;
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(obj->ID == Obj::MONSTER)
return;

View File

@@ -29,12 +29,6 @@
extern FuzzyHelper * fh;
VCMI_LIB_NAMESPACE_BEGIN
class CGVisitableOPW;
VCMI_LIB_NAMESPACE_END
const double SAFE_ATTACK_CONSTANT = 1.5;
//one thread may be turn of AI and another will be handling a side effect for AI2
@@ -1606,12 +1600,19 @@ void VCAI::markObjectVisited(const CGObjectInstance * obj)
{
if(!obj)
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
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(obj->ID == Obj::MONSTER)
return;
alreadyVisited.insert(obj);
}
@@ -2742,8 +2743,9 @@ bool AIStatus::channelProbing()
bool isWeeklyRevisitable(const CGObjectInstance * obj)
{
//TODO: allow polling of remaining creatures in dwelling
if(dynamic_cast<const CGVisitableOPW *>(obj)) // ensures future compatibility, unlike IDs
return true;
if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj))
return rewardable->getResetDuration() == 7;
if(dynamic_cast<const CGDwelling *>(obj))
return true;
if(dynamic_cast<const CBank *>(obj)) //banks tend to respawn often in mods

View File

@@ -51,7 +51,11 @@
"config/objects/moddables.json",
"config/objects/creatureBanks.json",
"config/objects/dwellings.json",
"config/objects/rewardable.json"
"config/objects/rewardableOncePerWeek.json",
"config/objects/rewardablePickable.json",
"config/objects/rewardableOnceVisitable.json",
"config/objects/rewardableOncePerHero.json",
"config/objects/rewardableBonusing.json"
],
"artifacts" :

View File

@@ -597,28 +597,6 @@
}
}
},
"magicWell" : {
"index" :49,
"handler" : "magicWell",
"base" : {
"sounds" : {
"visit" : ["FAERIE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 250,
"rmg" : {
"zoneLimit" : 1,
"value" : 250,
"rarity" : 100
}
},
"objectWoG" : { "index" : 1} // WoG object? Present on VCMI_Test 2011b
}
},
/// Random objects
"randomTown" : { "index" :77, "handler": "randomTown", "types" : { "object" : { "index" : 0} } },
"randomHero" : {

View File

@@ -1,716 +0,0 @@
{
/// These are objects that covered by concept of "configurable object"
/// Most or even all of their configuration located in this file
"magicSpring" : {//magic source
"index" : 48,
"handler": "magicSpring",
"base" : {
"sounds" : {
"ambient" : ["LOOPFOUN"],
"visit" : ["FAERIE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 500//,
//"rmg" : {
// "zoneLimit" : 1,
// "value" : 500,
// "rarity" : 50
//}
//banned due to problems with 2 viistable offsets
}
}
},
"mysticalGarden" : {
"index" : 55,
"handler": "oncePerWeek",
"base" : {
"sounds" : {
"ambient" : ["LOOPLEPR"],
"visit" : ["EXPERNCE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 500,
"rmg" : {
"value" : 500,
"rarity" : 50
}
}
}
},
"windmill" :{
"index" : 112,
"handler": "oncePerWeek",
"base" : {
"sounds" : {
"ambient" : ["LOOPWIND"],
"visit" : ["GENIE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 80
}
}
}
},
"waterWheel" : {
"index" : 109,
"handler": "oncePerWeek",
"base" : {
"sounds" : {
"ambient" : ["LOOPMILL"],
"visit" : ["GENIE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 750,
"rmg" : {
"value" : 750,
"rarity" : 50
}
}
}
},
"leanTo" :{
"index" : 39,
"handler": "onceVisitable",
"base" : {
"sounds" : {
"visit" : ["GENIE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 500,
"rmg" : {
"value" : 500,
"rarity" : 100
}
}
}
},
"corpse" :{
"index" : 22,
"handler": "onceVisitable",
"base" : {
"sounds" : {
"visit" : ["MYSTERY"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 500,
"rmg" : {
"value" : 500,
"rarity" : 100
}
}
}
},
"wagon" :{
"index" : 105,
"handler": "onceVisitable",
"base" : {
"sounds" : {
"visit" : ["GENIE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 500,
"rmg" : {
"value" : 500,
"rarity" : 50
}
}
}
},
"warriorTomb" : {
"index" : 108,
"handler": "onceVisitable",
"base" : {
"sounds" : {
"visit" : ["GRAVEYARD"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 6000,
"rmg" : {
"value" : 6000,
"rarity" : 20
}
}
}
},
"campfire" :{
"index" : 12,
"handler": "pickable",
"base" : {
"sounds" : {
"ambient" : ["LOOPCAMP"],
"visit" : ["EXPERNCE"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 2000,
"rmg" : {
"value" : 2000,
"rarity" : 500
}
}
}
},
"flotsam" :{
"index" : 29,
"handler": "pickable",
"base" : {
"sounds" : {
"visit" : ["GENIE"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 2000,
"rmg" : {
"value" : 2000,
"rarity" : 100
}
}
}
},
"seaChest" :{
"index" : 82,
"handler": "pickable",
"base" : {
"sounds" : {
"visit" : ["CHEST"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 500
}
}
}
},
"shipwreckSurvivor" : {
"index" : 86,
"handler": "pickable",
"base" : {
"sounds" : {
"visit" : ["TREASURE"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 50
}
}
}
},
"treasureChest" : {
"index" : 101,
"handler": "pickable",
"base" : {
"sounds" : {
"visit" : ["CHEST"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 1000
}
}
}
},
"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",
"base" : {
"sounds" : {
"ambient" : ["LOOPBUOY"],
"visit" : ["MORALE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"value" : 100,
"rarity" : 100
}
}
}
},
"swanPond" : {
"index" : 14,
"handler": "bonusingObject",
"base" : {
"sounds" : {
"visit" : ["LUCK"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 100
}
}
}
},
"faerieRing" : {
"index" : 28,
"handler": "bonusingObject",
"base" : {
"sounds" : {
"ambient" : ["LOOPFAER"],
"visit" : ["LUCK"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 100
}
}
}
},
"fountainOfFortune" : {
"index" : 30,
"handler": "bonusingObject",
"base" : {
"sounds" : {
"ambient" : ["LOOPFOUN"],
"visit" : ["LUCK"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 100
}
}
}
},
"fountainOfYouth" : {
"index" : 31,
"handler": "bonusingObject",
"base" : {
"sounds" : {
"ambient" : ["LOOPFALL"],
"visit" : ["MORALE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 50
}
}
}
},
"idolOfFortune" : {
"index" : 38,
"handler": "bonusingObject",
"base" : {
"sounds" : {
"visit" : ["LUCK"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 100
}
},
"object1" : {//WoG?
"index" : 1
}
}
},
"mermaids" : {
"index" : 52,
"handler": "bonusingObject",
"base" : {
"sounds" : {
"visit" : ["LUCK"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"value" : 100,
"rarity" : 20
}
}
}
},
"oasis" : {
"index" : 56,
"handler": "bonusingObject",
"base" : {
"sounds" : {
"visit" : ["MORALE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 50
}
}
}
},
"stables" : {
"index" : 94,
"handler": "bonusingObject",
"base" : {
"sounds" : {
"ambient" : ["LOOPHORS"],
"visit" : ["STORE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 200,
"rmg" : {
"zoneLimit" : 1,
"value" : 200,
"rarity" : 40
}
}
}
},
"temple" : {
"index" : 96,
"handler": "bonusingObject",
"base" : {
"sounds" : {
"ambient" : ["LOOPSANC"],
"visit" : ["TEMPLE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 100
}
}
}
},
"rallyFlag" : {
"index" : 64,
"handler": "bonusingObject",
"base" : {
"sounds" : {
"ambient" : ["LOOPFLAG"],
"visit" : ["MORALE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 100
}
}
}
},
"wateringHole" : {//waters
"index" : 110,
"handler": "bonusingObject",
"base" : {
"sounds" : {
"visit" : ["MORALE"]
}
},
"types" : {
"object" : {
"index" : 0,
"aiValue" : 500,
"rmg" : {
"zoneLimit" : 1,
"value" : 500,
"rarity" : 50
}
}
}
}
}

View File

@@ -0,0 +1,457 @@
{
/// These are objects that covered by concept of "configurable object" and have their entire configuration in this config
"buoy" : {
"index" : 11,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPBUOY"],
"visit" : ["MORALE"]
}
},
"types" : {
"buoy" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"value" : 100,
"rarity" : 100
},
"blockedVisitable" : true,
"onVisitedMessage" : 22,
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 21,
"bonuses" : [ { "type" : "MORALE", "val" : 1, "duration" : "ONE_BATTLE", "description" : 94 } ]
}
]
}
}
},
"swanPond" : {
"index" : 14,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["LUCK"]
}
},
"types" : {
"swanPond" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 100
},
"onVisitedMessage" : 30,
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 29,
"movePercentage" : 0,
"bonuses" : [ { "type" : "LUCK", "val" : 2, "duration" : "ONE_BATTLE", "description" : 67 } ]
}
]
}
}
},
"faerieRing" : {
"index" : 28,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPFAER"],
"visit" : ["LUCK"]
}
},
"types" : {
"faerieRing" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 100
},
"onVisitedMessage" : 50,
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 49,
"bonuses" : [ { "type" : "LUCK", "val" : 1, "duration" : "ONE_BATTLE", "description" : 71 } ]
}
]
}
}
},
"fountainOfFortune" : {
"index" : 30,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPFOUN"],
"visit" : ["LUCK"]
}
},
"types" : {
"fountainOfFortune" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 100
},
"onVisitedMessage" : 56,
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"resetParameters" : {
"period" : 7,
"rewards" : true
},
"rewards" : [
{
"message" : 55,
"appearChance" : { "min" : 0, "max" : 25 },
"bonuses" : [ { "type" : "LUCK", "val" : -1, "duration" : "ONE_BATTLE", "description" : 69 } ] // NOTE: strings has %s placeholder for morale value
},
{
"message" : 55,
"appearChance" : { "min" : 25, "max" : 50 },
"bonuses" : [ { "type" : "LUCK", "val" : 1, "duration" : "ONE_BATTLE", "description" : 69 } ] // NOTE: strings has %s placeholder for morale value
},
{
"message" : 55,
"appearChance" : { "min" : 50, "max" : 75 },
"bonuses" : [ { "type" : "LUCK", "val" : 2, "duration" : "ONE_BATTLE", "description" : 69 } ] // NOTE: strings has %s placeholder for morale value
},
{
"message" : 55,
"appearChance" : { "min" : 75, "max" : 100 },
"bonuses" : [ { "type" : "LUCK", "val" : 3, "duration" : "ONE_BATTLE", "description" : 69 } ] // NOTE: strings has %s placeholder for morale value
},
]
}
}
},
"fountainOfYouth" : {
"index" : 31,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPFALL"],
"visit" : ["MORALE"]
}
},
"types" : {
"fountainOfYouth" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 50
},
"onVisitedMessage" : 58,
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 57,
"movePoints" : 400,
"bonuses" : [ { "type" : "MORALE", "val" : 1, "duration" : "ONE_BATTLE", "description" : 103 } ]
}
]
}
}
},
"idolOfFortune" : {
"index" : 38,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["LUCK"]
}
},
"types" : {
"idolOfFortune" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 100
},
"onVisitedMessage" : 63,
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 62,
"limiter" : {
"anyOf" : [
{ "dayOfWeek" : 1 },
{ "dayOfWeek" : 3 },
{ "dayOfWeek" : 5 }
]
},
"bonuses" : [ { "type" : "LUCK", "val" : 1, "duration" : "ONE_BATTLE", "description" : 68 } ]
},
{
"message" : 62,
"limiter" : {
"anyOf" : [
{ "dayOfWeek" : 2 },
{ "dayOfWeek" : 4 },
{ "dayOfWeek" : 6 }
]
},
"bonuses" : [ { "type" : "MORALE", "val" : 1, "duration" : "ONE_BATTLE", "description" : 68 } ]
},
{
"message" : 62,
"limiter" : { "dayOfWeek" : 7 },
"bonuses" : [
{ "type" : "MORALE", "val" : 1, "duration" : "ONE_BATTLE", "description" : 68 },
{ "type" : "LUCK", "val" : 1, "duration" : "ONE_BATTLE", "description" : 68 }
]
}
]
}
}
},
"mermaids" : {
"index" : 52,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["LUCK"]
}
},
"types" : {
"mermaids" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"value" : 100,
"rarity" : 20
},
"onVisitedMessage" : 82,
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 83,
"bonuses" : [ { "type" : "LUCK", "val" : 1, "duration" : "ONE_BATTLE", "description" : 72 } ]
}
]
}
}
},
"oasis" : {
"index" : 56,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["MORALE"]
}
},
"types" : {
"oasis" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 50
},
"onVisitedMessage" : 95,
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 94,
"movePoints" : 800,
"bonuses" : [ { "type" : "MORALE", "val" : 1, "duration" : "ONE_BATTLE", "description" : 95 } ]
}
]
}
}
},
"stables" : {
"index" : 94,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPHORS"],
"visit" : ["STORE"]
}
},
"types" : {
"stables" : {
"index" : 0,
"aiValue" : 200,
"rmg" : {
"zoneLimit" : 1,
"value" : 200,
"rarity" : 40
},
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"onVisited" : [
{
"message" : 139,
"limiter" : {
"creatures" : [ { "type" : "cavalier", "amount" : 1 } ],
},
"changeCreatures" : {
"cavalier" : "champion"
}
},
{
"message" : 136
},
],
"rewards" : [
{
"limiter" : {
"creatures" : [ { "type" : "cavalier", "amount" : 1 } ],
},
"message" : 138,
"movePoints" : 400,
"bonuses" : [ { "type" : "LAND_MOVEMENT", "val" : 400, "duration" : "ONE_WEEK"} ],
"changeCreatures" : {
"cavalier" : "champion"
}
},
{
"message" : 137,
"movePoints" : 400,
"bonuses" : [ { "type" : "LAND_MOVEMENT", "val" : 400, "duration" : "ONE_WEEK"} ]
}
]
}
}
},
"temple" : {
"index" : 96,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPSANC"],
"visit" : ["TEMPLE"]
}
},
"types" : {
"temple" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 100
},
"onVisitedMessage" : 141,
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 140,
"limiter" : { "dayOfWeek" : 7 },
"bonuses" : [ { "type" : "MORALE", "val" : 2, "duration" : "ONE_BATTLE", "description" : 97 } ]
},
{
"message" : 140,
"bonuses" : [ { "type" : "MORALE", "val" : 1, "duration" : "ONE_BATTLE", "description" : 96 } ]
}
]
}
}
},
"rallyFlag" : {
"index" : 64,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPFLAG"],
"visit" : ["MORALE"]
}
},
"types" : {
"rallyFlag" : {
"index" : 0,
"aiValue" : 100,
"rmg" : {
"zoneLimit" : 1,
"value" : 100,
"rarity" : 100
},
"onVisitedMessage" : 111,
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 110,
"movePoints" : 400,
"bonuses" : [
{ "type" : "MORALE", "val" : 1, "duration" : "ONE_BATTLE", "description" : 102 },
{ "type" : "LUCK", "val" : 1, "duration" : "ONE_BATTLE", "description" : 102 }
]
}
]
}
}
},
"wateringHole" : {
"index" : 110,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["MORALE"]
}
},
"types" : {
"wateringHole" : {
"index" : 0,
"aiValue" : 500,
"rmg" : {
"zoneLimit" : 1,
"value" : 500,
"rarity" : 50
},
"onVisitedMessage" : 167,
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 166,
"movePoints" : 400,
"bonuses" : [ { "type" : "MORALE", "val" : 1, "duration" : "ONE_BATTLE", "description" : 100 } ]
}
]
}
}
}
}

View File

@@ -0,0 +1,364 @@
{
/// 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" : {
"arena" : {
"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" : {
"marlettoTower" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 100
},
"onVisitedMessage" : 40,
"visitMode" : "hero",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 39,
"primary" : { "defence" : 1 }
}
]
}
}
},
"gardenOfRevelation" : {
"index" : 32,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPGARD"],
"visit" : ["GETPROTECTION"]
}
},
"types" : {
"gardenOfRevelation" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 100
},
"onVisitedMessage" : 60,
"visitMode" : "hero",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 59,
"primary" : { "knowledge" : 1 }
}
]
}
}
},
"libraryOfEnlightenment" : {
"index" : 41,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["GAZEBO"]
}
},
"types" : {
"libraryOfEnlightenment" : {
"index" : 0,
"aiValue" : 12000,
"rmg" : {
"value" : 12000,
"rarity" : 20
},
"onVisitedMessage" : 67,
"onEmptyMessage" : 68,
"visitMode" : "hero",
"selectMode" : "selectFirst",
"rewards" : [
{
"limiter" : {
"anyOf" : [
{ "heroLevel" : 10 },
{ "heroLevel" : 8, "secondary" : { "diplomacy" : 1 } },
{ "heroLevel" : 6, "secondary" : { "diplomacy" : 2 } },
{ "heroLevel" : 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" : {
"mercenaryCamp" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 100
},
"onVisitedMessage" : 81,
"visitMode" : "hero",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 80,
"primary" : { "attack" : 1 }
}
]
}
}
},
"starAxis" :{
"index" : 61,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPSTAR"],
"visit" : ["GAZEBO"]
}
},
"types" : {
"starAxis" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 100
},
"onVisitedMessage" : 101,
"visitMode" : "hero",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 100,
"primary" : { "spellpower" : 1 }
}
]
}
}
},
"treeOfKnowledge" : {
"index" : 102,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["GAZEBO"]
}
},
"types" : {
"treeOfKnowledge" : {
"index" : 0,
"aiValue" : 2500,
"rmg" : {
"mapLimit" : 100,
"value" : 2500,
"rarity" : 50
},
"onEmpty" : [
{
"message" : 150,
"appearChance" : { "min" : 34, "max" : 67 }
},
{
"message" : 152,
"appearChance" : { "min" : 67, "max" : 67 }
}
],
"onVisitedMessage" : 147,
"visitMode" : "hero",
"selectMode" : "selectFirst",
"canRefuse" : true,
"rewards" : [
{
"message" : 148,
"appearChance" : { "max" : 34 },
"gainedLevels" : 1
},
{
"message" : 149,
"appearChance" : { "min" : 34, "max" : 67 },
"limiter" : { "resources" : { "gold" : 2000 } },
"resources" : { "gold" : -2000 },
"gainedLevels" : 1
},
{
"message" : 151,
"appearChance" : { "min" : 67 },
"limiter" : { "resources" : { "gems" : 10 } },
"resources" : { "gems" : -10 },
"gainedLevels" : 1
},
]
}
}
},
"schoolOfMagic" : {
"index" : 47,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPMAGI"],
"visit" : ["FAERIE"]
}
},
"types" : {
"schoolOfMagic" : {
"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" : 1 }
},
{
"limiter" : { "resources" : { "gold" : 1000 } },
"resources" : { "gold" : -1000 },
"primary" : { "knowledge" : 1 }
}
]
}
}
},
"schoolOfWar" : {
"index" : 107,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPSWAR"],
"visit" : ["MILITARY"]
}
},
"types" : {
"schoolOfWar" : {
"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" : {
"learningStone" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 200
},
"onVisitedMessage" : 144,
"visitMode" : "hero",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 143,
"heroExperience" : 1000
}
]
}
}
},
}

View File

@@ -0,0 +1,199 @@
{
/// These are objects that covered by concept of "configurable object" and have their entire configuration in this config
"magicWell" : {
"index" :49,
"handler" : "configurable",
"base" : {
"sounds" : {
"visit" : ["FAERIE"]
}
},
"types" : {
"magicWell" : {
"index" : 0,
"aiValue" : 250,
"rmg" : {
"zoneLimit" : 1,
"value" : 250,
"rarity" : 100
},
"onEmptyMessage" : 79,
"onVisitedMessage" : 78,
"visitMode" : "bonus",
"selectMode" : "selectFirst",
"rewards" : [
{
"limiter" : {
"noneOf" : [ { "manaPercentage" : 100 } ]
},
"bonuses" : [ { "type" : "NONE", "duration" : "ONE_DAY"} ],
"message" : 77,
"manaPercentage" : 100
}
]
},
}
},
"magicSpring" : {
"index" : 48,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPFOUN"],
"visit" : ["FAERIE"]
}
},
"types" : {
"magicSpring" : {
"index" : 0,
"aiValue" : 500,
//banned due to problems with 2 viistable offsets
//"rmg" : {
// "zoneLimit" : 1,
// "value" : 500,
// "rarity" : 50
//},
"onEmptyMessage" : 76,
"onVisitedMessage" : 75,
"resetParameters" : {
"period" : 7,
"visitors" : true
},
"visitMode" : "once",
"selectMode" : "selectFirst",
"rewards" : [
{
"limiter" : {
"noneOf" : [ { "manaPercentage" : 200 } ]
},
"message" : 74,
"manaPercentage" : 200
}
]
}
}
},
"mysticalGarden" : {
"index" : 55,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPLEPR"],
"visit" : ["EXPERNCE"]
}
},
"types" : {
"mysticalGarden" : {
"index" : 0,
"aiValue" : 500,
"rmg" : {
"value" : 500,
"rarity" : 50
},
"onVisitedMessage" : 93,
"resetParameters" : {
"period" : 7,
"visitors" : true,
"rewards" : true
},
"visitMode" : "once",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 92,
"appearChance" : { "max" : 50 },
"resources" : { "gems" : 5 }
},
{
"message" : 92,
"appearChance" : { "min" : 50 },
"resources" : { "gold" : 500 }
}
]
}
}
},
"windmill" :{
"index" : 112,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPWIND"],
"visit" : ["GENIE"]
}
},
"types" : {
"windmill" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 80
},
"onVisitedMessage" : 169,
"resetParameters" : {
"period" : 7,
"visitors" : true,
"rewards" : true
},
"visitMode" : "once",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 170,
"resources" : [
{
"list" : [ "ore", "mercury", "gems", "sulfur", "crystal" ],
"min" : 3,
"max" : 6
}
]
}
]
}
}
},
"waterWheel" : {
"index" : 109,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPMILL"],
"visit" : ["GENIE"]
}
},
"types" : {
"waterWheel" : {
"index" : 0,
"aiValue" : 750,
"rmg" : {
"value" : 750,
"rarity" : 50
},
"onVisitedMessage" : 165,
"resetParameters" : {
"period" : 7,
"visitors" : true
},
"visitMode" : "once",
"selectMode" : "selectFirst",
"rewards" : [
{
"limiter" : { "daysPassed" : 8 },
"message" : 164,
"resources" : { "gold" : 1000 }
},
{
"message" : 164,
"resources" : { "gold" : 500 }
}
]
}
}
}
}

View File

@@ -0,0 +1,185 @@
{
/// These are objects that covered by concept of "configurable object" and have their entire configuration in this config
"leanTo" :{
"index" : 39,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["GENIE"]
}
},
"types" : {
"leanTo" : {
"index" : 0,
"aiValue" : 500,
"rmg" : {
"value" : 500,
"rarity" : 100
},
"onVisitedMessage" : 65,
"visitMode" : "once",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 64,
"resources" : [
{
"list" : [ "wood", "ore", "mercury", "gems", "sulfur", "crystal" ],
"min" : 1,
"max" : 5
}
]
}
]
}
}
},
"corpse" :{
"index" : 22,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["MYSTERY"]
}
},
"types" : {
"corpse" : {
"index" : 0,
"aiValue" : 500,
"rmg" : {
"value" : 500,
"rarity" : 100
},
"onVisitedMessage" : 38,
"blockedVisitable" : true,
"visitMode" : "once",
"selectMode" : "selectFirst",
"rewards" : [
{
"appearChance" : { "max" : 10 },
"message" : 37,
"artifacts" : [ { "class" : "TREASURE" } ]
},
{
"appearChance" : { "min" : 10, "max" : 20 },
"message" : 37,
"artifacts" : [ { "class" : "MINOR" } ]
},
{
"appearChance" : { "min" : 20, "max" : 100 },
"message" : 38,
}
]
}
}
},
"wagon" :{
"index" : 105,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["GENIE"]
}
},
"types" : {
"wagon" : {
"index" : 0,
"aiValue" : 500,
"rmg" : {
"value" : 500,
"rarity" : 50
},
"onVisitedMessage" : 156,
"visitMode" : "once",
"selectMode" : "selectFirst",
"rewards" : [
{
"appearChance" : { "max" : 20 },
"message" : 155,
"artifacts" : [ { "class" : "TREASURE" } ]
},
{
"appearChance" : { "min" : 20, "max" : 40 },
"message" : 155,
"artifacts" : [ { "class" : "MINOR" } ]
},
{
"message" : 154,
"appearChance" : { "min" : 40, "max" : 90 },
"resources" : [
{
"list" : [ "wood", "ore", "mercury", "gems", "sulfur", "crystal" ],
"min" : 2,
"max" : 5
},
]
},
{
"appearChance" : { "min" : 90, "max" : 100 },
"message" : 156,
}
]
}
}
},
"warriorTomb" : {
"index" : 108,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["GRAVEYARD"]
}
},
"types" : {
"warriorTomb" : {
"index" : 0,
"aiValue" : 6000,
"rmg" : {
"value" : 6000,
"rarity" : 20
},
"onSelectMessage" : 161,
"visitMode" : "once",
"selectMode" : "selectFirst",
"onVisited" : [
{
"message" : 163,
"bonuses" : [ { "type" : "MORALE", "val" : -3, "duration" : "ONE_BATTLE" } ]
}
],
"rewards" : [
{
"appearChance" : { "max" : 30 },
"message" : 162,
"artifacts" : [ { "class" : "TREASURE" } ],
"bonuses" : [ { "type" : "MORALE", "val" : -3, "duration" : "ONE_BATTLE" } ]
},
{
"appearChance" : { "min" : 30, "max" : 80 },
"message" : 162,
"artifacts" : [ { "class" : "MINOR" } ],
"bonuses" : [ { "type" : "MORALE", "val" : -3, "duration" : "ONE_BATTLE" } ]
},
{
"appearChance" : { "min" : 80, "max" : 95 },
"message" : 162,
"artifacts" : [ { "class" : "MAJOR" } ],
"bonuses" : [ { "type" : "MORALE", "val" : -3, "duration" : "ONE_BATTLE" } ]
},
{
"appearChance" : { "min" : 95 },
"message" : 162,
"artifacts" : [ { "class" : "RELIC" } ],
"bonuses" : [ { "type" : "MORALE", "val" : -3, "duration" : "ONE_BATTLE" } ]
}
]
}
}
}
}

View File

@@ -0,0 +1,267 @@
{
/// These are objects that covered by concept of "configurable object" and have their entire configuration in this config
"campfire" :{
"index" : 12,
"handler": "configurable",
"base" : {
"sounds" : {
"ambient" : ["LOOPCAMP"],
"visit" : ["EXPERNCE"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
}
},
"types" : {
"campfire" : {
"index" : 0,
"aiValue" : 2000,
"rmg" : {
"value" : 2000,
"rarity" : 500
},
"blockedVisitable" : true,
"visitMode" : "unlimited",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 23,
"removeObject" : true,
"resources" : [
{
"list" : [ "wood", "ore", "mercury", "gems", "sulfur", "crystal" ],
"min" : 4,
"max" : 6
},
{
"type" : "gold",
"min" : 400,
"max" : 600
}
]
}
]
}
}
},
"flotsam" :{
"index" : 29,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["GENIE"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
}
},
"types" : {
"flotsam" : {
"index" : 0,
"aiValue" : 2000,
"rmg" : {
"value" : 2000,
"rarity" : 100
},
"blockedVisitable" : true,
"visitMode" : "unlimited",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 51,
"appearChance" : { "max" : 25 },
"removeObject" : true,
},
{
"message" : 52,
"appearChance" : { "min" : 25, "max" : 50 },
"removeObject" : true,
"resources" : {
"wood" : 5
}
},
{
"message" : 53,
"appearChance" : { "min" : 50, "max" : 75 },
"removeObject" : true,
"resources" : {
"wood" : 5,
"gold" : 200
}
},
{
"message" : 54,
"appearChance" : { "min" : 75 },
"removeObject" : true,
"resources" : {
"wood" : 10,
"gold" : 500
}
}
]
}
}
},
"seaChest" :{
"index" : 82,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["CHEST"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
}
},
"types" : {
"seaChest" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 500
},
"blockedVisitable" : true,
"visitMode" : "unlimited",
"selectMode" : "selectFirst",
"rewards" : [
{
"appearChance" : { "max" : 20 },
"message" : 116,
"removeObject" : true,
},
{
"appearChance" : { "min" : 20, "max" : 30 },
"message" : 117,
"removeObject" : true,
"artifacts" : [
{ "class" : "TREASURE" }
],
"resources" : {
"gold" : 1000
}
},
{
"appearChance" : { "min" : 30 },
"message" : 118,
"removeObject" : true,
"resources" : {
"gold" : 1500
}
}
]
}
}
},
"shipwreckSurvivor" : {
"index" : 86,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["TREASURE"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
}
},
"types" : {
"shipwreckSurvivor" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 50
},
"blockedVisitable" : true,
"visitMode" : "unlimited",
"selectMode" : "selectFirst",
"rewards" : [
{
"appearChance" : { "max" : 55 },
"message" : 125,
"removeObject" : true,
"artifacts" : [ { "class" : "TREASURE" } ]
},
{
"appearChance" : { "min" : 55, "max" : 75 },
"message" : 125,
"removeObject" : true,
"artifacts" : [ { "class" : "MINOR" } ]
},
{
"appearChance" : { "min" : 75, "max" : 95 },
"message" : 125,
"removeObject" : true,
"artifacts" : [ { "class" : "MAJOR" } ]
},
{
"appearChance" : { "min" : 95 },
"message" : 125,
"removeObject" : true,
"artifacts" : [ { "class" : "RELIC" } ]
}
]
}
}
},
"treasureChest" : {
"index" : 101,
"handler": "configurable",
"base" : {
"sounds" : {
"visit" : ["CHEST"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
}
},
"types" : {
"treasureChest" : {
"index" : 0,
"aiValue" : 2000,
"rmg" : {
"value" : 1500,
"rarity" : 1000
},
"blockedVisitable" : true,
"onSelectMessage" : 146,
"visitMode" : "unlimited",
"selectMode" : "selectPlayer",
"rewards" : [
{
"appearChance" : { "max" : 33 },
"resources" : { "gold" : 2000 },
"removeObject" : true,
},
{
"appearChance" : { "max" : 33 },
"gainedExp" : 1500,
"removeObject" : true,
},
{
"appearChance" : { "min" : 33, "max" : 65 },
"resources" : { "gold" : 1500 },
"removeObject" : true,
},
{
"appearChance" : { "min" : 33, "max" : 65 },
"gainedExp" : 1000,
"removeObject" : true,
},
{
"appearChance" : { "min" : 65, "max" : 95 },
"resources" : { "gold" : 1000 },
"removeObject" : true,
},
{
"appearChance" : { "min" : 65, "max" : 95 },
"gainedExp" : 500,
"removeObject" : true,
},
{
"appearChance" : { "min" : 95 },
"message" : 145,
"removeObject" : true,
"artifacts" : [ { "class" : "TREASURE" } ]
}
]
}
}
}
}

View File

@@ -142,9 +142,6 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
case GENERAL_TXT:
dst = VLC->generaltexth->translate("core.genrltxt", ser);
break;
case XTRAINFO_TXT:
dst = VLC->generaltexth->translate("core.xtrainfo", ser);
break;
case RES_NAMES:
dst = VLC->generaltexth->translate("core.restypes", ser);
break;

View File

@@ -409,7 +409,6 @@ CGeneralTextHandler::CGeneralTextHandler():
hcommands (*this, "core.hallinfo" ),
fcommands (*this, "core.castinfo" ),
advobtxt (*this, "core.advevent" ),
xtrainfo (*this, "core.xtrainfo" ),
restypes (*this, "core.restypes" ),
randsign (*this, "core.randsign" ),
overview (*this, "core.overview" ),
@@ -439,7 +438,6 @@ CGeneralTextHandler::CGeneralTextHandler():
readToVector("core.hallinfo", "DATA/HALLINFO.TXT" );
readToVector("core.castinfo", "DATA/CASTINFO.TXT" );
readToVector("core.advevent", "DATA/ADVEVENT.TXT" );
readToVector("core.xtrainfo", "DATA/XTRAINFO.TXT" );
readToVector("core.restypes", "DATA/RESTYPES.TXT" );
readToVector("core.randsign", "DATA/RANDSIGN.TXT" );
readToVector("core.crgen1", "DATA/CRGEN1.TXT" );

View File

@@ -212,7 +212,6 @@ public:
//objects
LegacyTextContainer advobtxt;
LegacyTextContainer xtrainfo;
LegacyTextContainer restypes; //names of resources
LegacyTextContainer randsign;
LegacyTextContainer seerEmpty;

View File

@@ -260,6 +260,21 @@ bool JsonNode::isNumber() const
return type == JsonType::DATA_INTEGER || type == JsonType::DATA_FLOAT;
}
bool JsonNode::isString() const
{
return type == JsonType::DATA_STRING;
}
bool JsonNode::isVector() const
{
return type == JsonType::DATA_VECTOR;
}
bool JsonNode::isStruct() const
{
return type == JsonType::DATA_STRUCT;
}
bool JsonNode::containsBaseData() const
{
switch(type)
@@ -796,7 +811,12 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
b->sid = static_cast<si32>(ability["sourceID"].Float());
if(!ability["description"].isNull())
b->description = ability["description"].String();
{
if (ability["description"].isString())
b->description = ability["description"].String();
if (ability["description"].isNumber())
b->description = VLC->generaltexth->translate("core.arraytxt", ability["description"].Integer());
}
value = &ability["effectRange"];
if (!value->isNull())

View File

@@ -83,6 +83,9 @@ public:
bool isNull() const;
bool isNumber() const;
bool isString() const;
bool isVector() const;
bool isStruct() const;
/// true if node contains not-null data that cannot be extended via merging
/// used for generating common base node from multiple nodes (e.g. bonuses)
bool containsBaseData() const;

View File

@@ -1178,7 +1178,7 @@ namespace ObjProperty
BANK_DAYCOUNTER, BANK_RESET, BANK_CLEAR,
//object with reward
REWARD_RESET, REWARD_SELECT
REWARD_RANDOMIZE, REWARD_SELECT, REWARD_CLEARED
};
}

View File

@@ -96,7 +96,7 @@ struct DLL_LINKAGE MetaString
private:
enum EMessage {TEXACT_STRING, TLOCAL_STRING, TNUMBER, TREPLACE_ESTRING, TREPLACE_LSTRING, TREPLACE_NUMBER, TREPLACE_PLUSNUMBER};
public:
enum {GENERAL_TXT=1, XTRAINFO_TXT, OBJ_NAMES, RES_NAMES, ART_NAMES, ARRAY_TXT, CRE_PL_NAMES, CREGENS, MINE_NAMES,
enum {GENERAL_TXT=1, OBJ_NAMES, RES_NAMES, ART_NAMES, ARRAY_TXT, CRE_PL_NAMES, CREGENS, MINE_NAMES,
MINE_EVNTS, ADVOB_TXT, ART_EVNTS, SPELL_NAME, SEC_SKILL_NAME, CRE_SING_NAMES, CREGENS4, COLOR, ART_DESCR, JK_TXT};
std::vector<ui8> message; //vector of EMessage

View File

@@ -54,7 +54,6 @@ CObjectClassesHandler::CObjectClassesHandler()
SET_HANDLER("artifact", CGArtifact);
SET_HANDLER("blackMarket", CGBlackMarket);
SET_HANDLER("boat", CGBoat);
SET_HANDLER("bonusingObject", CGBonusingObject);
SET_HANDLER("borderGate", CGBorderGate);
SET_HANDLER("borderGuard", CGBorderGuard);
SET_HANDLER("monster", CGCreature);
@@ -65,15 +64,11 @@ CObjectClassesHandler::CObjectClassesHandler()
SET_HANDLER("keymaster", CGKeymasterTent);
SET_HANDLER("lighthouse", CGLighthouse);
SET_HANDLER("magi", CGMagi);
SET_HANDLER("magicSpring", CGMagicSpring);
SET_HANDLER("magicWell", CGMagicWell);
SET_HANDLER("market", CGMarket);
SET_HANDLER("mine", CGMine);
SET_HANDLER("obelisk", CGObelisk);
SET_HANDLER("observatory", CGObservatory);
SET_HANDLER("onceVisitable", CGOnceVisitable);
SET_HANDLER("pandora", CGPandoraBox);
SET_HANDLER("pickable", CGPickable);
SET_HANDLER("prison", CGHeroInstance);
SET_HANDLER("questGuard", CGQuestGuard);
SET_HANDLER("resource", CGResource);
@@ -87,8 +82,6 @@ CObjectClassesHandler::CObjectClassesHandler()
SET_HANDLER("subterraneanGate", CGSubterraneanGate);
SET_HANDLER("whirlpool", CGWhirlpool);
SET_HANDLER("university", CGUniversity);
SET_HANDLER("oncePerHero", CGVisitableOPH);
SET_HANDLER("oncePerWeek", CGVisitableOPW);
SET_HANDLER("witch", CGWitchHut);
SET_HANDLER("terrain", CGTerrainPatch);

View File

@@ -13,6 +13,7 @@
#include "../CRandomGenerator.h"
#include "../StringConstants.h"
#include "../CCreatureHandler.h"
#include "../CModHandler.h"
#include "JsonRandom.h"
#include "../IGameCallback.h"
@@ -45,11 +46,118 @@ void CRandomRewardObjectInfo::init(const JsonNode & objectConfig)
parameters = objectConfig;
}
void CRandomRewardObjectInfo::configureObject(CRewardableObject * object, CRandomGenerator & rng) const
TRewardLimitersList CRandomRewardObjectInfo::configureSublimiters(CRewardableObject * object, CRandomGenerator & rng, const JsonNode & source) const
{
std::map<si32, si32> thrownDice;
TRewardLimitersList result;
for (const auto & input : source.Vector())
{
auto newLimiter = std::make_shared<CRewardLimiter>();
for (const JsonNode & reward : parameters["rewards"].Vector())
configureLimiter(object, rng, *newLimiter, input);
result.push_back(newLimiter);
}
return result;
}
void CRandomRewardObjectInfo::configureLimiter(CRewardableObject * object, CRandomGenerator & rng, CRewardLimiter & limiter, const JsonNode & source) const
{
std::vector<SpellID> spells;
for (size_t i=0; i<6; i++)
IObjectInterface::cb->getAllowedSpells(spells, static_cast<ui16>(i));
limiter.dayOfWeek = JsonRandom::loadValue(source["dayOfWeek"], rng);
limiter.daysPassed = JsonRandom::loadValue(source["daysPassed"], rng);
limiter.heroExperience = JsonRandom::loadValue(source["heroExperience"], rng);
limiter.heroLevel = JsonRandom::loadValue(source["heroLevel"], rng)
+ JsonRandom::loadValue(source["minLevel"], rng); // VCMI 1.1 compatibilty
limiter.manaPercentage = JsonRandom::loadValue(source["manaPercentage"], rng);
limiter.manaPoints = JsonRandom::loadValue(source["manaPoints"], rng);
limiter.resources = JsonRandom::loadResources(source["resources"], rng);
limiter.primary = JsonRandom::loadPrimary(source["primary"], rng);
limiter.secondary = JsonRandom::loadSecondary(source["secondary"], rng);
limiter.artifacts = JsonRandom::loadArtifacts(source["spells"], rng);
limiter.spells = JsonRandom::loadSpells(source["artifacts"], rng, spells);
limiter.creatures = JsonRandom::loadCreatures(source["creatures"], rng);
limiter.allOf = configureSublimiters(object, rng, source["allOf"] );
limiter.anyOf = configureSublimiters(object, rng, source["anyOf"] );
limiter.noneOf = configureSublimiters(object, rng, source["noneOf"] );
}
void CRandomRewardObjectInfo::configureReward(CRewardableObject * object, CRandomGenerator & rng, CRewardInfo & reward, const JsonNode & source) const
{
reward.resources = JsonRandom::loadResources(source["resources"], rng);
reward.heroExperience = JsonRandom::loadValue(source["heroExperience"], rng)
+ JsonRandom::loadValue(source["gainedExp"], rng); // VCMI 1.1 compatibilty
reward.heroLevel = JsonRandom::loadValue(source["heroLevel"], rng)
+ JsonRandom::loadValue(source["gainedLevels"], rng); // VCMI 1.1 compatibilty
reward.manaDiff = JsonRandom::loadValue(source["manaPoints"], rng);
reward.manaOverflowFactor = JsonRandom::loadValue(source["manaOverflowFactor"], rng);
reward.manaPercentage = JsonRandom::loadValue(source["manaPercentage"], rng, -1);
reward.movePoints = JsonRandom::loadValue(source["movePoints"], rng);
reward.movePercentage = JsonRandom::loadValue(source["movePercentage"], rng, -1);
reward.removeObject = source["removeObject"].Bool();
reward.bonuses = JsonRandom::loadBonuses(source["bonuses"]);
for (auto & bonus : reward.bonuses)
{
bonus.source = Bonus::OBJECT;
bonus.sid = object->ID;
//TODO: bonus.description = object->getObjectName();
if (bonus.type == Bonus::MORALE)
reward.extraComponents.push_back(Component(Component::MORALE, 0, bonus.val, 0));
if (bonus.type == Bonus::LUCK)
reward.extraComponents.push_back(Component(Component::LUCK, 0, bonus.val, 0));
}
reward.primary = JsonRandom::loadPrimary(source["primary"], rng);
reward.secondary = JsonRandom::loadSecondary(source["secondary"], rng);
std::vector<SpellID> spells;
for (size_t i=0; i<6; i++)
IObjectInterface::cb->getAllowedSpells(spells, static_cast<ui16>(i));
reward.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng);
reward.spells = JsonRandom::loadSpells(source["spells"], rng, spells);
reward.creatures = JsonRandom::loadCreatures(source["creatures"], rng);
for ( auto node : source["changeCreatures"].Struct() )
{
CreatureID from (VLC->modh->identifiers.getIdentifier (node.second.meta, "creature", node.first) .get());
CreatureID dest (VLC->modh->identifiers.getIdentifier (node.second.meta, "creature", node.second.String()).get());
reward.extraComponents.push_back(Component(Component::CREATURE, dest.getNum(), 0, 0));
reward.creaturesChange[from] = dest;
}
}
void CRandomRewardObjectInfo::configureResetInfo(CRewardableObject * object, CRandomGenerator & rng, CRewardResetInfo & resetParameters, const JsonNode & source) const
{
resetParameters.period = static_cast<ui32>(source["period"].Float());
resetParameters.visitors = source["visitors"].Bool();
resetParameters.rewards = source["rewards"].Bool();
}
void CRandomRewardObjectInfo::configureRewards(
CRewardableObject * object,
CRandomGenerator & rng, const
JsonNode & source,
std::map<si32, si32> & thrownDice,
CRewardVisitInfo::ERewardEventType event ) const
{
for (const JsonNode & reward : source.Vector())
{
if (!reward["appearChance"].isNull())
{
@@ -57,7 +165,7 @@ void CRandomRewardObjectInfo::configureObject(CRewardableObject * object, CRando
si32 diceID = static_cast<si32>(chance["dice"].Float());
if (thrownDice.count(diceID) == 0)
thrownDice[diceID] = rng.getIntRange(1, 100)();
thrownDice[diceID] = rng.getIntRange(0, 99)();
if (!chance["min"].isNull())
{
@@ -68,61 +176,59 @@ void CRandomRewardObjectInfo::configureObject(CRewardableObject * object, CRando
if (!chance["max"].isNull())
{
int max = static_cast<int>(chance["max"].Float());
if (max < thrownDice[diceID])
if (max <= thrownDice[diceID])
continue;
}
}
const JsonNode & limiter = reward["limiter"];
CVisitInfo info;
// load limiter
info.limiter.numOfGrants = JsonRandom::loadValue(limiter["numOfGrants"], rng);
info.limiter.dayOfWeek = JsonRandom::loadValue(limiter["dayOfWeek"], rng);
info.limiter.minLevel = JsonRandom::loadValue(limiter["minLevel"], rng);
info.limiter.resources = JsonRandom::loadResources(limiter["resources"], rng);
info.limiter.primary = JsonRandom::loadPrimary(limiter["primary"], rng);
info.limiter.secondary = JsonRandom::loadSecondary(limiter["secondary"], rng);
info.limiter.artifacts = JsonRandom::loadArtifacts(limiter["artifacts"], rng);
info.limiter.creatures = JsonRandom::loadCreatures(limiter["creatures"], rng);
info.reward.resources = JsonRandom::loadResources(reward["resources"], rng);
info.reward.gainedExp = JsonRandom::loadValue(reward["gainedExp"], rng);
info.reward.gainedLevels = JsonRandom::loadValue(reward["gainedLevels"], rng);
info.reward.manaDiff = JsonRandom::loadValue(reward["manaPoints"], rng);
info.reward.manaPercentage = JsonRandom::loadValue(reward["manaPercentage"], rng, -1);
info.reward.movePoints = JsonRandom::loadValue(reward["movePoints"], rng);
info.reward.movePercentage = JsonRandom::loadValue(reward["movePercentage"], rng, -1);
info.reward.removeObject = reward["removeObject"].Bool();
//FIXME: compile this line on Visual
//info.reward.bonuses = JsonRandom::loadBonuses(reward["bonuses"]);
info.reward.primary = JsonRandom::loadPrimary(reward["primary"], rng);
info.reward.secondary = JsonRandom::loadSecondary(reward["secondary"], rng);
std::vector<SpellID> spells;
for (size_t i=0; i<6; i++)
IObjectInterface::cb->getAllowedSpells(spells, static_cast<ui16>(i));
info.reward.artifacts = JsonRandom::loadArtifacts(reward["artifacts"], rng);
info.reward.spells = JsonRandom::loadSpells(reward["spells"], rng, spells);
info.reward.creatures = JsonRandom::loadCreatures(reward["creatures"], rng);
CRewardVisitInfo info;
configureLimiter(object, rng, info.limiter, reward["limiter"]);
configureReward(object, rng, info.reward, reward);
info.visitType = event;
info.message = loadMessage(reward["message"]);
info.selectChance = JsonRandom::loadValue(reward["selectChance"], rng);
for (const auto & artifact : info.reward.artifacts )
info.message.addReplacement(MetaString::ART_NAMES, artifact.getNum());
for (const auto & artifact : info.reward.spells )
info.message.addReplacement(MetaString::SPELL_NAME, artifact.getNum());
object->info.push_back(info);
}
}
void CRandomRewardObjectInfo::configureObject(CRewardableObject * object, CRandomGenerator & rng) const
{
object->info.clear();
std::map<si32, si32> thrownDice;
configureRewards(object, rng, parameters["rewards"], thrownDice, CRewardVisitInfo::EVENT_FIRST_VISIT);
configureRewards(object, rng, parameters["onVisited"], thrownDice, CRewardVisitInfo::EVENT_ALREADY_VISITED);
configureRewards(object, rng, parameters["onEmpty"], thrownDice, CRewardVisitInfo::EVENT_NOT_AVAILABLE);
object->blockVisit= parameters["blockedVisitable"].Bool();
object->onSelect = loadMessage(parameters["onSelectMessage"]);
object->onVisited = loadMessage(parameters["onVisitedMessage"]);
object->onEmpty = loadMessage(parameters["onEmptyMessage"]);
object->resetDuration = static_cast<ui16>(parameters["resetDuration"].Float());
if (!parameters["onVisitedMessage"].isNull())
{
CRewardVisitInfo onVisited;
onVisited.visitType = CRewardVisitInfo::EVENT_ALREADY_VISITED;
onVisited.message = loadMessage(parameters["onVisitedMessage"]);
object->info.push_back(onVisited);
}
if (!parameters["onEmptyMessage"].isNull())
{
CRewardVisitInfo onEmpty;
onEmpty.visitType = CRewardVisitInfo::EVENT_NOT_AVAILABLE;
onEmpty.message = loadMessage(parameters["onEmptyMessage"]);
object->info.push_back(onEmpty);
}
configureResetInfo(object, rng, object->resetParameters, parameters["resetParameters"]);
object->canRefuse = parameters["canRefuse"].Bool();
auto visitMode = parameters["visitMode"].String();

View File

@@ -19,6 +19,14 @@ VCMI_LIB_NAMESPACE_BEGIN
class DLL_LINKAGE CRandomRewardObjectInfo : public IObjectInfo
{
JsonNode parameters;
void configureRewards(CRewardableObject * object, CRandomGenerator & rng, const JsonNode & source, std::map<si32, si32> & thrownDice, CRewardVisitInfo::ERewardEventType mode) const;
void configureLimiter(CRewardableObject * object, CRandomGenerator & rng, CRewardLimiter & limiter, const JsonNode & source) const;
TRewardLimitersList configureSublimiters(CRewardableObject * object, CRandomGenerator & rng, const JsonNode & source) const;
void configureReward(CRewardableObject * object, CRandomGenerator & rng, CRewardInfo & info, const JsonNode & source) const;
void configureResetInfo(CRewardableObject * object, CRandomGenerator & rng, CRewardResetInfo & info, const JsonNode & source) const;
public:
bool givesResources() const override;

File diff suppressed because it is too large Load Diff

View File

@@ -19,20 +19,30 @@ VCMI_LIB_NAMESPACE_BEGIN
class CRandomRewardObjectInfo;
class CRewardLimiter;
using TRewardLimitersList = std::vector<std::shared_ptr<CRewardLimiter>>;
/// Limiters of rewards. Rewards will be granted to hero only if he satisfies requirements
/// Note: for this is only a test - it won't remove anything from hero (e.g. artifacts or creatures)
/// NOTE: in future should (partially) replace seer hut/quest guard quests checks
class DLL_LINKAGE CRewardLimiter
{
public:
/// how many times this reward can be granted, 0 for unlimited
si32 numOfGrants;
/// day of week, unused if 0, 1-7 will test for current day of week
si32 dayOfWeek;
si32 daysPassed;
/// total experience that hero needs to have
si32 heroExperience;
/// level that hero needs to have
si32 minLevel;
si32 heroLevel;
/// mana points that hero needs to have
si32 manaPoints;
/// percentage of mana points that hero needs to have
si32 manaPercentage;
/// resources player needs to have in order to trigger reward
TResources resources;
@@ -45,13 +55,25 @@ public:
/// Note: does not checks for multiple copies of the same arts
std::vector<ArtifactID> artifacts;
/// Spells that hero must have in the spellbook
std::vector<SpellID> spells;
/// creatures that hero needs to have
std::vector<CStackBasicDescriptor> creatures;
/// sub-limiters, all must pass for this limiter to pass
TRewardLimitersList allOf;
/// sub-limiters, at least one should pass for this limiter to pass
TRewardLimitersList anyOf;
/// sub-limiters, none should pass for this limiter to pass
TRewardLimitersList noneOf;
CRewardLimiter():
numOfGrants(0),
dayOfWeek(0),
minLevel(0),
daysPassed(0),
heroLevel(0),
primary(4, 0)
{}
@@ -59,14 +81,47 @@ public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & numOfGrants;
h & dayOfWeek;
h & minLevel;
h & daysPassed;
h & heroExperience;
h & heroLevel;
h & manaPoints;
h & manaPercentage;
h & resources;
h & primary;
h & secondary;
h & artifacts;
h & creatures;
h & allOf;
h & anyOf;
h & noneOf;
}
};
class DLL_LINKAGE CRewardResetInfo
{
public:
CRewardResetInfo()
: period(0)
, visitors(false)
, rewards(false)
{}
/// if above zero, object state will be reset each resetDuration days
ui32 period;
/// if true - reset list of visitors (heroes & players) on reset
bool visitors;
/// if true - re-randomize rewards on a new week
bool rewards;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & period;
h & visitors;
h & rewards;
}
};
@@ -79,12 +134,16 @@ public:
TResources resources;
/// received experience
ui32 gainedExp;
si32 heroExperience;
/// received levels (converted into XP during grant)
ui32 gainedLevels;
si32 heroLevel;
/// mana given to/taken from hero, fixed value
si32 manaDiff;
/// if giving mana points puts hero above mana pool, any overflow will be multiplied by specified percentage
si32 manaOverflowFactor;
/// fixed value, in form of percentage from max
si32 manaPercentage;
@@ -100,6 +159,9 @@ public:
std::vector<si32> primary;
std::map<SecondarySkill, si32> secondary;
/// creatures that will be changed in hero's army
std::map<CreatureID, CreatureID> creaturesChange;
/// objects that hero may receive
std::vector<ArtifactID> artifacts;
std::vector<SpellID> spells;
@@ -117,8 +179,8 @@ public:
Component getDisplayedComponent(const CGHeroInstance * h) const;
CRewardInfo() :
gainedExp(0),
gainedLevels(0),
heroExperience(0),
heroLevel(0),
manaDiff(0),
manaPercentage(-1),
movePoints(0),
@@ -134,9 +196,10 @@ public:
h & removeObject;
h & manaPercentage;
h & movePercentage;
h & gainedExp;
h & gainedLevels;
h & heroExperience;
h & heroLevel;
h & manaDiff;
h & manaOverflowFactor;
h & movePoints;
h & primary;
h & secondary;
@@ -144,42 +207,44 @@ public:
h & artifacts;
h & spells;
h & creatures;
h & creaturesChange;
}
};
class DLL_LINKAGE CVisitInfo
class DLL_LINKAGE CRewardVisitInfo
{
public:
enum ERewardEventType
{
EVENT_INVALID,
EVENT_FIRST_VISIT,
EVENT_ALREADY_VISITED,
EVENT_NOT_AVAILABLE
};
CRewardLimiter limiter;
CRewardInfo reward;
/// Message that will be displayed on granting of this reward, if not empty
MetaString message;
/// Chance for this reward to be selected in case of random choice
si32 selectChance;
/// Event to which this reward is assigned
ERewardEventType visitType;
/// How many times this reward has been granted since last reset
si32 numOfGrants;
CVisitInfo():
selectChance(0),
numOfGrants(0)
{}
CRewardVisitInfo() = default;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & limiter;
h & reward;
h & message;
h & selectChance;
h & numOfGrants;
h & visitType;
}
};
namespace Rewardable
{
const std::array<std::string, 3> SelectModeString{"selectFirst", "selectPlayer", "selectRandom"};
const std::array<std::string, 3> SelectModeString{"selectFirst", "selectPlayer"};
const std::array<std::string, 5> VisitModeString{"unlimited", "once", "hero", "bonus", "player"};
}
@@ -188,20 +253,12 @@ namespace Rewardable
class DLL_LINKAGE CRewardableObject : public CArmedInstance
{
/// function that must be called if hero got level-up during grantReward call
void grantRewardAfterLevelup(const CVisitInfo & reward, const CGHeroInstance * hero) const;
void grantRewardAfterLevelup(const CRewardVisitInfo & reward, const CGHeroInstance * hero) const;
/// 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
};
void grantRewardBeforeLevelup(const CRewardVisitInfo & reward, const CGHeroInstance * hero) const;
public:
enum EVisitMode
{
VISIT_UNLIMITED, // any number of times. Side effect - object hover text won't contain visited/not visited text
@@ -211,37 +268,52 @@ 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
};
/// filters list of visit info and returns rewards that can be granted to current hero
virtual std::vector<ui32> getAvailableRewards(const CGHeroInstance * hero) const;
virtual std::vector<ui32> getAvailableRewards(const CGHeroInstance * hero, CRewardVisitInfo::ERewardEventType event ) const;
virtual void grantReward(ui32 rewardID, const CGHeroInstance * hero) const;
virtual void grantReward(ui32 rewardID, const CGHeroInstance * hero, bool markVisited) const;
virtual CVisitInfo getVisitInfo(int index, const CGHeroInstance *h) const;
virtual void triggerReset() const;
virtual void triggerRewardReset() const;
/// Rewards that can be granted by an object
std::vector<CVisitInfo> info;
/// MetaString's that contain text for messages for specific situations
/// Message that will be shown if player needs to select one of multiple rewards
MetaString onSelect;
MetaString onVisited;
MetaString onEmpty;
/// Rewards that can be applied by an object
std::vector<CRewardVisitInfo> info;
/// how reward will be selected, uses ESelectMode enum
ui8 selectMode;
/// contols who can visit an object, uses EVisitMode enum
ui8 visitMode;
/// reward selected by player
ui16 selectedReward;
/// object visitability info will be reset each resetDuration days
ui16 resetDuration;
/// how and when should the object be reset
CRewardResetInfo resetParameters;
/// if true - player can refuse visiting an object (e.g. Tomb)
bool canRefuse;
/// return true if this object was "cleared" before and no longer has rewards applicable to selected hero
/// unlike wasVisited, this method uses information not available to player owner, for example, if object was cleared by another player before
bool wasVisitedBefore(const CGHeroInstance * contextHero) const;
bool onceVisitableObjectCleared;
public:
EVisitMode getVisitMode() const;
ui16 getResetDuration() const;
void setPropertyDer(ui8 what, ui32 val) override;
std::string getHoverText(PlayerColor player) const override;
std::string getHoverText(const CGHeroInstance * hero) const override;
@@ -262,9 +334,6 @@ public:
/// applies player selection of reward
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
/// function that will be called once reward is fully granted to hero
virtual void onRewardGiven(const CGHeroInstance * hero) const;
void initObj(CRandomGenerator & rand) override;
CRewardableObject();
@@ -274,116 +343,18 @@ public:
h & static_cast<CArmedInstance&>(*this);
h & info;
h & canRefuse;
h & resetDuration;
h & resetParameters;
h & onSelect;
h & onVisited;
h & onEmpty;
h & visitMode;
h & selectMode;
h & selectedReward;
h & onceVisitableObjectCleared;
}
// for configuration/object setup
friend class CRandomRewardObjectInfo;
};
class DLL_LINKAGE CGPickable : public CRewardableObject //campfire, treasure chest, Flotsam, Shipwreck Survivor, Sea Chest
{
public:
void initObj(CRandomGenerator & rand) override;
CGPickable();
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CRewardableObject&>(*this);
}
};
class DLL_LINKAGE CGBonusingObject : public CRewardableObject //objects giving bonuses to luck/morale/movement
{
protected:
CVisitInfo getVisitInfo(int index, const CGHeroInstance *h) const override;
void grantReward(ui32 rewardID, const CGHeroInstance * hero) const override;
public:
void initObj(CRandomGenerator & rand) override;
CGBonusingObject();
void onHeroVisit(const CGHeroInstance *h) const override;
bool wasVisited(const CGHeroInstance * h) const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CRewardableObject&>(*this);
}
};
class DLL_LINKAGE CGOnceVisitable : public CRewardableObject // wagon, corpse, lean to, warriors tomb
{
public:
void initObj(CRandomGenerator & rand) override;
CGOnceVisitable();
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CRewardableObject&>(*this);
}
};
class DLL_LINKAGE CGVisitableOPH : public CRewardableObject //objects visitable only once per hero
{
public:
void initObj(CRandomGenerator & rand) override;
CGVisitableOPH();
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CRewardableObject&>(*this);
}
};
class DLL_LINKAGE CGVisitableOPW : public CRewardableObject //objects visitable once per week
{
protected:
void triggerRewardReset() const override;
public:
void initObj(CRandomGenerator & rand) override;
CGVisitableOPW();
void setPropertyDer(ui8 what, ui32 val) override;
void setRandomReward(CRandomGenerator & rand);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CRewardableObject&>(*this);
}
};
///Special case - magic spring that has two separate visitable entrances
class DLL_LINKAGE CGMagicSpring : public CGVisitableOPW
{
protected:
std::vector<ui32> getAvailableRewards(const CGHeroInstance * hero) const override;
public:
void initObj(CRandomGenerator & rand) override;
std::vector<int3> getVisitableOffsets() const;
int3 getVisitableOffset() const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CGVisitableOPW&>(*this);
}
};
//TODO:
// MAX

View File

@@ -32,15 +32,45 @@ namespace JsonRandom
if (value.isNumber())
return static_cast<si32>(value.Float());
if (!value["amount"].isNull())
return static_cast<si32>(value["amount"].Float());
return static_cast<si32>(loadValue(value["amount"], rng, defaultValue));
si32 min = static_cast<si32>(value["min"].Float());
si32 max = static_cast<si32>(value["max"].Float());
return rng.getIntRange(min, max)();
}
DLL_LINKAGE std::string loadKey(const JsonNode & value, CRandomGenerator & rng, std::string defaultValue)
{
if (value.isNull())
return defaultValue;
if (value.isString())
return value.String();
if (!value["type"].isNull())
return value["type"].String();
if (value["list"].isNull())
return defaultValue;
const auto & resourceList = value["list"].Vector();
if (resourceList.empty())
return defaultValue;
si32 index = rng.getIntRange(0, resourceList.size() - 1 )();
return resourceList[index].String();
}
TResources loadResources(const JsonNode & value, CRandomGenerator & rng)
{
TResources ret;
if (value.isVector())
{
for (const auto & entry : value.Vector())
ret += loadResource(entry, rng);
return ret;
}
for (size_t i=0; i<GameConstants::RESOURCE_QUANTITY; i++)
{
ret[i] = loadValue(value[GameConstants::RESOURCE_NAMES[i]], rng);
@@ -48,6 +78,18 @@ namespace JsonRandom
return ret;
}
TResources loadResource(const JsonNode & value, CRandomGenerator & rng)
{
std::string resourceName = loadKey(value, rng, "");
si32 resourceAmount = loadValue(value, rng, 0);
si32 resourceID(VLC->modh->identifiers.getIdentifier(value.meta, "resource", resourceName).get());
TResources ret;
ret[resourceID] = resourceAmount;
return ret;
}
std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng)
{
std::vector<si32> ret;
@@ -131,8 +173,6 @@ namespace JsonRandom
{
if (value.getType() == JsonNode::JsonType::DATA_STRING)
return SpellID(VLC->modh->identifiers.getIdentifier("spell", value).get());
if (value["type"].getType() == JsonNode::JsonType::DATA_STRING)
return SpellID(VLC->modh->identifiers.getIdentifier("spell", value["type"]).get());
vstd::erase_if(spells, [=](SpellID spell)
{

View File

@@ -32,7 +32,9 @@ namespace JsonRandom
};
DLL_LINKAGE si32 loadValue(const JsonNode & value, CRandomGenerator & rng, si32 defaultValue = 0);
DLL_LINKAGE std::string loadKey(const JsonNode & value, CRandomGenerator & rng, std::string defaultValue = "");
DLL_LINKAGE TResources loadResources(const JsonNode & value, CRandomGenerator & rng);
DLL_LINKAGE TResources loadResource(const JsonNode & value, CRandomGenerator & rng);
DLL_LINKAGE std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng);
DLL_LINKAGE std::map<SecondarySkill, si32> loadSecondary(const JsonNode & value, CRandomGenerator & rng);

View File

@@ -1523,32 +1523,6 @@ void CGWitchHut::serializeJsonOptions(JsonSerializeFormat & handler)
}
}
void CGMagicWell::onHeroVisit( const CGHeroInstance * h ) const
{
int message;
if(h->hasBonusFrom(Bonus::OBJECT,ID)) //has already visited Well today
{
message = 78;//"A second drink at the well in one day will not help you."
}
else if(h->mana < h->manaLimit())
{
giveDummyBonus(h->id);
cb->setManaPoints(h->id,h->manaLimit());
message = 77;
}
else
{
message = 79;
}
showInfoDialog(h, message);
}
std::string CGMagicWell::getHoverText(const CGHeroInstance * hero) const
{
return getObjectName() + " " + visitedTxt(hero->hasBonusFrom(Bonus::OBJECT,ID));
}
void CGObservatory::onHeroVisit( const CGHeroInstance * h ) const
{
InfoWindow iw;

View File

@@ -391,18 +391,6 @@ public:
}
};
class DLL_LINKAGE CGMagicWell : public CGObjectInstance //objects giving bonuses to luck/morale/movement
{
public:
void onHeroVisit(const CGHeroInstance * h) const override;
std::string getHoverText(const CGHeroInstance * hero) const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CGObjectInstance&>(*this);
}
};
class DLL_LINKAGE CGSirens : public CGObjectInstance
{
public:

View File

@@ -45,7 +45,6 @@ void registerTypesMapObjects1(Serializer &s)
s.template registerType<CGMonolith, CGWhirlpool>();
s.template registerType<CGObjectInstance, CGSignBottle>();
s.template registerType<CGObjectInstance, CGScholar>();
s.template registerType<CGObjectInstance, CGMagicWell>();
s.template registerType<CGObjectInstance, CGObservatory>();
s.template registerType<CGObjectInstance, CGKeys>();
s.template registerType<CGKeys, CGKeymasterTent>();
@@ -99,7 +98,6 @@ void registerTypesMapObjectTypes(Serializer &s)
REGISTER_GENERIC_HANDLER(CGArtifact);
REGISTER_GENERIC_HANDLER(CGBlackMarket);
REGISTER_GENERIC_HANDLER(CGBoat);
REGISTER_GENERIC_HANDLER(CGBonusingObject);
REGISTER_GENERIC_HANDLER(CGBorderGate);
REGISTER_GENERIC_HANDLER(CGBorderGuard);
REGISTER_GENERIC_HANDLER(CGCreature);
@@ -113,15 +111,11 @@ void registerTypesMapObjectTypes(Serializer &s)
REGISTER_GENERIC_HANDLER(CGLighthouse);
REGISTER_GENERIC_HANDLER(CGTerrainPatch);
REGISTER_GENERIC_HANDLER(CGMagi);
REGISTER_GENERIC_HANDLER(CGMagicSpring);
REGISTER_GENERIC_HANDLER(CGMagicWell);
REGISTER_GENERIC_HANDLER(CGMarket);
REGISTER_GENERIC_HANDLER(CGMine);
REGISTER_GENERIC_HANDLER(CGObelisk);
REGISTER_GENERIC_HANDLER(CGObservatory);
REGISTER_GENERIC_HANDLER(CGOnceVisitable);
REGISTER_GENERIC_HANDLER(CGPandoraBox);
REGISTER_GENERIC_HANDLER(CGPickable);
REGISTER_GENERIC_HANDLER(CGQuestGuard);
REGISTER_GENERIC_HANDLER(CGResource);
REGISTER_GENERIC_HANDLER(CGScholar);
@@ -135,8 +129,6 @@ void registerTypesMapObjectTypes(Serializer &s)
REGISTER_GENERIC_HANDLER(CGWhirlpool);
REGISTER_GENERIC_HANDLER(CGTownInstance);
REGISTER_GENERIC_HANDLER(CGUniversity);
REGISTER_GENERIC_HANDLER(CGVisitableOPH);
REGISTER_GENERIC_HANDLER(CGVisitableOPW);
REGISTER_GENERIC_HANDLER(CGWitchHut);
#undef REGISTER_GENERIC_HANDLER
@@ -162,12 +154,6 @@ void registerTypesMapObjects2(Serializer &s)
s.template registerType<CGTownBuilding, COPWBonus>();
s.template registerType<CGObjectInstance, CRewardableObject>();
s.template registerType<CRewardableObject, CGPickable>();
s.template registerType<CRewardableObject, CGBonusingObject>();
s.template registerType<CRewardableObject, CGVisitableOPH>();
s.template registerType<CRewardableObject, CGVisitableOPW>();
s.template registerType<CRewardableObject, CGOnceVisitable>();
s.template registerType<CGVisitableOPW, CGMagicSpring>();
s.template registerType<CGObjectInstance, CTeamVisited>();
s.template registerType<CTeamVisited, CGWitchHut>();