diff --git a/Mods/WoG/config/defaultMods.json b/Mods/WoG/config/defaultMods.json deleted file mode 100644 index 1887af91c..000000000 --- a/Mods/WoG/config/defaultMods.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "textData" : - { - "heroClass" : 18, - "artifact" : 171, - "creature" : 197, - "faction" : 9, - "hero" : 156, - "mapVersion" : 51 // WoG - } -} diff --git a/Mods/WoG/config/wog/artifacts.json b/Mods/WoG/config/wog/artifacts.json deleted file mode 100644 index ecdcf3561..000000000 --- a/Mods/WoG/config/wog/artifacts.json +++ /dev/null @@ -1,309 +0,0 @@ -{ - "art144": // placeholder for selection image - { - "index" : 144, - "type" : ["HERO"] - }, - "art145": // placeholder for lock image - { - "index" : 145, - "type" : ["HERO"] - }, - "axeOfSmashing": //TODO: move growing bonuses to this config, someday - { - "bonuses" : [ - { - "subtype" : "primSkill.attack", - "type" : "PRIMARY_SKILL", - "val" : 6, - "valueType" : "BASE_NUMBER" - } - ], - "index" : 146, - "type" : ["COMMANDER"], - "growing": - { - "bonusesPerLevel": - [ - { - "level": 6, - "bonus": - { - "type" : "PRIMARY_SKILL", - "subtype" : "primSkill.attack", - "val" : 1 - } - } - ] - } - }, - "mithrilMail": - { - "bonuses" : [ - { - "type" : "STACK_HEALTH", - "val" : 12, - "valueType" : "PERCENT_TO_ALL" - } - ], - "index" : 147, - "type" : ["COMMANDER"], - "growing": - { - "bonusesPerLevel": - [ - { - "level": 1, - "bonus": - { - "type" : "STACK_HEALTH", - "val" : 1 - } - } - ] - } - }, - "swordOfSharpness": - { - "bonuses" : [ - { - "subtype" : 0, - "type" : "CREATURE_DAMAGE", - "val" : 12, - "valueType" : "PERCENT_TO_ALL" - } - ], - "index" : 148, - "type" : ["COMMANDER"], - "growing": - { - "bonusesPerLevel": - [ - { - "level": 1, - "bonus": - { - "type" : "CREATURE_DAMAGE", - "val" : 1 - } - } - ] - } - }, - "helmOfImmortality": //TODO: implement - { - "index" : 149, - "type" : ["COMMANDER"] - }, - "pendantOfSorcery": - { - "bonuses" : [ - { - "type" : "CASTS", - "val" : 1, - "valueType" : "BASE_NUMBER" - } - ], - "index" : 150, - "type" : ["COMMANDER"], - "growing": - { - "bonusesPerLevel": - [ - { - "level": 10, - "bonus": - { - "type" : "CREATURE_ENCHANT_POWER", - "val" : 1 - } - } - ] - } - }, - "bootsOfHaste": - { - "bonuses" : [ - { - "type" : "STACKS_SPEED", - "val" : 1, - "valueType" : "BASE_NUMBER" - } - ], - "index" : 151, - "type" : ["COMMANDER"], - "growing": - { - "bonusesPerLevel": - [ - { - "level": 10, - "bonus": - { - "type" : "STACKS_SPEED", - "val" : 1 - } - } - ] - } - }, - "bowOfSeeking": - { - "index" : 152, - "type" : ["COMMANDER"], - "growing": - { - "thresholdBonuses": - [ - { - "level": 5, - "bonus": - { - "type" : "SHOOTER" - } - }, - { - "level": 25, - "bonus": - { - "type" : "NO_WALL_PENALTY" - } - }, - { - "level": 50, - "bonus": - { - "type" : "NO_DISTANCE_PENALTY" - } - } - ] - } - }, - "dragonEyeRing": //TODO: implement - { - "index" : 153, - "type" : ["COMMANDER"], - }, - "hardenedShield": - { - "bonuses" : [ - { - "subtype" : "primSkill.attack", - "type" : "PRIMARY_SKILL", - "val" : 6, - "valueType" : "BASE_NUMBER" - } - ], - "index" : 154, - "type" : ["COMMANDER"], - "growing": - { - "bonusesPerLevel": - [ - { - "level": 6, - "bonus": - { - "type" : "PRIMARY_SKILL", - "subtype" : "primSkill.defence", - "val" : 1 - } - } - ] - } - }, - "slavasRingOfPower": //TODO: implement if possible - { - "index" : 155, - "type" : ["COMMANDER"] - }, - "warlordsBanner": - { - "bonuses" : [ - { - "type" : "STACK_HEALTH", - "val" : 2, - "valueType" : "BASE_NUMBER" - } - ], - "index" : 156, - "type" : ["CREATURE"] - }, - "crimsonShieldOfRetribution": //TODO: implement - { - "index" : 157, - "type" : ["HERO"] - }, - "barbarianLordsAxeOfFerocity": //TODO: implement - { - "index" : 158, - "type" : ["HERO"], - "components": - [ - "ogresClubOfHavoc", - "targOfTheRampagingOgre", - "crownOfTheSupremeMagi", - "tunicOfTheCyclopsKing" - ] - }, - "dragonheart": - { - "index" : 159, - "type" : ["HERO"] - }, - "gateKey": - { - "index" : 160, - "type" : ["HERO"] - }, - "art161": - { - "index" : 161, - "type" : ["HERO"] - }, - "art162": - { - "index" : 162, - "type" : ["HERO"] - }, - "art163": - { - "index" : 163, - "type" : ["HERO"] - }, - "art164": - { - "index" : 164, - "type" : ["HERO"] - }, - "art165": - { - "index" : 165, - "type" : ["HERO"] - }, - "art166": - { - "index" : 166, - "type" : ["HERO"] - }, - "art167": - { - "index" : 167, - "type" : ["HERO"] - }, - "art168": - { - "index" : 168, - "type" : ["HERO"] - }, - "art169": - { - "index" : 169, - "type" : ["HERO"] - }, - "art170": - { - "index" : 170, - "type" : ["HERO"] - } -} diff --git a/Mods/WoG/config/wog/creatureBanks.json b/Mods/WoG/config/wog/creatureBanks.json deleted file mode 100644 index 9b4d61409..000000000 --- a/Mods/WoG/config/wog/creatureBanks.json +++ /dev/null @@ -1,817 +0,0 @@ -{ - "core:creatureBank" : { - "types" : { - "huntingLodge" : { - "name" : "Hunting Lodge", - "index" : 11, - "levels": [ - { - "chance": 30, - "guards": [ - { "amount": 3, "type" : "dendroidGuard" }, - { "amount": 4, "type" : "woodElf" }, - { "amount": 3, "type" : "dendroidGuard" }, - { "amount": 4, "type" : "woodElf" } - ], - "combat_value": 100, - "reward" : { - "resources": - { - "wood" : 15, - "gold" : 500 - } - } - }, - { - "chance": 30, - "guards": [ - { "amount": 4, "type" : "dendroidGuard" }, - { "amount": 6, "type" : "woodElf" }, - { "amount": 4, "type" : "dendroidGuard" }, - { "amount": 6, "type" : "woodElf" } - ], - "combat_value": 150, - "reward" : { - "resources": - { - "wood" : 20, - "gold" : 500 - }, - "value": 3000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 5, "type" : "dendroidGuard" }, - { "amount": 8, "type" : "woodElf" }, - { "amount": 5, "type" : "dendroidGuard" }, - { "amount": 8, "type" : "woodElf" } - ], - "combat_value": 200, - "reward" : { - "resources": - { - "wood" : 25, - "gold" : 500 - }, - "value": 4000 - } - }, - { - "chance": 10, - "guards": [ - { "amount": 6, "type" : "dendroidGuard" }, - { "amount": 10, "type" : "woodElf" }, - { "amount": 6, "type" : "dendroidGuard" }, - { "amount": 10, "type" : "woodElf" } - ], - "combat_value": 300, - "reward" : { - "resources": - { - "wood" : 40, - "gold" : 1000 - }, - "value": 6000 - } - } - ] - }, - "snowGrotto" : - { - "name" : "Snow-covered Grotto", - "index" : 12, - "levels": [ - { - "chance": 30, - "guards": [ - { "amount": 5, "type" : "ironGolem" }, - { "amount": 20, "type" : "masterGremlin" }, - { "amount": 5, "type" : "ironGolem" }, - { "amount": 20, "type" : "masterGremlin" } - ], - "combat_value": 100, - "reward" : { - "resources": - { - "ore" : 15, - "gold" : 500 - } - } - }, - { - "chance": 30, - "guards": [ - { "amount": 6, "type" : "ironGolem" }, - { "amount": 30, "type" : "masterGremlin" }, - { "amount": 6, "type" : "ironGolem" }, - { "amount": 30, "type" : "masterGremlin" } - ], - "combat_value": 150, - "reward" : { - "resources": - { - "ore" : 20, - "gold" : 500 - }, - "value": 3000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 7, "type" : "ironGolem" }, - { "amount": 40, "type" : "masterGremlin" }, - { "amount": 7, "type" : "ironGolem" }, - { "amount": 40, "type" : "masterGremlin" } - ], - "combat_value": 200, - "reward" : { - "resources": - { - "ore" : 30, - "gold" : 500 - }, - "value": 4000 - } - }, - { - "chance": 10, - "guards": [ - { "amount": 8, "type" : "ironGolem" }, - { "amount": 50, "type" : "masterGremlin" }, - { "amount": 8, "type" : "ironGolem" }, - { "amount": 50, "type" : "masterGremlin" } - ], - "combat_value": 300, - "reward" : { - "resources": - { - "ore" : 40, - "gold" : 1000 - }, - "value": 6000 - } - } - ] - }, - "martialSpiritPalace" : - { - "name" : "Palace of Martial Spirit", - "index" : 13, - "levels": [ - { - "chance": 30, - "guards": [ - { "amount": 10, "type" : "godWar" }, - { "amount": 10, "type" : "godWar" }, - { "amount": 10, "type" : "godWar" }, - { "amount": 10, "type" : "godWar" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godWar" } - ], - "value": 38000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 15, "type" : "godWar" }, - { "amount": 15, "type" : "godWar" }, - { "amount": 15, "type" : "godWar" }, - { "amount": 15, "type" : "godWar" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godWar" } - ], - "value": 57000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 20, "type" : "godWar" }, - { "amount": 20, "type" : "godWar" }, - { "amount": 20, "type" : "godWar" }, - { "amount": 20, "type" : "godWar" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godWar" } - ], - "value": 75000 - } - }, - { - "chance": 10, - "guards": [ - { "amount": 30, "type" : "godWar" }, - { "amount": 30, "type" : "godWar" }, - { "amount": 30, "type" : "godWar" }, - { "amount": 30, "type" : "godWar" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godWar" } - ], - "value": 90000 - } - } - ] - }, - "pacificationCitadel" : - { - "name" : "Citadel of Pacification", - "index" : 14, - "levels": [ - { - "chance": 30, - "guards": [ - { "amount": 10, "type" : "godPeace" }, - { "amount": 10, "type" : "godPeace" }, - { "amount": 10, "type" : "godPeace" }, - { "amount": 10, "type" : "godPeace" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godPeace" } - ], - "value": 38000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 15, "type" : "godPeace" }, - { "amount": 15, "type" : "godPeace" }, - { "amount": 15, "type" : "godPeace" }, - { "amount": 15, "type" : "godPeace" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godPeace" } - ], - "value": 57000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 20, "type" : "godPeace" }, - { "amount": 20, "type" : "godPeace" }, - { "amount": 20, "type" : "godPeace" }, - { "amount": 20, "type" : "godPeace" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godPeace" } - ], - "value": 75000 - } - }, - { - "chance": 10, - "guards": [ - { "amount": 30, "type" : "godPeace" }, - { "amount": 30, "type" : "godPeace" }, - { "amount": 30, "type" : "godPeace" }, - { "amount": 30, "type" : "godPeace" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godPeace" } - ], - "value": 90000 - } - } - ] - }, - "magiciansMonastery" : - { - "name" : "Monastery of Magicians", - "index" : 15, - "levels": [ - { - "chance": 30, - "guards": [ - { "amount": 10, "type" : "godMana" }, - { "amount": 10, "type" : "godMana" }, - { "amount": 10, "type" : "godMana" }, - { "amount": 10, "type" : "godMana" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godMana" } - ], - "value": 38000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 15, "type" : "godMana" }, - { "amount": 15, "type" : "godMana" }, - { "amount": 15, "type" : "godMana" }, - { "amount": 15, "type" : "godMana" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godMana" } - ], - "value": 57000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 20, "type" : "godMana" }, - { "amount": 20, "type" : "godMana" }, - { "amount": 20, "type" : "godMana" }, - { "amount": 20, "type" : "godMana" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godMana" } - ], - "value": 75000 - } - }, - { - "chance": 10, - "guards": [ - { "amount": 30, "type" : "godMana" }, - { "amount": 30, "type" : "godMana" }, - { "amount": 30, "type" : "godMana" }, - { "amount": 30, "type" : "godMana" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godMana" } - ], - "value": 90000 - } - } - ] - }, - "legendsLibrary" : - { - "name" : "Library of Legends", - "index" : 16, - "levels": [ - { - "chance": 30, - "guards": [ - { "amount": 10, "type" : "godLore" }, - { "amount": 10, "type" : "godLore" }, - { "amount": 10, "type" : "godLore" }, - { "amount": 10, "type" : "godLore" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godLore" } - ], - "value": 38000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 15, "type" : "godLore" }, - { "amount": 15, "type" : "godLore" }, - { "amount": 15, "type" : "godLore" }, - { "amount": 15, "type" : "godLore" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godLore" } - ], - "value": 57000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 20, "type" : "godLore" }, - { "amount": 20, "type" : "godLore" }, - { "amount": 20, "type" : "godLore" }, - { "amount": 20, "type" : "godLore" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godLore" } - ], - "value": 75000 - } - }, - { - "chance": 10, - "guards": [ - { "amount": 30, "type" : "godLore" }, - { "amount": 30, "type" : "godLore" }, - { "amount": 30, "type" : "godLore" }, - { "amount": 30, "type" : "godLore" } - ], - "combat_value": 2000, - "reward" : { - "creatures": [ - { "amount": 1, "type" : "godLore" } - ], - "value": 90000 - } - } - ] - }, - "transylvanianTavern" : - { - "name" : "Transylvanian Tavern", - "index" : 17, - "levels": [ - { - "chance": 30, - "guards": [ - { "amount": 2, "type" : "vampire" }, - { "amount": 2, "type" : "vampire" }, - { "amount": 2, "type" : "vampire" }, - { "amount": 2, "type" : "vampire" }, - { "amount": 2, "type" : "vampire" } - ], - "combat_value": 100, - "reward" : { - "resources": - { - "gold" : 1500 - }, - "creatures": [ - { "amount": 3, "type" : "vampireLord" } - ] - } - }, - { - "chance": 30, - "guards": [ - { "amount": 3, "type" : "vampire" }, - { "amount": 3, "type" : "vampire" }, - { "amount": 3, "type" : "vampire" }, - { "amount": 3, "type" : "vampire" }, - { "amount": 3, "type" : "vampire" } - ], - "combat_value": 150, - "reward" : { - "resources": - { - "gold" : 2500 - }, - "creatures": [ - { "amount": 5, "type" : "vampireLord" } - ], - "value": 3000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 4, "type" : "vampire" }, - { "amount": 4, "type" : "vampire" }, - { "amount": 4, "type" : "vampire" }, - { "amount": 4, "type" : "vampire" }, - { "amount": 4, "type" : "vampire" } - ], - "combat_value": 200, - "reward" : { - "resources": - { - "gold" : 3500 - }, - "creatures": [ - { "amount": 7, "type" : "vampireLord" } - ], - "value": 4000 - } - }, - { - "chance": 10, - "guards": [ - { "amount": 6, "type" : "vampire" }, - { "amount": 6, "type" : "vampire" }, - { "amount": 6, "type" : "vampire" }, - { "amount": 6, "type" : "vampire" }, - { "amount": 6, "type" : "vampire" } - ], - "combat_value": 250, - "reward" : { - "resources": - { - "gold" : 5000 - }, - "creatures": [ - { "amount": 9, "type" : "vampireLord" } - ], - "value": 5000 - } - } - ] - }, - "homeofthebat" : - { - "name" : "Home of the Bat", - "index" : 18, - "levels": [ - { - "chance": 30, - "guards": [ - { "amount": 2, "type" : "vampire" }, - { "amount": 2, "type" : "vampire" }, - { "amount": 2, "type" : "vampire", "upgrade_chance": 50 }, - { "amount": 2, "type" : "vampire" }, - { "amount": 2, "type" : "vampire" } - ], - "combat_value": 100, - "reward" : { - "resources": - { - "mercury" : 2, - "sulfur" : 2, - "crystal" : 2, - "gems" : 2 - }, - "creatures": [ - { "amount": 3, "type" : "vampireLord" } - ] - } - }, - { - "chance": 30, - "guards": [ - { "amount": 3, "type" : "vampire" }, - { "amount": 3, "type" : "vampire" }, - { "amount": 3, "type" : "vampire", "upgrade_chance": 70 }, - { "amount": 3, "type" : "vampire" }, - { "amount": 3, "type" : "vampire" } - ], - "combat_value": 150, - "reward" : { - "resources": - { - "mercury" : 3, - "sulfur" : 3, - "crystal" : 3, - "gems" : 3 - }, - "creatures": [ - { "amount": 5, "type" : "vampireLord" } - ], - "value": 3000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 4, "type" : "vampire" }, - { "amount": 4, "type" : "vampire" }, - { "amount": 4, "type" : "vampire", "upgrade_chance": 80 }, - { "amount": 4, "type" : "vampire" }, - { "amount": 4, "type" : "vampire" } - ], - "combat_value": 200, - "reward" : { - "resources": - { - "mercury" : 4, - "sulfur" : 4, - "crystal" : 4, - "gems" : 4 - }, - "creatures": [ - { "amount": 7, "type" : "vampireLord" } - ], - "value": 4000 - } - }, - { - "chance": 10, - "guards": [ - { "amount": 6, "type" : "vampire" }, - { "amount": 6, "type" : "vampire" }, - { "amount": 6, "type" : "vampire", "upgrade_chance": 50 }, - { "amount": 6, "type" : "vampire" }, - { "amount": 6, "type" : "vampire" } - ], - "combat_value": 250, - "reward" : { - "resources": - { - "mercury" : 5, - "sulfur" : 5, - "crystal" : 5, - "gems" : 5 - }, - "creatures": [ - { "amount": 9, "type" : "vampireLord" } - ], - "value": 5000 - } - } - ] - }, - "lostBottle" : - { - "name" : "Lost Bottle", - "index" : 19, - "levels": [ - { - "chance": 30, - "guards": [ - { "amount": 2, "type" : "genie" }, - { "amount": 2, "type" : "genie" }, - { "amount": 2, "type" : "genie", "upgrade_chance": 50 }, - { "amount": 2, "type" : "genie" }, - { "amount": 2, "type" : "genie" } - ], - "combat_value": 100, - "reward" : { - "resources": - { - "mercury" : 2, - "sulfur" : 2, - "crystal" : 2, - "gems" : 2 - }, - "creatures": [ - { "amount": 3, "type" : "genie" } - ] - } - }, - { - "chance": 30, - "guards": [ - { "amount": 3, "type" : "genie" }, - { "amount": 3, "type" : "genie" }, - { "amount": 3, "type" : "genie", "upgrade_chance": 70 }, - { "amount": 3, "type" : "genie" }, - { "amount": 3, "type" : "genie" } - ], - "combat_value": 150, - "reward" : { - "resources": - { - "mercury" : 3, - "sulfur" : 3, - "crystal" : 3, - "gems" : 3 - }, - "creatures": [ - { "amount": 5, "type" : "genie" } - ], - "value": 3000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 4, "type" : "genie" }, - { "amount": 4, "type" : "genie" }, - { "amount": 4, "type" : "genie", "upgrade_chance": 80 }, - { "amount": 4, "type" : "genie" }, - { "amount": 4, "type" : "genie" } - ], - "combat_value": 200, - "reward" : { - "resources": - { - "mercury" : 4, - "sulfur" : 4, - "crystal" : 4, - "gems" : 4 - }, - "creatures": [ - { "amount": 7, "type" : "genie" } - ], - "value": 4000 - } - }, - { - "chance": 10, - "guards": [ - { "amount": 6, "type" : "genie" }, - { "amount": 6, "type" : "genie" }, - { "amount": 6, "type" : "genie", "upgrade_chance": 90 }, - { "amount": 6, "type" : "genie" }, - { "amount": 6, "type" : "genie" } - ], - "combat_value": 250, - "reward" : { - "resources": - { - "mercury" : 5, - "sulfur" : 5, - "crystal" : 5, - "gems" : 5 - }, - "creatures": [ - { "amount": 9, "type" : "genie" } - ], - "value": 5000 - } - } - ] - }, - "grotto" : - { - "name" : "Grotto", - "index" : 20, - "levels": [ - { - "chance": 30, - "guards": [ - { "amount": 3, "type" : "harpy" }, - { "amount": 3, "type" : "beholder" }, - { "amount": 3, "type" : "harpy" }, - { "amount": 3, "type" : "beholder" } - ], - "combat_value": 200, - "reward" : { - "resources": - { - "ore" : 20, - "gold" : 1000 - } - } - }, - { - "chance": 30, - "guards": [ - { "amount": 4, "type" : "harpy" }, - { "amount": 4, "type" : "beholder" }, - { "amount": 4, "type" : "harpy" }, - { "amount": 4, "type" : "beholder" } - ], - "combat_value": 300, - "reward" : { - "resources": - { - "ore" : 25, - "gold" : 1000 - }, - "value": 3000 - } - }, - { - "chance": 30, - "guards": [ - { "amount": 5, "type" : "harpy" }, - { "amount": 5, "type" : "beholder" }, - { "amount": 5, "type" : "harpy" }, - { "amount": 5, "type" : "beholder" } - ], - "combat_value": 400, - "reward" : { - "resources": - { - "ore" : 30, - "gold" : 1500 - }, - "value": 4000 - } - }, - { - "chance": 10, - "guards": [ - { "amount": 6, "type" : "harpy" }, - { "amount": 6, "type" : "beholder" }, - { "amount": 6, "type" : "harpy" }, - { "amount": 6, "type" : "beholder" } - ], - "combat_value": 500, - "reward" : { - "resources": - { - "ore" : 35, - "gold" : 2000 - }, - "value": 5000 - } - } - ] - } - } - } -} diff --git a/Mods/WoG/config/wog/creatures.json b/Mods/WoG/config/wog/creatures.json deleted file mode 100644 index ae2eab1a1..000000000 --- a/Mods/WoG/config/wog/creatures.json +++ /dev/null @@ -1,1586 +0,0 @@ -{ - "supremeArchangel" : - { - "index": 150, - "level": 8, - "faction": "castle", - "abilities": - { - "resurrection100hp" : - { - "type" : "SPECIFIC_SPELL_POWER", - "subtype" : "spell.resurrection", - "val" : 100 - }, - "resurrects" : - { - "type" : "SPELLCASTER", - "subtype" : "spell.resurrection" - }, - "fearless" : - { - "type" : "FEARLESS" - }, - "spellpoints" : - { - "type" : "CASTS", - "val" : 2 - } - }, - "graphics" : - { - "animation": "ZM150Z.DEF" - }, - "sound" : - { - "attack": "AAGLATTK.wav", - "defend": "AAGLDFND.wav", - "killed": "AAGLKILL.wav", - "move": "AAGLMOVE.wav", - "wince": "AAGLWNCE.wav" - } - }, - "diamondDragon" : - { - "index": 151, - "level": 8, - "faction": "rampart", - "abilities": - { - "dragon" : - { - "type" : "DRAGON_NATURE" - }, - "fireBreath" : - { - "type" : "TWO_HEX_ATTACK_BREATH" - }, - "spellImmunity" : - { - "type" : "LEVEL_SPELL_IMMUNITY", - "val" : 5 - }, - "fearless" : - { - "type" : "FEARLESS" - }, - }, - "graphics" : - { - "animation": "ZM151Z.DEF", - }, - "sound" : - { - "attack": "GODRATTK.wav", - "defend": "GODRDFND.wav", - "killed": "GODRKILL.wav", - "move": "GODRMOVE.wav", - "wince": "GODRWNCE.wav" - } - }, - "lordofThunder" : - { - "index": 152, - "level": 8, - "faction": "tower", - "abilities" : - { - "additionalAttack" : - { - "type" : "ADDITIONAL_ATTACK", - "val" : 1 - }, - "thunderStrength" : - { - "type" : "SPECIFIC_SPELL_POWER", - "subtype" : "spell.thunderbolt", - "val" : 10 - }, - "thunderOnAttack" : - { - "type" : "SPELL_AFTER_ATTACK", - "subtype" : "spell.thunderbolt", - "val" : 20 - }, - "castsAirShield" : - { - "type" : "ENCHANTED", - "subtype" : "spell.airShield", - "val" : 3, - "addInfo" : 1 - }, - "noPenalty" : - { - "type" : "NO_WALL_PENALTY" - }, - "noDistancePenalty" : - { - "type" : "NO_DISTANCE_PENALTY" - } - }, - "graphics" : - { - "animation": "ZM152Z.DEF", - "missile" : - { - "projectile": "CPRGTIX.DEF" - } - }, - "sound" : - { - "attack": "GTITATTK.wav", - "defend": "GTITDFND.wav", - "killed": "GTITKILL.wav", - "move": "GTITMOVE.wav", - "shoot": "GTITSHOT.wav", - "wince": "GTITWNCE.wav" - } - }, - "hellBaron" : - { - "index": 153, - "level": 8, - "faction": "inferno", - "abilities": - { - "fearless" : - { - "type" : "FEARLESS" - }, - "petrify" : - { - "type" : "SPELL_AFTER_ATTACK", - "subtype" : "spell.stoneGaze", - "val" : 50, - "addInfo" : 3 - }, - "descreaseLuck" : - { - "type" : "LUCK", - "effectRange" : "ONLY_ENEMY_ARMY", - "val" : -1 - }, - "blockRetaliation" : - { - "type" : "BLOCKS_RETALIATION" - } - }, - "graphics" : - { - "missile" : null, - "animation": "ZM153Z.DEF" - }, - "sound" : - { - "attack": "ADVLATTK.wav", - "defend": "ADVLDFND.wav", - "killed": "ADVLKILL.wav", - "move": "ADVLMOVE.wav", - "wince": "ADVLWNCE.wav", - "startMoving": "ADVLEXT1.wav", - "endMoving": "ADVLEXT2.wav" - } - }, - "bloodDragon" : - { - "index": 154, - "level": 8, - "faction": "necropolis", - "abilities": - { - "drainsLife" : - { - "type" : "LIFE_DRAIN", - "val" : 40 - }, - "dragon" : - { - "type" : "DRAGON_NATURE" - }, - "descreaseLuck" : - { - "type" : "LUCK", - "effectRange" : "ONLY_ENEMY_ARMY", - "val" : -1 - }, - "fearless" : - { - "type" : "FEARLESS" - } - }, - "graphics" : - { - "animation": "ZM154Z.DEF" - }, - "sound" : - { - "attack": "GHDRATTK.wav", - "defend": "GHDRDFND.wav", - "killed": "GHDRKILL.wav", - "move": "GHDRMOVE.wav", - "wince": "GHDRWNCE.wav" - } - }, - "darknessDragon" : - { - "index": 155, - "level": 8, - "faction": "dungeon", - "abilities": - { - "dragon" : - { - "type" : "DRAGON_NATURE" - }, - "spellImmunity" : - { - "type" : "LEVEL_SPELL_IMMUNITY", - "val" : 5 - }, - "fearless" : - { - "type" : "FEARLESS" - }, - "fear" : - { - "type" : "FEAR" - }, - "strikeAndReturn" : - { - "type" : "RETURN_AFTER_STRIKE", - "val" : 0, - "valueType" : "BASE_NUMBER" - } - }, - "graphics" : - { - "animation": "ZM155Z.DEF" - }, - "sound" : - { - "attack": "BKDRATTK.wav", - "defend": "BKDRDFND.wav", - "killed": "BKDRKILL.wav", - "move": "BKDRMOVE.wav", - "wince": "BKDRWNCE.wav" - } - }, - "ghostBehemoth" : - { - "index": 156, - "level": 8, - "faction": "stronghold", - "abilities" : - { - "canFly" : - { - "type" : "FLYING" - }, - "fearless" : - { - "type" : "FEARLESS" - }, - "fear" : - { - "type" : "FEAR" - }, - "selfMorale" : - { - "type" : "SELF_MORALE", - "effectRange" : "ONLY_ENEMY_ARMY", - "val" : 1 - }, - "extraRetaliation" : - { - "type" : "ADDITIONAL_RETALIATION", - "val" : 2 - }, - "reduceDefence" : - { - "type" : "ENEMY_DEFENCE_REDUCTION", - "val" : 100 - } - }, - "graphics" : - { - "animation": "ZM156Z.DEF" - }, - "sound" : - { - "attack": "BMTHATTK.wav", - "defend": "BMTHDFND.wav", - "killed": "BMTHKILL.wav", - "move": "BMTHMOVE.wav", - "wince": "BMTHWNCE.wav" - } - }, - "hellHydra" : - { - "index": 157, - "level": 8, - "faction": "fortress", - "abilities": - { - "acidBreath" : - { - "type" : "ACID_BREATH", - "val" : 25, - "addInfo" : 20 - }, - "reduceDefence" : - { - "type" : "SPELL_AFTER_ATTACK", - "subtype" : "spell.acidBreath", - "val" : 100 - }, - "noRetaliation" : - { - "type" : "BLOCKS_RETALIATION" - }, - "regenerates" : - { - "type" : "HP_REGENERATION", - "val" : 40 - }, - "fearless" : - { - "type" : "FEARLESS" - }, - "SHOOTER" : null - }, - "graphics" : - { - "animation": "ZM157Z.DEF" - }, - "sound" : - { - "attack": "CHYDATTK.wav", - "defend": "CHYDDFND.wav", - "killed": "CHYDKILL.wav", - "move": "CHYDMOVE.wav", - "wince": "CHYDWNCE.wav" - } - }, - "sacredPhoenix" : - { - "index": 158, - "level": 8, - "faction": "conflux", - "abilities": - { - "rebirthOnce" : - { - "type" : "CASTS", - "val" : 1 - }, - "immuneToFire" : - { - "type" : "FIRE_IMMUNITY", - "subtype" : 0 //this IS important - }, - "rebirth" : - { - "type" : "REBIRTH", - "val" : 100 - }, - "fireShield" : - { - "type" : "FIRE_SHIELD", - "subtype" : 100, - "val" : 100 - }, - "spellSlayer" : - { - "type" : "ENCHANTED", - "subtype" : "spell.slayer", - "val" : 3, - "addInfo" : 100 - }, - "fearless" : - { - "type" : "FEARLESS" - }, - "spellpower" : - { - "type" : "CREATURE_SPELL_POWER", - "val" : 100 - } - }, - "graphics" : - { - "animation": "ZM158Z.DEF" - }, - "sound" : - { - "attack": "PHOEATTK.wav", - "defend": "PHOEDFND.wav", - "killed": "PHOEKILL.wav", - "move": "PHOEMOVE.wav", - "wince": "PHOEWNCE.wav" - } - }, - "ghost" : - { - "index": 159, - "growth" : 7, - "level": 3, - "faction": "necropolis", - "abilities": - { - "fearless" : - { - "type" : "FEARLESS" - }, - "armageddonImmunity" : - { - "type" : "SPELL_IMMUNITY", - "subtype" : "spell.armageddon" - }, - }, - "graphics" : - { - "animation": "ZM159G.DEF" - }, - "sound" : - { - "attack": "WRTHATTK.wav", - "defend": "WRTHDFND.wav", - "killed": "WRTHKILL.wav", - "move": "WRTHMOVE.wav", - "wince": "WRTHWNCE.wav" - } - }, - "godWar" : - { - "disabled" : true, - "index": 160, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM160G.DEF" - } - }, - "godPeace" : - { - "disabled" : true, - "index": 161, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM161G.DEF" - } - }, - "godMana" : - { - "disabled" : true, - "index": 162, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM162G.DEF" - } - }, - "godLore" : - { - "disabled" : true, - "index": 163, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM163G.DEF" - } - }, - "minotaurKing2" :// WTF is this? Same ID as Minotaur King from Dungeon - { - "index": 164, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM164GD.DEF" - }, - "sound" : - { - "attack": "SGLMATTK.wav", - "defend": "SGLMDFND.wav", - "killed": "SGLMKILL.wav", - "move": "SGLMMOVE.wav", - "wince": "SGLMWNCE.wav" - } - }, - "mineralElemental" : - { - "index": 165, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM165GD.DEF" - }, - "sound" : - { - "attack": "SGLMATTK.wav", - "defend": "SGLMDFND.wav", - "killed": "SGLMKILL.wav", - "move": "SGLMMOVE.wav", - "wince": "SGLMWNCE.wav" - } - }, - "electricityElemental" : - { - "index": 166, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM166GD.DEF" - }, - "sound" : - { - "attack": "SGLMATTK.wav", - "defend": "SGLMDFND.wav", - "killed": "SGLMKILL.wav", - "move": "SGLMMOVE.wav", - "wince": "SGLMWNCE.wav" - } - }, - "ancientBasilisk" : - { - "index": 167, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM167GD.DEF" - }, - "sound" : - { - "attack": "SGLMATTK.wav", - "defend": "SGLMDFND.wav", - "killed": "SGLMKILL.wav", - "move": "SGLMMOVE.wav", - "wince": "SGLMWNCE.wav" - } - }, - "gorynych" : - { - "index": 168, - "level": 7, - "faction": "neutral", - "abilities": - { - "canFly" : - { - "type" : "FLYING" - }, - "noRetaliation" : - { - "type" : "BLOCKS_RETALIATION" - }, - "fearless" : - { - "type" : "FEARLESS" - } - }, - "graphics" : - { - "animation": "ZM168DG.DEF" - }, - "sound" : - { - "attack": "BKDRATTK.wav", - "defend": "BKDRDFND.wav", - "killed": "BKDRKILL.wav", - "move": "BKDRMOVE.wav", - "wince": "BKDRWNCE.wav" - } - }, - "warZealot" : - { - "index": 169, - "level": 6, - "growth" : 3, - "faction": "castle", - "abilities": - { - "spellMagicMirror" : - { - "type" : "ENCHANTED", - "subtype" : "spell.magicMirror", - "val" : 3, - "addInfo" : 100 - }, - "fearless" : - { - "type" : "FEARLESS" - } - }, - "graphics" : - { - "animation": "ZM169ZL.DEF", - "missile" : - { - "projectile": "CPRZEAX.DEF" - } - }, - "sound" : - { - "attack": "ZELTATTK.wav", - "defend": "ZELTDFND.wav", - "killed": "ZELTKILL.wav", - "move": "ZELTMOVE.wav", - "shoot": "ZELTSHOT.wav", - "wince": "ZELTWNCE.wav" - } - }, - "arcticSharpshooter" : - { - "index": 170, - "level": 5, - "faction": "neutral", - "graphics" : - { - "animation": "ZM170SW.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - }, - "sound" : - { - "attack": "HCRSATTK.wav", - "defend": "HCRSDFND.wav", - "killed": "HCRSKILL.wav", - "move": "HCRSMOVE.wav", - "shoot": "HCRSSHOT.wav", - "wince": "HCRSWNCE.wav" - } - }, - "lavaSharpshooter" : - { - "index": 171, - "level": 5, - "faction": "neutral", - "graphics" : - { - "animation": "ZM171SR.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - }, - "sound" : - { - "attack": "HCRSATTK.wav", - "defend": "HCRSDFND.wav", - "killed": "HCRSKILL.wav", - "move": "HCRSMOVE.wav", - "shoot": "HCRSSHOT.wav", - "wince": "HCRSWNCE.wav" - } - }, - "nightmare" : - { - "index": 172, - "level": 6, - "faction": "neutral", - "abilities": - { - "deathStare" : - { - "type" : "DEATH_STARE", - "subtype" : 0, - "val" : 10 - }, - "mindImmunity" : - { - "type" : "MIND_IMMUNITY" - }, - "fearless" : - { - "type" : "FEARLESS" - } - }, - "upgrades": ["hellSteed"], - "graphics" : - { - "animation": "ZM172N.DEF" - }, - "sound" : - { - "attack": "BGORATTK.wav", - "defend": "BGORDFND.wav", - "killed": "BGORKILL.wav", - "move": "BGORMOVE.wav", - "wince": "BGORWNCE.wav" - } - }, - "santaGremlin" : - { - "index": 173, - "level": 3, - "faction": "tower", - "growth" : 16, - "abilities": - { - "castsImplosion" : - { - "type" : "SPELLCASTER", - "subtype" : "spell.iceBolt", - "addInfo" : 2, - "val" : 2 - }, - "spellpower" : - { - "type" : "CREATURE_SPELL_POWER", - "val" : 100 - }, - "casts" : - { - "type" : "CASTS", - "val" : 12 - }, - }, - "graphics" : - { - "animation": "ZM173M.DEF" - }, - "sound" : - { - "attack": "AAGLATTK.wav", - "defend": "AAGLDFND.wav", - "killed": "AAGLKILL.wav", - "move": "AAGLMOVE.wav", - "wince": "AAGLWNCE.wav" - } - }, - "paladin1" : - { - "special" : true, - "index": 174, - "level": 0, - "faction": "neutral", - "abilities": - { - "magicResistance" : - { - "type" : "MAGIC_RESISTANCE", - "val" : 5 - }, - "castsAmount" : - { - "type" : "CASTS", - "val" : 1 - }, - "enchant" : - { - "type" : "CREATURE_ENCHANT_POWER", - "val" : 1 - }, - "spellpower" : - { - "type" : "CREATURE_SPELL_POWER", - "val" : 100 - }, - "canCast" : - { - "type" : "SPELLCASTER", - "subtype" : "spell.cure", - "val" : 3 - } - }, - "graphics" : - { - "animation": "ZM174NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - }, - "sound" : - { - "attack": "CRUSATTK.wav", - "defend": "CRUSDFND.wav", - "killed": "CRUSKILL.wav", - "move": "CRUSMOVE.wav", - "wince": "CRUSWNCE.wav" - } - }, - "hierophant1" : - { - "special" : true, - "index": 175, - "level": 0, - "faction": "neutral", - "abilities": - { - "magicResistance" : - { - "type" : "MAGIC_RESISTANCE", - "val" : 5 - }, - "castsAmount" : - { - "type" : "CASTS", - "val" : 1 - }, - "enchant" : - { - "type" : "CREATURE_ENCHANT_POWER", - "val" : 1 - }, - "spellpower" : - { - "type" : "CREATURE_SPELL_POWER", - "val" : 100 - }, - "canCast" : - { - "type" : "SPELLCASTER", - "subtype" : "spell.shield", - "val" : 3 - } - }, - "graphics" : - { - "animation": "ZM175NPC.DEF", - "missile" : - { - "projectile": "CPRZEAX.DEF" - } - }, - "sound" : - { - "attack": "MONKATTK.wav", - "defend": "MONKDFND.wav", - "killed": "MONKKILL.wav", - "move": "MONKMOVE.wav", - "shoot": "MONKSHOT.wav", - "wince": "MONKWNCE.wav" - } - }, - "templeGuardian1" : - { - "special" : true, - "index": 176, - "level": 0, - "faction": "neutral", - "abilities": - { - "magicResistance" : - { - "type" : "MAGIC_RESISTANCE", - "val" : 5 - }, - "castsAmount" : - { - "type" : "CASTS", - "val" : 1 - }, - "enchant" : - { - "type" : "CREATURE_ENCHANT_POWER", - "val" : 1 - }, - "spellpower" : - { - "type" : "CREATURE_SPELL_POWER", - "val" : 100 - }, - "canCast" : - { - "type" : "SPELLCASTER", - "subtype" : "spell.precision", - "val" : 3 - } - }, - "graphics" : - { - "animation": "ZM176NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - }, - "sound" : - { - "attack": "LICHATTK.wav", - "defend": "LICHDFND.wav", - "killed": "LICHKILL.wav", - "move": "LICHMOVE.wav", - "shoot": "LICHSHOT.wav", - "wince": "LICHWNCE.wav" - } - }, - "succubus1" : - { - "special" : true, - "index": 177, - "level": 0, - "faction": "neutral", - "abilities": - { - "magicResistance" : - { - "type" : "MAGIC_RESISTANCE", - "val" : 5 - }, - "castsAmount" : - { - "type" : "CASTS", - "val" : 1 - }, - "enchant" : - { - "type" : "CREATURE_ENCHANT_POWER", - "val" : 1 - }, - "spellpower" : - { - "type" : "CREATURE_SPELL_POWER", - "val" : 100 - }, - "canCast" : - { - "type" : "SPELLCASTER", - "subtype" : "spell.fireShield", - "val" : 3 - } - }, - "graphics" : - { - "animation": "ZM177NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - }, - "sound" : - { - "attack": "SGRGATTK.wav", - "defend": "SGRGDFND.wav", - "killed": "SGRGKILL.wav", - "move": "SGRGMOVE.wav", - "wince": "SGRGWNCE.wav" - } - }, - "soulEater1" : - { - "special" : true, - "index": 178, - "level": 0, - "faction": "neutral", - "abilities": - { - "magicResistance" : - { - "type" : "MAGIC_RESISTANCE", - "val" : 5 - }, - "castsAmount" : - { - "type" : "CASTS", - "val" : 1 - }, - "enchant" : - { - "type" : "CREATURE_ENCHANT_POWER", - "val" : 1 - }, - "spellpower" : - { - "type" : "CREATURE_SPELL_POWER", - "val" : 100 - }, - "canCast" : - { - "type" : "SPELLCASTER", - "subtype" : "spell.animateDead", - "val" : 3 - } - }, - "graphics" : - { - "animation": "ZM178NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - }, - "sound" : - { - "attack": "GNOLATTK.wav", - "defend": "GNOLDFND.wav", - "killed": "GNOLKILL.wav", - "move": "GNOLMOVE.wav", - "wince": "GNOLWNCE.wav" - } - }, - "brute1" : - { - "special" : true, - "index": 179, - "level": 0, - "faction": "neutral", - "abilities": - { - "magicResistance" : - { - "type" : "MAGIC_RESISTANCE", - "val" : 5 - }, - "castsAmount" : - { - "type" : "CASTS", - "val" : 1 - }, - "enchant" : - { - "type" : "CREATURE_ENCHANT_POWER", - "val" : 1 - }, - "spellpower" : - { - "type" : "CREATURE_SPELL_POWER", - "val" : 100 - }, - "canCast" : - { - "type" : "SPELLCASTER", - "subtype" : "spell.stoneSkin", - "val" : 3 - } - }, - "graphics" : - { - "animation": "ZM179NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - }, - "sound" : - { - "attack": "PFOEATTK.wav", - "defend": "PFOEDFND.wav", - "killed": "PFOEKILL.wav", - "move": "PFOEMOVE.wav", - "wince": "PFOEWNCE.wav" - } - }, - "ogreLeader1" : - { - "special" : true, - "index": 180, - "level": 0, - "faction": "neutral", - "abilities": - { - "magicResistance" : - { - "type" : "MAGIC_RESISTANCE", - "val" : 5 - }, - "castsAmount" : - { - "type" : "CASTS", - "val" : 1 - }, - "enchant" : - { - "type" : "CREATURE_ENCHANT_POWER", - "val" : 1 - }, - "spellpower" : - { - "type" : "CREATURE_SPELL_POWER", - "val" : 100 - }, - "canCast" : - { - "type" : "SPELLCASTER", - "subtype" : "spell.cure", - "val" : 3 - } - }, - "graphics" : - { - "animation": "ZM180NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - }, - "sound" : - { - "attack": "TRLLATTK.wav", - "defend": "TRLLDFND.wav", - "killed": "TRLLKILL.wav", - "move": "TRLLMOVE.wav", - "wince": "TRLLWNCE.wav" - } - }, - "shaman1" : - { - "special" : true, - "index": 181, - "level": 0, - "faction": "neutral", - "abilities": - { - "magicResistance" : - { - "type" : "MAGIC_RESISTANCE", - "val" : 5 - }, - "castsAmount" : - { - "type" : "CASTS", - "val" : 1 - }, - "enchant" : - { - "type" : "CREATURE_ENCHANT_POWER", - "val" : 1 - }, - "spellpower" : - { - "type" : "CREATURE_SPELL_POWER", - "val" : 100 - }, - "canCast" : - { - "type" : "SPELLCASTER", - "subtype" : "spell.haste", - "val" : 3 - } - }, - "graphics" : - { - "animation": "ZM181NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - }, - "sound" : - { - "attack": "AMAGATTK.wav", - "defend": "AMAGDFND.wav", - "killed": "AMAGKILL.wav", - "move": "AMAGMOVE.wav", - "shoot": "AMAGSHOT.wav", - "wince": "AMAGWNCE.wav" - } - }, - "astralSpirit1" : - { - "special" : true, - "index": 182, - "level": 0, - "faction": "neutral", - "abilities": - { - "magicResistance" : - { - "type" : "MAGIC_RESISTANCE", - "val" : 5 - }, - "castsAmount" : - { - "type" : "CASTS", - "val" : 1 - }, - "enchant" : - { - "type" : "CREATURE_ENCHANT_POWER", - "val" : 1 - }, - "spellpower" : - { - "type" : "CREATURE_SPELL_POWER", - "val" : 100 - }, - "canCast" : - { - "type" : "SPELLCASTER", - "subtype" : "spell.counterstrike", - "val" : 3 - } - }, - "graphics" : - { - "animation": "ZM182NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - }, - "sound" : - { - "attack": "GENIATTK.wav", - "defend": "GENIDFND.wav", - "killed": "GENIKILL.wav", - "move": "GENIMOVE.wav", - "wince": "GENIWNCE.wav" - } - }, - "paladin2" : - { - "special" : true, - "index": 183, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM174NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - } - }, - "hierophant2" : - { - "special" : true, - "disabled" : true, - "index": 184, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM175NPC.DEF", - "missile" : - { - "projectile": "CPRZEAX.DEF" - } - } - }, - "templeGuardian2" : - { - "special" : true, - "disabled" : true, - "index": 185, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM176NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - } - }, - "succubus2" : - { - "special" : true, - "disabled" : true, - "index": 186, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM177NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - } - }, - "soulEater2" : - { - "special" : true, - "disabled" : true, - "index": 187, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM178NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - } - }, - "brute2" : - { - "special" : true, - "disabled" : true, - "index": 188, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM179NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - } - }, - "ogreLeader2" : - { - "special" : true, - "disabled" : true, - "index": 189, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM180NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - } - }, - "shaman2" : - { - "special" : true, - "disabled" : true, - "index": 190, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM181NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - } - }, - "astralSpirit2" : - { - "special" : true, - "disabled" : true, - "index": 191, - "level": 0, - "faction": "neutral", - "graphics" : - { - "animation": "ZM182NPC.DEF", - "missile" : - { - "projectile": "PLCBOWX.DEF" - } - } - }, - "sylvanCentaur" : - { - "index": 192, - "growth" : 14, - "level": 3, - "doubleWide" : true, - "faction": "rampart", - "abilities" : - { - "additionalAttack" : - { - "type" : "ADDITIONAL_ATTACK", - "val" : 1 - }, - "spellMirth" : - { - "type" : "ENCHANTED", - "subtype" : "spell.mirth", - "val" : 1, - "addInfo" : 3 - } - }, - "graphics" : - { - "animation": "ZM192Z.DEF", - "missile" : - { - "projectile": "PELFX.DEF" - } - }, - "sound" : - { - "attack": "ECNTATTK.wav", - "defend": "ECNTDFND.wav", - "killed": "ECNTKILL.wav", - "move": "ECNTMOVE.wav", - "shoot": "ECNTSHOT.wav", - "wince": "ECNTWNCE.wav" - } - }, - "sorceress" : - { - "index": 193, - "level": 6, - "faction": "neutral", - "graphics" : - { - "animation": "ZM193Z.DEF", - "missile" : - { - "projectile": "CPRZEAX.DEF" - } - }, - "sound" : - { - "attack": "MONKATTK.wav", - "defend": "MONKDFND.wav", - "killed": "MONKKILL.wav", - "move": "MONKMOVE.wav", - "shoot": "MONKSHOT.wav", - "wince": "MONKWNCE.wav" - } - }, - "werewolf" : - { - "index": 194, - "level": 4, - "faction": "neutral", - "graphics" : - { - "animation": "ZM194Z.DEF" - } - }, - "hellSteed" : - { - "index": 195, - "level": 6, - "faction": "neutral", - "abilities": - { - "immuneToFire" : - { - "type" : "FIRE_IMMUNITY", - "subtype" : 0 //this IS important - }, - "fireShield" : - { - "type" : "FIRE_SHIELD", - "subtype" : 100, - "val" : 100 - }, - "spellFireWall" : - { - "type" : "SPELL_AFTER_ATTACK", - "subtype" : "spell.fireWall", - "val" : 12, - "addInfo" : 3 - }, - "spellpower" : - { - "type" : "CREATURE_SPELL_POWER", - "val" : 100 - } - }, - "upgrades": ["nightmare"], - "graphics" : - { - "animation": "ZM195Z.DEF" - }, - "sound" : - { - "attack": "BGORATTK.wav", - "defend": "BGORDFND.wav", - "killed": "BGORKILL.wav", - "move": "BGORMOVE.wav", - "wince": "BGORWNCE.wav" - } - }, - "dracolich" : - { - "index": 196, - "level": 10, - "faction": "neutral", - "abilities": - { - "dragon" : - { - "type" : "DRAGON_NATURE", - }, - "dragonBreath" : - { - "type" : "TWO_HEX_ATTACK_BREATH" - }, - "undead" : - { - "type" : "UNDEAD" - }, - "canFly" : - { - "type" : "FLYING" - }, - "deathCloud" : - { - "type" : "SPELL_LIKE_ATTACK", - "subtype" : "spell.deathCloud" - }, - "canShoot" : - { - "type" : "SHOOTER" - } - }, - "graphics" : - { - "animation": "ZM196Z.DEF", - "missile" : - { - "projectile": "ZSHOT195.DEF" - } - }, - "sound" : - { - "attack": "GHDRATTK.wav", - "defend": "GHDRDFND.wav", - "killed": "GHDRKILL.wav", - "move": "GHDRMOVE.wav", - "shoot": "GHDRSHOT.wav", - "wince": "GHDRWNCE.wav" - } - } -} - -// For future reference. Creatures from WoG and their sound prefixes. -//aagl ZM150Z.def SupremeArchangel -//godr ZM151Z.def DiamondDragon -//gtit ZM152Z.def LordofThunder -//advl ZM153Z.def HellBaron -//ghdr ZM154Z.def BloodDragon -//bkdr ZM155Z.def DarknessDragon -//bmth ZM156Z.def GhostBehemoth -//chyd ZM157Z.def HellHydra -//phoe ZM158Z.def SacredPhoenix -//wrth ZM159G.def Ghost -//aagl ZM160G.def God1War -//aagl ZM161G.def God2Peace -//aagl ZM162G.def God3Mana -//aagl ZM163G.def God4Lore -//sglm ZM164GD.def MinotaurKing -//sglm ZM165GD.def MineralElemental -//sglm ZM166GD.def ElectricityElemental -//sglm ZM167GD.def AncientBasilisk -//bkdr ZM168DG.def Gorynych -//zelt ZM169ZL.def WarZealot -//hcrs ZM170SW.def Myriad -//hcrs ZM171SR.def MedusaMatriarch -//bgor ZM172N.def Nightmare -//aagl ZM173M.def SantaGremlin - -//crus ZM174NPC.def Paladin2 -//monk ZM175NPC.def Hierophant2 -//lich ZM176NPC.def TempleGuardian2 -//sgrg ZM177NPC.def Succubus2 -//gnol ZM178NPC.def SoulEater2 -///pfoe ZM179NPC.def Brute2 -//trll ZM180NPC.def OgreLeader2 -//amag ZM181NPC.def Shaman2 -//geni ZM182NPC.def AstralSpirit2 - -//ecnt ZM192Z.def SylvanCentaur -//monk ZM193Z.def Sorceress -//monk ZM194Z.def Werewolf -//bgor ZM195Z.def HellSteed -//ghdr ZM196Z.def Dracolich diff --git a/Mods/WoG/config/wog/dwellings.json b/Mods/WoG/config/wog/dwellings.json deleted file mode 100644 index 696fa2e5a..000000000 --- a/Mods/WoG/config/wog/dwellings.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "core:creatureGeneratorCommon" : { - "types" : { - "supremeArchangel" : { "index" : 80, "creatures" : [[ "supremeArchangel" ]] }, - "diamondDragon" : { "index" : 81, "creatures" : [[ "diamondDragon" ]] }, - "lordofThunder" : { "index" : 82, "creatures" : [[ "lordofThunder" ]] }, - "hellBaron" : { "index" : 83, "creatures" : [[ "hellBaron" ]] }, - "bloodDragon" : { "index" : 84, "creatures" : [[ "bloodDragon" ]] }, - - "darknessDragon" : { "index" : 85, "creatures" : [[ "darknessDragon" ]] }, - "ghostBehemoth" : { "index" : 86, "creatures" : [[ "ghostBehemoth" ]] }, - "hellHydra" : { "index" : 87, "creatures" : [[ "hellHydra" ]] }, - "sacredPhoenix" : { "index" : 88, "creatures" : [[ "sacredPhoenix" ]] }, - "lavaSharpshooter" : { "index" : 89, "creatures" : [[ "lavaSharpshooter" ]] }, - - "arcticSharpshooter" : { "index" : 90, "creatures" : [[ "arcticSharpshooter" ]] }, - "gorynych" : { "index" : 91, "creatures" : [[ "gorynych" ]] }, - "nightmare" : { "index" : 92, "creatures" : [[ "nightmare" ]] }, - "minotaurKing2" : { "index" : 93, "creatures" : [[ "minotaurKing2" ]] }, - "warZealot" : { "index" : 94, "creatures" : [[ "warZealot" ]] }, - - "santaGremlin" : { "index" : 95, "creatures" : [[ "santaGremlin" ]] }, - "sylvanCentaur" : { "index" : 96, "creatures" : [[ "sylvanCentaur" ]] }, - "sorceress" : { "index" : 97, "creatures" : [[ "sorceress" ]] }, - "werewolf" : { "index" : 98, "creatures" : [[ "werewolf" ]] }, - "hellSteed" : { "index" : 99, "creatures" : [[ "hellSteed" ]] }, - - "dracolich" : { "index" :100, "creatures" : [[ "dracolich" ]] } - } - } -} diff --git a/Mods/WoG/config/wog/heroClasses.json b/Mods/WoG/config/wog/heroClasses.json deleted file mode 100644 index 8b33e7cd6..000000000 --- a/Mods/WoG/config/wog/heroClasses.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "core:knight" : - { - "commander" : "paladin1" - }, - "core:cleric" : - { - "commander" : "paladin1" - }, - "core:planeswalker" : - { - "commander" : "astralSpirit1" - }, - "core:elementalist" : - { - "commander" : "astralSpirit1" - }, - "core:warlock" : - { - "commander" : "brute1" - }, - "core:overlord" : - { - "commander" : "brute1" - }, - "core:beastmaster" : - { - "commander" : "shaman1" - }, - "core:witch" : - { - "commander" : "shaman1" - }, - "core:demoniac" : - { - "commander" : "succubus1" - }, - "core:heretic" : - { - "commander" : "succubus1" - }, - "core:deathknight" : - { - "commander" : "soulEater1" - }, - "core:necromancer" : - { - "commander" : "soulEater1" - }, - "core:ranger" : - { - "commander" : "hierophant1" - }, - "core:druid" : - { - "commander" : "hierophant1" - }, - "core:barbarian" : - { - "commander" : "ogreLeader1" - }, - "core:battlemage" : - { - "commander" : "ogreLeader1" - }, - "core:alchemist" : - { - "commander" : "templeGuardian1" - }, - "core:wizard" : - { - "commander" : "templeGuardian1" - } -} diff --git a/Mods/WoG/config/wog/spells.json b/Mods/WoG/config/wog/spells.json deleted file mode 100644 index 81a10a39d..000000000 --- a/Mods/WoG/config/wog/spells.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - - "core:implosion" : - { - "graphics" : { - "iconImmune" : "ZVS/LIB1.RES/E_SPIMP" - } - }, - - "core:meteorShower" : { - "graphics" : { - "iconImmune" : "ZVS/LIB1.RES/E_SPMET" - } - }, - "core:armageddon" : { - - "graphics" : { - "iconImmune" : "ZVS/LIB1.RES/E_SPARM" - } - - }, - "core:dispel" : { - - "graphics" : { - "iconImmune" : "ZVS/LIB1.RES/E_SPDISP" - } - - }, - "core:slow" : { - "graphics" : { - "iconImmune" : "ZVS/LIB1.RES/E_SPSLOW" - } - }, - - "core:berserk" : { - "graphics" : { - "iconImmune" : "ZVS/LIB1.RES/E_SPBERS" - } - }, - "core:hypnotize" : { - "graphics" : { - "iconImmune" : "ZVS/LIB1.RES/E_SPHYPN" - } - }, - - "core:blind" : { - "graphics" : { - "iconImmune" : "ZVS/LIB1.RES/E_SPBLIND" - } - }, - - "core:dispelHelpful" : { - "graphics" : { - "iconImmune" : "ZVS/LIB1.RES/E_SPDISB" - } - } -} \ No newline at end of file diff --git a/Mods/WoG/config/wogFileOverrides.json b/Mods/WoG/config/wogFileOverrides.json deleted file mode 100644 index ecffd762d..000000000 --- a/Mods/WoG/config/wogFileOverrides.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - // Text configs - "data/crgen1.txt" : "data/zcrgn1.txt", - "data/crtraits.txt" : "data/zcrtrait.txt", - "data/help.txt" : "data/zelp.txt", - "data/objects.txt" : "data/zobjcts.txt", - - // main menu images - "data/gamselb0.bmp" : "data/ZPIC1000.bmp", // map selection screen - "data/gamselb1.bmp" : "data/ZPIC1001.bmp", // map selection screen - "data/loadbar.bmp" : "data/ZPIC106.bmp", // loading screen - "data/gamselbk.bmp" : "data/ZPIC1005.bmp", // background - "data/newgame.bmp" : "data/ZNEWGAM.bmp", // "new game" text - "data/loadgame.bmp" : "data/ZLOADGAM.bmp", // "load game" text - - // main menu buttons - "sprites/mmenung.def" : "sprites/zmenung.def", - "sprites/mmenulg.def" : "sprites/zmenulg.def", - "sprites/mmenuhs.def" : "sprites/zmenuhs.def", - "sprites/mmenucr.def" : "sprites/zmenucr.def", - "sprites/mmenuqt.def" : "sprites/zmenuqt.def", - - // game type select (single/multi/campaign) - "sprites/gtsingl.def" : "sprites/ztsingl.def", - "sprites/gtmulti.def" : "sprites/ztmulti.def", - "sprites/gtcampn.def" : "sprites/ztcampn.def", - "sprites/gttutor.def" : "sprites/zttutor.def", - "sprites/gtback.def" : "sprites/ztback.def", - - // campaigns - "sprites/csssod.def" : "sprites/zsssod.def", - "sprites/cssroe.def" : "sprites/zssroe.def", - "sprites/cssarm.def" : "sprites/zssarm.def", - "sprites/csscus.def" : "sprites/zsscus.def", - - // resource bars - "data/tresbar.bmp" : "data/zresbar.bmp", - "data/kresbar.bmp" : "data/z2esbar.bmp", - "data/aresbar.bmp" : "data/zresbar.bmp", - - // misc - "data/tpcainfo.bmp" : "data/zpcainfo.bmp", // stats images for town fort - "music/mainmenu.mp3" : "music/mainmenuwog.mp3", - "video/credits.bik" : "video/acredit.bik" -} diff --git a/Mods/WoG/mod.json b/Mods/WoG/mod.json deleted file mode 100644 index 9e26047f4..000000000 --- a/Mods/WoG/mod.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "name" : "In The Wake of Gods", - "description" : "Unnofficial addon for Heroes of Might and Magic III", - - "version" : "0.0.0", - "author" : "WoG Team", - "contact" : "http://forum.vcmi.eu/index.php", - "modType" : "Expansion", - - "artifacts" : - [ - "config/wog/artifacts.json" - ], - - "creatures" : - [ - "config/wog/creatures.json" - ], - - "heroClasses" : - [ - "config/wog/heroClasses.json" - ], - - "objects" : - [ - "config/wog/dwellings.json", - "config/wog/creatureBanks.json" - ], - - "filesystem": - { - "" : - [ - { "type" : "map", "path" : "/Config/wogFileOverrides.json"} - ], - "CONFIG/" : - [ - { "type" : "dir", "path" : "/Config"} - ], - "DATA/" : - [ - {"type" : "lod", "path" : "/Data/hmm35wog.pac"}, - {"type" : "dir", "path" : "/Data"} - ], - "SPRITES/": - [ - {"type" : "lod", "path" : "/Data/hmm35wog.pac"}, - {"type" : "lod", "path" : "/Data/wog - animated objects.pac"}, - {"type" : "lod", "path" : "/Data/wog - animated trees.pac"}, - {"type" : "lod", "path" : "/Data/wog - battle decorations.pac"} - ], - "SOUNDS/": - [ - {"type" : "snd", "path" : "/Data/wog - sounds.snd"}, - {"type" : "snd", "path" : "/Data/wog.snd"} - ], - "MUSIC/": - [ - {"type" : "dir", "path" : "/Mp3"} - ], - "VIDEO/": - [ - {"type" : "vid", "path" : "/Data/wog - video.vid"}, - {"type" : "vid", "path" : "/Data/wog.vid"} - ], - "MAPS/": - [ - {"type" : "dir", "path" : "/Maps"} - ] - } -} diff --git a/client/CPreGame.cpp b/client/CPreGame.cpp index 7ae547fc9..700dbdc33 100644 --- a/client/CPreGame.cpp +++ b/client/CPreGame.cpp @@ -881,6 +881,9 @@ void CSelectionScreen::startScenario() if(sInfo.mapGenOptions) { + //copy settings from interface to actual options. TODO: refactor, it used to have no effect at all -.- + sInfo.mapGenOptions = std::shared_ptr(new CMapGenOptions(randMapTab->getMapGenOptions())); + // Update player settings for RMG for(const auto & psetPair : sInfo.playerInfos) { diff --git a/config/objects/creatureBanks.json b/config/objects/creatureBanks.json index 53c175481..665e06fd3 100644 --- a/config/objects/creatureBanks.json +++ b/config/objects/creatureBanks.json @@ -7,6 +7,10 @@ { "index" : 0, "name" : "Cyclops Stockpile", + "rmg" : { + "value" : 3000, + "rarity" : 100 + }, "levels": [ { "chance": 30, @@ -17,7 +21,7 @@ { "amount": 4, "type": "cyclop" }, { "amount": 4, "type": "cyclop" } ], - + "combat_value": 506, "reward" : { "value": 10000, @@ -108,6 +112,10 @@ "index" : 1, "resetDuraition" : 28, "name" : "Dwarven Treasury", + "rmg" : { + "value" : 2000, + "rarity" : 100 + }, "levels": [ { "chance": 30, @@ -191,6 +199,10 @@ "index" : 2, "resetDuraition" : 28, "name" : "Griffin Conservatory", + "rmg" : { + "value" : 2000, + "rarity" : 100 + }, "levels": [ { "chance": 30, @@ -258,6 +270,10 @@ "index" : 3, "resetDuraition" : 28, "name" : "Imp Cache", + "rmg" : { + "value" : 5000, + "rarity" : 100 + }, "levels": [ { "chance": 30, @@ -340,6 +356,10 @@ "index" : 4, "resetDuraition" : 28, "name" : "Medusa Stores", + "rmg" : { + "value" : 1500, + "rarity" : 100 + }, "levels": [ { "chance": 30, @@ -423,6 +443,10 @@ "index" : 5, "resetDuraition" : 28, "name" : "Naga Bank", + "rmg" : { + "value" : 3000, + "rarity" : 100 + }, "levels": [ { "chance": 30, @@ -506,6 +530,10 @@ "index" : 6, "resetDuraition" : 28, "name" : "Dragon Fly Hive", + "rmg" : { + "value" : 9000, + "rarity" : 100 + }, "levels": [ { "chance": 30, @@ -579,6 +607,10 @@ "index" : 0, "resetDuraition" : 28, "name" : "Shipwreck", + "rmg" : { + "value" : 2000, + "rarity" : 100 + }, "levels": [ { "chance": 30, @@ -666,6 +698,10 @@ "index" : 0, "resetDuraition" : 28, "name" : "Derelict Ship", + "rmg" : { + "value" : 4000, + "rarity" : 20 + }, "levels": [ { "chance": 30, @@ -754,6 +790,10 @@ "index" : 0, "resetDuraition" : 28, "name" : "Crypt", + "rmg" : { + "value" : 1000, + "rarity" : 100 + }, "levels": [ { "chance": 30, @@ -839,6 +879,10 @@ "index" : 0, "resetDuraition" : 28, "name" : "Dragon Utopia", + "rmg" : { + "value" : 10000, + "rarity" : 100 + }, "levels": [ { "chance": 30, @@ -944,6 +988,10 @@ "index" : 0, "resetDuraition" : 28, "name" : "Pyramid", + "rmg" : { + "value" : 5000, + "rarity" : 20 + }, "levels": [ { "chance": 100, @@ -963,4 +1011,4 @@ } } } -} +} \ No newline at end of file diff --git a/config/objects/generic.json b/config/objects/generic.json index c92a6d97a..7ec6f81e0 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -9,56 +9,402 @@ } }, - "altarOfSacrifice" : { "index" :2, "handler": "market", "types" : { "object" : { "index" : 0} } }, - "tradingPost" : { "index" :221, "handler": "market", "types" : { "object" : { "index" : 0} } }, - "tradingPostDUPLICATE" : { "index" :99, "handler": "market", "types" : { "object" : { "index" : 0} } }, - "freelancersGuild" : { "index" :213, "handler": "market", "types" : { "object" : { "index" : 0} } }, + "altarOfSacrifice" : { + "index" :2, + "handler" : "market", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 100, + "rarity" : 20 + } + } + } + }, + "tradingPost" : { + "index" :221, + "handler" : "market", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 100, + "rarity" : 100 + } + } + } + }, + "tradingPostDUPLICATE" : { + "index" :99, + "handler" : "market", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 100, + "rarity" : 100 + } + } + } + }, + "freelancersGuild" : { + "index" :213, + "handler" : "market", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "value" : 100, + "rarity" : 100 + } + } + } + }, - "blackMarket" : { "index" :7, "handler": "blackMarket", "types" : { "object" : { "index" : 0} } }, + "blackMarket" : { + "index" :7, + "handler" : "blackMarket", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 8000, + "rarity" : 20 + } + } + } + }, - "pandoraBox" : { "index" :6, "handler": "pandora", "types" : { "object" : { "index" : 0} } }, - "event" : { "index" :26, "handler": "event", "types" : { "object" : { "index" : 0} } }, + "pandoraBox" : { + "index" :6, + "handler" : "pandora", + "types" : { + "object" : { + "index" : 0, + "templates" : { + "normal" : { "animation" : "ava0128.def", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] } + }, + "rmg" : { + } + } + } + }, + "event" : { + "index" :26, + "handler" : "event", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + } + } + } + }, - "redwoodObservatory" : { "index" :58, "handler": "observatory", "types" : { "object" : { "index" : 0} } }, - "pillarOfFire" : { "index" :60, "handler": "observatory", "types" : { "object" : { "index" : 0} } }, - "coverOfDarkness" : { "index" :15, "handler": "observatory", "types" : { "object" : { "index" : 0} } }, + "redwoodObservatory" : { + "index" :58, + "handler" : "observatory", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 750, + "rarity" : 100 + } + } + } + }, + "pillarOfFire" : { + "index" :60, + "handler" : "observatory", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 750, + "rarity" : 100 + } + } + } + }, + "coverOfDarkness" : { + "index" :15, + "handler" : "observatory", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + } + } + } + }, - "whirlpool" : { "index" :111, "handler": "teleport", "types" : { "object" : { "index" : 0} } }, + "whirlpool" : { + "index" :111, + "handler" : "teleport", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + } + } + } + }, "subterraneanGate" : { "index" :103, - "handler": "teleport", + "handler" : "teleport", "types" : { - "object" : { "index" : 0 }, + "object" : { + "index" : 0 }, "objectWoG" : { "index" : 1 } // WoG object? Present on VCMI Test 2011b } }, - "refugeeCamp" : { "index" :78, "handler": "dwelling", "types" : { "object" : { "index" : 0} } }, - "warMachineFactory" : { "index" :106, "handler": "dwelling", "types" : { "object" : { "index" : 0} } }, - - "shrineOfMagicLevel1" : { "index" :88, "handler": "shrine", "types" : { "object" : { "index" : 0} } }, - "shrineOfMagicLevel2" : { "index" :89, "handler": "shrine", "types" : { "object" : { "index" : 0} } }, - "shrineOfMagicLevel3" : { "index" :90, "handler": "shrine", "types" : { "object" : { "index" : 0} } }, - - "eyeOfTheMagi" : { "index" :27, "handler": "magi", "types" : { "object" : { "index" : 0} } }, - "hutOfTheMagi" : { "index" :37, "handler": "magi", "types" : { "object" : { "index" : 0} } }, - - "lighthouse" : { "index" :42, "handler": "lighthouse", "types" : { "object" : { "index" : 0} } }, - "obelisk" : { "index" :57, "handler": "obelisk", "types" : { "object" : { "index" : 0} } }, - "oceanBottle" : { "index" :59, "handler": "sign", "types" : { "object" : { "index" : 0} } }, - "scholar" : { "index" :81, "handler": "scholar", "types" : { "object" : { "index" : 0} } }, - "shipyard" : { "index" :87, "handler": "shipyard", "types" : { "object" : { "index" : 0} } }, - "sign" : { "index" :91, "handler": "sign", "types" : { "object" : { "index" : 0} } }, - "sirens" : { "index" :92, "handler": "siren", "types" : { "object" : { "index" : 0} } }, - "denOfThieves" : { "index" :97, "handler": "denOfThieves", "types" : { "object" : { "index" : 0} } }, - "university" : { "index" :104, "handler": "university", "types" : { "object" : { "index" : 0} } }, - "witchHut" : { "index" :113, "handler": "witch", "types" : { "object" : { "index" : 0} } }, - "questGuard" : { "index" :215, "handler": "questGuard", "types" : { "object" : { "index" : 0} } }, + "refugeeCamp" : { + "index" :78, + "handler" : "dwelling", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "value" : 5000, + "rarity" : 20 + } + } + } + }, + "warMachineFactory" : { + "index" :106, + "handler" : "dwelling", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 1500, + "rarity" : 50 + } + } + } + }, + "shrineOfMagicLevel1" : {//incantation + "index" :88, + "handler" : "shrine", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 500, + "rarity" : 100 + } + } + } + }, + "shrineOfMagicLevel2" : {//gesture + "index" :89, + "handler" : "shrine", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 2000, + "rarity" : 100 + } + } + } + }, + "shrineOfMagicLevel3" : {//thinking + "index" :90, + "handler" : "shrine", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 3000, + "rarity" : 100 + } + } + } + }, + "eyeOfTheMagi" : { + "index" :27, + "handler" : "magi", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + } + } + } + }, + "hutOfTheMagi" : { + "index" :37, + "handler" : "magi", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + } + } + } + }, + "lighthouse" : { + "index" :42, + "handler" : "lighthouse", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + } + } + } + }, + "obelisk" : { + "index" :57, + "handler" : "obelisk", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 48, + "value" : 3500, + "rarity" : 200 + } + } + } + }, + "oceanBottle" : { + "index" :59, + "handler" : "sign", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + } + } + } + }, + "scholar" : { + "index" :81, + "handler" : "scholar", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "value" : 1500, + "rarity" : 100 + } + } + } + }, + "shipyard" : { + "index" :87, + "handler" : "shipyard", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + } + } + } + }, + "sign" : { + "index" :91, + "handler" : "sign", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + } + } + } + }, + "sirens" : { + "index" :92, + "handler" : "siren", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 100, + "rarity" : 20 + } + } + } + }, + "denOfThieves" : { + "index" :97, + "handler" : "denOfThieves", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + } + } + } + }, + "university" : { + "index" :104, + "handler" : "university", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 2500, + "rarity" : 20 + } + } + } + }, + "witchHut" : { + "index" :113, + "handler" : "witch", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 3, + "mapLimit" : 32, + "value" : 1500, + "rarity" : 80 + } + } + } + }, + "questGuard" : { + "index" :215, + "handler" : "questGuard", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + } + } + } + }, "magicWell" : { "index" :49, - "handler": "magicWell", + "handler" : "magicWell", "types" : { - "object" : { "index" : 0}, + "object" : { + "index" : 0, + "rmg" : { + "value" : 250, + "rarity" : 100 + } + }, "objectWoG" : { "index" : 1} // WoG object? Present on VCMI_Test 2011b } }, @@ -99,6 +445,10 @@ "types" : { "object" : { "index" : 0, + "rmg" : { + "value" : 2000, + "rarity" : 150 + }, "templates" : { "normal" : { "animation" : "AVArnd1", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] } } @@ -111,6 +461,10 @@ "types" : { "object" : { "index" : 0, + "rmg" : { + "value" : 5000, + "rarity" : 150 + }, "templates" : { "normal" : { "animation" : "AVArnd2", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] } } @@ -123,6 +477,10 @@ "types" : { "object" : { "index" : 0, + "rmg" : { + "value" : 10000, + "rarity" : 150 + }, "templates" : { "normal" : { "animation" : "AVArnd3", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] } } @@ -135,6 +493,10 @@ "types" : { "object" : { "index" : 0, + "rmg" : { + "value" : 20000, + "rarity" : 150 + }, "templates" : { "normal" : { "animation" : "AVArnd4", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] } } @@ -240,10 +602,58 @@ }, /// Classes without dedicated object - "hillFort" : { "index" :35, "handler": "generic", "types" : { "object" : { "index" : 0} } }, - "grail" : { "index" :36, "handler": "generic", "types" : { "object" : { "index" : 0} } }, - "tavern" : { "index" :95, "handler": "generic", "types" : { "object" : { "index" : 0} } }, - "sanctuary" : { "index" :80, "handler": "generic", "types" : { "object" : { "index" : 0} } }, + "hillFort" : { + "index" :35, + "handler": "generic", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 7000, + "rarity" : 20 + } + } + } + }, + "grail" : { + "index" :36, + "handler": "generic", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + } + } + } + }, + "tavern" : { + "index" :95, + "handler": "generic", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "value" : 100, + "rarity" : 20 + } + } + } + }, + "sanctuary" : { + "index" :80, + "handler": "generic", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 100, + "rarity" : 50 + } + } + } + }, /// Passive objects, terrain overlays "cursedGround" : { "index" :21, "handler": "generic", "types" : { "object" : { "index" : 0} } }, diff --git a/config/objects/moddables.json b/config/objects/moddables.json index 1f460b3c1..b1a037407 100644 --- a/config/objects/moddables.json +++ b/config/objects/moddables.json @@ -50,13 +50,13 @@ } }, "types" : { - "wood" : { "index" : 0, "templates" : { "res" : { "animation" : "AVTwood0.def" } } }, - "mercury" : { "index" : 1, "templates" : { "res" : { "animation" : "AVTmerc0.def" } } }, - "ore" : { "index" : 2, "templates" : { "res" : { "animation" : "AVTore0.def" } } }, - "sulfur" : { "index" : 3, "templates" : { "res" : { "animation" : "AVTsulf0.def" } } }, - "crystal" : { "index" : 4, "templates" : { "res" : { "animation" : "AVTcrys0.def" } } }, - "gems" : { "index" : 5, "templates" : { "res" : { "animation" : "AVTgems0.def" } } }, - "gold" : { "index" : 6, "templates" : { "res" : { "animation" : "AVTgold0.def" } } }, + "wood" : { "index" : 0, "rmg" : { "value" : 1400, "rarity" : 300 }, "templates" : { "res" : { "animation" : "AVTwood0.def" } } }, + "mercury" : { "index" : 1, "rmg" : { "value" : 2000, "rarity" : 300 }, "templates" : { "res" : { "animation" : "AVTmerc0.def" } } }, + "ore" : { "index" : 2, "rmg" : { "value" : 1400, "rarity" : 300 }, "templates" : { "res" : { "animation" : "AVTore0.def" } } }, + "sulfur" : { "index" : 3, "rmg" : { "value" : 2000, "rarity" : 300 }, "templates" : { "res" : { "animation" : "AVTsulf0.def" } } }, + "crystal" : { "index" : 4, "rmg" : { "value" : 2000, "rarity" : 300 }, "templates" : { "res" : { "animation" : "AVTcrys0.def" } } }, + "gems" : { "index" : 5, "rmg" : { "value" : 2000, "rarity" : 300 }, "templates" : { "res" : { "animation" : "AVTgems0.def" } } }, + "gold" : { "index" : 6, "rmg" : { "value" : 750, "rarity" : 300 }, "templates" : { "res" : { "animation" : "AVTgold0.def" } } }, "mithril" : { "index" : 7 } // TODO: move to WoG? } }, @@ -101,9 +101,9 @@ } }, "types" : { - "evil" : { "index" : 0 }, - "good" : { "index" : 1 }, - "neutral" : { "index" : 2 }, + "evil" : { "index" : 0, "rmg" : { "mapLimit" : 64 } }, + "good" : { "index" : 1, "rmg" : { "mapLimit" : 64 } }, + "neutral" : { "index" : 2, "rmg" : { "mapLimit" : 64 } }, } }, @@ -156,9 +156,9 @@ "index" :13, "handler": "cartographer", "types" : { - "water" : { "index" : 0 }, - "land" : { "index" : 1 }, - "subterra" : { "index" : 2 } + "water" : { "index" : 0, "rmg" : { "zoneLimit" : 1, "value" : 5000, "rarity" : 20 } }, + "land" : { "index" : 1, "rmg" : { "zoneLimit" : 1, "value" : 10000, "rarity" : 20 } }, + "subterra" : { "index" : 2, "rmg" : { "zoneLimit" : 1, "value" : 7500, "rarity" : 20 } } } }, @@ -167,13 +167,13 @@ "index" :53, "handler": "mine", "types" : { - "sawmill" : { "index" : 0 }, - "alchemistLab" : { "index" : 1 }, - "orePit" : { "index" : 2 }, - "sulfurDune" : { "index" : 3 }, - "crystalCavern" : { "index" : 4 }, - "gemPond" : { "index" : 5 }, - "goldMine" : { "index" : 6 }, + "sawmill" : { "index" : 0, "rmg" : { "value" : 1500 } }, + "alchemistLab" : { "index" : 1, "rmg" : { "value" : 3500 } }, + "orePit" : { "index" : 2, "rmg" : { "value" : 1500 } }, + "sulfurDune" : { "index" : 3, "rmg" : { "value" : 3500 } }, + "crystalCavern" : { "index" : 4, "rmg" : { "value" : 3500 } }, + "gemPond" : { "index" : 5, "rmg" : { "value" : 3500 } }, + "goldMine" : { "index" : 6, "rmg" : { "value" : 7000 } }, } }, "abandonedMine" : { diff --git a/config/objects/rewardable.json b/config/objects/rewardable.json index b0847471e..b4d9e1df2 100644 --- a/config/objects/rewardable.json +++ b/config/objects/rewardable.json @@ -1,51 +1,496 @@ { /// These are objects that covered by concept of "configurable object" /// Most or even all of their configuration located in this file - "magicSpring" : { "index" :48, "handler": "magicSpring", "types" : { "object" : { "index" : 0} } }, - - "mysticalGarden" : { "index" :55, "handler": "oncePerWeek", "types" : { "object" : { "index" : 0} } }, - "windmill" : { "index" :112, "handler": "oncePerWeek", "types" : { "object" : { "index" : 0} } }, - "waterWheel" : { "index" :109, "handler": "oncePerWeek", "types" : { "object" : { "index" : 0} } }, - - "leanTo" : { "index" :39, "handler": "onceVisitable", "types" : { "object" : { "index" : 0} } }, - "corpse" : { "index" :22, "handler": "onceVisitable", "types" : { "object" : { "index" : 0} } }, - "wagon" : { "index" :105, "handler": "onceVisitable", "types" : { "object" : { "index" : 0} } }, - "warriorTomb" : { "index" :108, "handler": "onceVisitable", "types" : { "object" : { "index" : 0} } }, - - "campfire" : { "index" :12, "handler": "pickable", "types" : { "object" : { "index" : 0} } }, - "flotsam" : { "index" :29, "handler": "pickable", "types" : { "object" : { "index" : 0} } }, - "seaChest" : { "index" :82, "handler": "pickable", "types" : { "object" : { "index" : 0} } }, - "shipwreckSurvivor" : { "index" :86, "handler": "pickable", "types" : { "object" : { "index" : 0} } }, - "treasureChest" : { "index" :101, "handler": "pickable", "types" : { "object" : { "index" : 0} } }, - - "arena" : { "index" :4, "handler": "oncePerHero", "types" : { "object" : { "index" : 0} } }, - "marlettoTower" : { "index" :23, "handler": "oncePerHero", "types" : { "object" : { "index" : 0} } }, - "gardenOfRevelation" : { "index" :32, "handler": "oncePerHero", "types" : { "object" : { "index" : 0} } }, - "libraryOfEnlightenment" : { "index" :41, "handler": "oncePerHero", "types" : { "object" : { "index" : 0} } }, - "mercenaryCamp" : { "index" :51, "handler": "oncePerHero", "types" : { "object" : { "index" : 0} } }, - "starAxis" : { "index" :61, "handler": "oncePerHero", "types" : { "object" : { "index" : 0} } }, - "treeOfKnowledge" : { "index" :102, "handler": "oncePerHero", "types" : { "object" : { "index" : 0} } }, - "schoolOfMagic" : { "index" :47, "handler": "oncePerHero", "types" : { "object" : { "index" : 0} } }, - "schoolOfWar" : { "index" :107, "handler": "oncePerHero", "types" : { "object" : { "index" : 0} } }, - "learningStone" : { - "index" :100, - "handler": "oncePerHero", + "magicSpring" : {//magic source + "index" : 48, + "handler": "magicSpring", "types" : { - "object" : { "index" : 0}, - "objectWoG" : { "index" : 1} // WoG object? Present on VCMI_Tests 2011 + "object" : { + "index" : 0//, + //"rmg" : { + // "zoneLimit" : 1, + // "mapLimit" : 32, + // "value" : 500, + // "rarity" : 50 + //} + //banned due to problems with 2 viistable offsets + } } }, - "buoy" : { "index" :11, "handler": "bonusingObject", "types" : { "object" : { "index" : 0} } }, - "swanPond" : { "index" :14, "handler": "bonusingObject", "types" : { "object" : { "index" : 0} } }, - "faerieRing" : { "index" :28, "handler": "bonusingObject", "types" : { "object" : { "index" : 0} } }, - "fountainOfFortune" : { "index" :30, "handler": "bonusingObject", "types" : { "object" : { "index" : 0} } }, - "fountainOfYouth" : { "index" :31, "handler": "bonusingObject", "types" : { "object" : { "index" : 0} } }, - "idolOfFortune" : { "index" :38, "handler": "bonusingObject", "types" : { "object" : { "index" : 0} } }, - "mermaids" : { "index" :52, "handler": "bonusingObject", "types" : { "object" : { "index" : 0} } }, - "oasis" : { "index" :56, "handler": "bonusingObject", "types" : { "object" : { "index" : 0} } }, - "stables" : { "index" :94, "handler": "bonusingObject", "types" : { "object" : { "index" : 0} } }, - "temple" : { "index" :96, "handler": "bonusingObject", "types" : { "object" : { "index" : 0} } }, - "rallyFlag" : { "index" :64, "handler": "bonusingObject", "types" : { "object" : { "index" : 0} } }, - "wateringHole" : { "index" :110, "handler": "bonusingObject", "types" : { "object" : { "index" : 0} } } + "mysticalGarden" : { + "index" : 55, + "handler": "oncePerWeek", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 500, + "rarity" : 50 + } + } + } + }, + "windmill" :{ + "index" : 112, + "handler": "oncePerWeek", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 1500, + "rarity" : 80 + } + } + } + }, + "waterWheel" : { + "index" : 109, + "handler": "oncePerWeek", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 750, + "rarity" : 50 + } + } + } + }, + + "leanTo" :{ + "index" : 39, + "handler": "onceVisitable", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 500, + "rarity" : 100 + } + } + } + }, + "corpse" :{ + "index" : 22, + "handler": "onceVisitable", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 500, + "rarity" : 100 + } + } + } + }, + "wagon" :{ + "index" : 105, + "handler": "onceVisitable", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 500, + "rarity" : 50 + } + } + } + }, + "warriorTomb" : { + "index" : 108, + "handler": "onceVisitable", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 6000, + "rarity" : 20 + } + } + } + }, + + "campfire" :{ + "index" : 12, + "handler": "pickable", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "value" : 2000, + "rarity" : 500 + } + } + } + }, + "flotsam" :{ + "index" : 29, + "handler": "pickable", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "value" : 2000, + "rarity" : 100 + } + } + } + }, + "seaChest" :{ + "index" : 82, + "handler": "pickable", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "value" : 1500, + "rarity" : 500 + } + } + } + }, + "shipwreckSurvivor" : { + "index" : 86, + "handler": "pickable", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "value" : 1500, + "rarity" : 50 + } + } + } + }, + "treasureChest" : { + "index" : 101, + "handler": "pickable", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "value" : 1500, + "rarity" : 1000 + } + } + } + }, + + "arena" : { + "index" : 4, + "handler": "oncePerHero", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 3000, + "rarity" : 50 + } + } + } + }, + "marlettoTower" : { + "index" : 23, + "handler": "oncePerHero", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 1500, + "rarity" : 100 + } + } + } + }, + "gardenOfRevelation" : { + "index" : 32, + "handler": "oncePerHero", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 1500, + "rarity" : 100 + } + } + } + }, + "libraryOfEnlightenment" : { + "index" : 41, + "handler": "oncePerHero", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 12000, + "rarity" : 20 + } + } + } + }, + "mercenaryCamp" : { + "index" : 51, + "handler": "oncePerHero", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 1500, + "rarity" : 100 + } + } + } + }, + "starAxis" :{ + "index" : 61, + "handler": "oncePerHero", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 1500, + "rarity" : 100 + } + } + } + }, + "treeOfKnowledge" : { + "index" : 102, + "handler": "oncePerHero", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 2500, + "rarity" : 50 + } + } + } + }, + "schoolOfMagic" : { + "index" : 47, + "handler": "oncePerHero", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 1000, + "rarity" : 50 + } + } + } + }, + "schoolOfWar" : { + "index" : 107, + "handler": "oncePerHero", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 1000, + "rarity" : 50 + } + } + } + }, + "learningStone" : { + "index" : 100, + "handler": "oncePerHero", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 1500, + "rarity" : 200 + } + }, + "objectWoG" : { "index" : 1 } // WoG object? Present on VCMI_Tests 2011 + } + }, + + "buoy" : { + "index" : 11, + "handler": "bonusingObject", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "value" : 100, + "rarity" : 100 + } + } + } + }, + "swanPond" : { + "index" : 14, + "handler": "bonusingObject", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 100, + "rarity" : 100 + } + } + } + }, + "faerieRing" : { + "index" : 28, + "handler": "bonusingObject", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 100, + "rarity" : 100 + } + } + } + }, + "fountainOfFortune" : { + "index" : 30, + "handler": "bonusingObject", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 100, + "rarity" : 100 + } + } + } + }, + "fountainOfYouth" : { + "index" : 31, + "handler": "bonusingObject", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 100, + "rarity" : 50 + } + } + } + }, + "idolOfFortune" : { + "index" : 38, + "handler": "bonusingObject", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 100, + "rarity" : 100 + } + } + } + }, + "mermaids" : { + "index" : 52, + "handler": "bonusingObject", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "mapLimit" : 32, + "value" : 100, + "rarity" : 20 + } + } + } + }, + "oasis" : { + "index" : 56, + "handler": "bonusingObject", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 100, + "rarity" : 50 + } + } + } + }, + "stables" : { + "index" : 94, + "handler": "bonusingObject", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 200, + "rarity" : 40 + } + } + } + }, + "temple" : { + "index" : 96, + "handler": "bonusingObject", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 100, + "rarity" : 100 + } + } + } + }, + "rallyFlag" : { + "index" : 64, + "handler": "bonusingObject", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 100, + "rarity" : 100 + } + } + } + }, + "wateringHole" : {//waters + "index" : 110, + "handler": "bonusingObject", + "types" : { + "object" : { + "index" : 0, + "rmg" : { + "zoneLimit" : 1, + "value" : 500, + "rarity" : 50 + } + } + } + } } diff --git a/config/rmg.json b/config/rmg.json index 8ad5fd895..8750c78a9 100644 --- a/config/rmg.json +++ b/config/rmg.json @@ -2,13 +2,13 @@ { "Analogy" : { - "minSize" : "m", "maxSize" : "m", + "minSize" : "m", "maxSize" : "m+u", "players" : "4", "zones" : { "1" : { - "type" : "playerStart", "size" : 1, "owner" : 1, + "type" : "playerStart", "size" : 2, "owner" : 1, "playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true, "monsters" : "normal", "mines" : {"wood" : 1, "ore" : 1, "gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1}, @@ -19,7 +19,7 @@ }, "2" : { - "type" : "playerStart", "size" : 1, "owner" : 2, + "type" : "playerStart", "size" : 2, "owner" : 2, "playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true, "monsters" : "normal", "minesLikeZone" : 1, @@ -27,7 +27,7 @@ }, "3" : { - "type" : "playerStart", "size" : 1, "owner" : 3, + "type" : "playerStart", "size" : 2, "owner" : 3, "playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true, "monsters" : "normal", "minesLikeZone" : 1, @@ -35,7 +35,7 @@ }, "4" : { - "type" : "playerStart", "size" : 1, "owner" : 4, + "type" : "playerStart", "size" : 2, "owner" : 4, "playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true, "monsters" : "normal", "minesLikeZone" : 1, @@ -43,7 +43,7 @@ }, "5" : { - "type" : "treasure", "size" : 2, "terrainTypes" : [ "sand" ], "matchTerrainToTown" : false, + "type" : "treasure", "size" : 3, "terrainTypes" : [ "sand" ], "matchTerrainToTown" : false, "neutralTowns" : { "castles" : 1 }, "monsters" : "strong", "mines" : {"gold" : 2}, @@ -63,13 +63,13 @@ }, "Upgrade" : { - "minSize" : "s", "maxSize" : "m", + "minSize" : "s+u", "maxSize" : "m", "players" : "2", "zones" : { "1" : { - "type" : "playerStart", "size" : 1, "owner" : 1, + "type" : "playerStart", "size" : 3, "owner" : 1, "playerTowns" : { "castles" : 1 }, "monsters" : "normal", "mines" : {"wood" : 1, "ore" : 1}, @@ -80,7 +80,7 @@ }, "2" : { - "type" : "playerStart", "size" : 1, "owner" : 2, + "type" : "playerStart", "size" : 3, "owner" : 2, "playerTowns" : { "castles" : 1 }, "monsters" : "normal", "minesLikeZone" : 1, @@ -88,7 +88,7 @@ }, "3" : { - "type" : "treasure", "size" : 2, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : 1, + "type" : "treasure", "size" : 4, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : 1, "monsters" : "weak", "mines" : {"gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1, "gold" : 1}, "treasure" : [ @@ -98,14 +98,14 @@ }, "4" : { - "type" : "treasure", "size" : 2, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : 2, + "type" : "treasure", "size" : 4, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : 2, "monsters" : "weak", "minesLikeZone" : 3, "treasureLikeZone" : 3 }, "5" : { - "type" : "treasure", "size" : 3, "neutralTowns" : { "castles" : 1 }, "terrainTypes" : [ "sand" ], + "type" : "treasure", "size" : 5, "neutralTowns" : { "castles" : 1 }, "terrainTypes" : [ "sand" ], "monsters" : "strong", "mines" : {"gold" : 2}, "treasure" : [ @@ -126,13 +126,13 @@ }, "Golden Ring" : { - "minSize" : "m", "maxSize" : "l", + "minSize" : "m+u", "maxSize" : "l", "players" : "3", "zones" : { "1" : { - "type" : "playerStart", "size" : 3, "owner" : 1, + "type" : "playerStart", "size" : 2, "owner" : 1, "playerTowns" : { "castles" : 1 }, "monsters" : "normal", "mines" : {"wood" : 1, "ore" : 1}, @@ -143,7 +143,7 @@ }, "2" : { - "type" : "playerStart", "size" : 3, "owner" : 2, + "type" : "playerStart", "size" : 2, "owner" : 2, "playerTowns" : { "castles" : 1 }, "monsters" : "normal", "minesLikeZone" : 1, @@ -151,7 +151,7 @@ }, "3" : { - "type" : "playerStart", "size" : 3, "owner" : 3, + "type" : "playerStart", "size" : 2, "owner" : 3, "playerTowns" : { "castles" : 1 }, "monsters" : "normal", "minesLikeZone" : 1, @@ -243,7 +243,7 @@ }, "Jebus Cross": { - "minSize" : "l", "maxSize" : "xl", + "minSize" : "l+u", "maxSize" : "xl+u", "players" : "4", "zones": { diff --git a/lib/CRandomGenerator.h b/lib/CRandomGenerator.h index a77aa9240..d25e81386 100644 --- a/lib/CRandomGenerator.h +++ b/lib/CRandomGenerator.h @@ -117,7 +117,7 @@ namespace RandomGeneratorUtil int n = (container.end() - container.begin()); for (int i = n-1; i>0; --i) { - std::swap (container.begin()[i],container.begin()[rand.nextInt(i+1)]); + std::swap (container.begin()[i],container.begin()[rand.nextInt(i)]); } } } diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index eea0b1b5a..d8fcaa790 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -241,13 +241,7 @@ int CGObjectInstance::getSightRadious() const int3 CGObjectInstance::getVisitableOffset() const { - for(int y = 0; y < appearance.getHeight(); y++) - for (int x = 0; x < appearance.getWidth(); x++) - if (appearance.isVisitableAt(x, y)) - return int3(x,y,0); - - //logGlobal->warnStream() << "Warning: getVisitableOffset called on non-visitable obj!"; - return int3(0,0,0); + return appearance.getVisitableOffset(); } void CGObjectInstance::giveDummyBonus(ObjectInstanceID heroID, ui8 duration) const diff --git a/lib/mapObjects/CommonConstructors.cpp b/lib/mapObjects/CommonConstructors.cpp index 7fb4ce5ff..e4432db8a 100644 --- a/lib/mapObjects/CommonConstructors.cpp +++ b/lib/mapObjects/CommonConstructors.cpp @@ -218,6 +218,17 @@ bool CDwellingInstanceConstructor::producesCreature(const CCreature * crea) cons return false; } +std::vector CDwellingInstanceConstructor::getProducedCreatures() const +{ + std::vector creatures; //no idea why it's 2D, to be honest + for (auto & entry : availableCreatures) + { + for (const CCreature * cre : entry) + creatures.push_back(cre); + } + return creatures; +} + CBankInstanceConstructor::CBankInstanceConstructor() { } diff --git a/lib/mapObjects/CommonConstructors.h b/lib/mapObjects/CommonConstructors.h index 241673d12..5853c2062 100644 --- a/lib/mapObjects/CommonConstructors.h +++ b/lib/mapObjects/CommonConstructors.h @@ -13,6 +13,7 @@ * */ +class CGObjectInstance; class CGTownInstance; class CGHeroInstance; class CGDwelling; @@ -123,6 +124,7 @@ public: void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const; bool producesCreature(const CCreature * crea) const; + std::vector getProducedCreatures() const; template void serialize(Handler &h, const int version) { diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 9d4a88a32..57680916b 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -871,6 +871,12 @@ void CGArtifact::initObj() blockVisit = true; if(ID == Obj::ARTIFACT) { + if (!storedArtifact) + { + auto a = new CArtifactInstance(); + cb->gameState()->map->addNewArtifactInstance(a); + storedArtifact = a; + } if(!storedArtifact->artType) storedArtifact->setType(VLC->arth->artifacts[subID]); } @@ -963,6 +969,11 @@ void CGArtifact::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) void CGWitchHut::initObj() { + if (allowedAbilities.empty()) //this can happen for RMG. regular maps load abilities from map file + { + for (int i = 0; i < GameConstants::SKILL_QUANTITY; i++) + allowedAbilities.push_back(i); + } ability = *RandomGeneratorUtil::nextItem(allowedAbilities, cb->gameState()->getRandomGenerator()); } diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index ca1601c6a..802aabd26 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -158,6 +158,8 @@ public: CArtifactInstance *storedArtifact; std::string message; + CGArtifact() : CArmedInstance() {storedArtifact = nullptr;}; + void onHeroVisit(const CGHeroInstance * h) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; diff --git a/lib/mapObjects/ObjectTemplate.cpp b/lib/mapObjects/ObjectTemplate.cpp index b8955b84a..642a108f3 100644 --- a/lib/mapObjects/ObjectTemplate.cpp +++ b/lib/mapObjects/ObjectTemplate.cpp @@ -23,7 +23,7 @@ * */ -static bool isVisitableFromTop(int identifier, int type) +static bool isOnVisitableFromTopList(int identifier, int type) { if(type == 2 || type == 3 || type == 4 || type == 5) //creature, hero, artifact, resource return true; @@ -106,7 +106,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser) int type = boost::lexical_cast(strings[7]); printPriority = boost::lexical_cast(strings[8]) * 100; // to have some space in future - if (isVisitableFromTop(id, type)) + if (isOnVisitableFromTopList(id, type)) visitDir = 0xff; else visitDir = (8|16|32|64|128); @@ -168,7 +168,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader) int type = reader.readUInt8(); printPriority = reader.readUInt8() * 100; // to have some space in future - if (isVisitableFromTop(id, type)) + if (isOnVisitableFromTopList(id, type)) visitDir = 0xff; else visitDir = (8|16|32|64|128); @@ -354,6 +354,24 @@ bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const return dirMap[dy][dx] != 0; } +int3 ObjectTemplate::getVisitableOffset() const +{ + for(int y = 0; y < getHeight(); y++) + for (int x = 0; x < getWidth(); x++) + if (isVisitableAt(x, y)) + return int3(x,y,0); + + //logGlobal->warnStream() << "Warning: getVisitableOffset called on non-visitable obj!"; + return int3(0,0,0); +} + +bool ObjectTemplate::isVisitableFromTop() const +{ + return visitDir & 2; + //for some reason the line below is never called :? + //return isVisitableFrom (0, 1); +} + bool ObjectTemplate::canBePlacedAt(ETerrainType terrain) const { return allowedTerrains.count(terrain) != 0; diff --git a/lib/mapObjects/ObjectTemplate.h b/lib/mapObjects/ObjectTemplate.h index 62320363e..8184efba1 100644 --- a/lib/mapObjects/ObjectTemplate.h +++ b/lib/mapObjects/ObjectTemplate.h @@ -62,6 +62,8 @@ public: // Checks if object is visitable from certain direction. X and Y must be between -1..+1 bool isVisitableFrom(si8 X, si8 Y) const; + int3 getVisitableOffset() const; + bool isVisitableFromTop() const; // Checks if object can be placed on specific terrain bool canBePlacedAt(ETerrainType terrain) const; @@ -73,6 +75,8 @@ public: void readMap(CBinaryReader & reader); void readJson(const JsonNode & node); + bool operator==(const ObjectTemplate& ot) const { return (id == ot.id && subid == ot.subid); } + template void serialize(Handler &h, const int version) { h & usedTiles & allowedTerrains & animationFile & stringID; diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index c1f26e443..b202d98ef 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -175,14 +175,11 @@ const std::map & CMapGenOptions::getAvailableTempla return VLC->tplh->getTemplates(); } -void CMapGenOptions::finalize() -{ - CRandomGenerator rand; - finalize(rand); -} - void CMapGenOptions::finalize(CRandomGenerator & rand) { + logGlobal->infoStream() << boost::format ("RMG settings: players %d, teams %d, computer players %d, computer teams %d, water %d, monsters %d") + % playerCount % teamCount % compOnlyPlayerCount % compOnlyTeamCount % waterContent % monsterStrength; + if(!mapTemplate) { mapTemplate = getPossibleTemplate(rand); diff --git a/lib/rmg/CMapGenOptions.h b/lib/rmg/CMapGenOptions.h index 4829fa00d..dc0c095de 100644 --- a/lib/rmg/CMapGenOptions.h +++ b/lib/rmg/CMapGenOptions.h @@ -145,7 +145,6 @@ public: /// Finalizes the options. All random sizes for various properties will be overwritten by numbers from /// a random number generator by keeping the options in a valid state. Check options should return true, otherwise /// this function fails. - void finalize(); void finalize(CRandomGenerator & rand); /// Returns false if there is no template available which fits to the currently selected options. diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 3ef5e15bf..1501b19d2 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -11,6 +11,7 @@ #include "CRmgTemplate.h" #include "CRmgTemplateZone.h" #include "CZonePlacer.h" +#include "../mapObjects/CObjectClassesHandler.h" void CMapGenerator::foreach_neighbour(const int3 &pos, std::function foo) { @@ -24,7 +25,7 @@ void CMapGenerator::foreach_neighbour(const int3 &pos, std::function mapGenOptions, int RandomSeed /*= std::time(nullptr)*/) : - mapGenOptions(mapGenOptions), randomSeed(RandomSeed), monolithIndex(0) + mapGenOptions(mapGenOptions), randomSeed(RandomSeed), monolithIndex(0), zonesTotal(0) { rand.setSeed(randomSeed); } @@ -215,9 +216,9 @@ void CMapGenerator::fillZones() logGlobal->infoStream() << "Started filling zones"; createConnections(); + //make sure all connections are passable before creating borders for (auto it : zones) { - //make sure all connections are passable before creating borders it.second->createBorder(this); it.second->fill(this); } @@ -239,25 +240,96 @@ void CMapGenerator::createConnections() int3 guardPos(-1,-1,-1); auto otherZoneTiles = zoneB->getTileInfo(); - //auto otherZoneCenter = zoneB->getPos(); - for (auto tile : tiles) + int3 posA = zoneA->getPos(); + int3 posB = zoneB->getPos(); + + if (posA.z == posB.z) { - foreach_neighbour (tile, [&guardPos, tile, &otherZoneTiles](int3 &pos) + for (auto tile : tiles) { - if (vstd::contains(otherZoneTiles, pos)) - guardPos = tile; - }); - if (guardPos.valid()) - { - setOccupied (guardPos, ETileType::FREE); //just in case monster is too weak to spawn - zoneA->addMonster (this, guardPos, connection.getGuardStrength()); //TODO: set value according to template - //zones can make paths only in their own area - zoneA->crunchPath (this, guardPos, zoneA->getPos(), zoneA->getId(), zoneA->getFreePaths()); //make connection towards our zone center - zoneB->crunchPath (this, guardPos, zoneB->getPos(), zoneB->getId(), zoneB->getFreePaths()); //make connection towards other zone center - break; //we're done with this connection + if (isBlocked(tile)) //tiles may be occupied by subterranean gates already placed + continue; + foreach_neighbour (tile, [&guardPos, tile, &otherZoneTiles, this](int3 &pos) + { + //if (vstd::contains(otherZoneTiles, pos) && !this->isBlocked(pos)) + if (vstd::contains(otherZoneTiles, pos)) + guardPos = tile; + }); + if (guardPos.valid()) + { + setOccupied (guardPos, ETileType::FREE); //just in case monster is too weak to spawn + zoneA->addMonster (this, guardPos, connection.getGuardStrength(), false, true); + //zones can make paths only in their own area + zoneA->crunchPath (this, guardPos, posA, zoneA->getId(), zoneA->getFreePaths()); //make connection towards our zone center + zoneB->crunchPath (this, guardPos, posB, zoneB->getId(), zoneB->getFreePaths()); //make connection towards other zone center + break; //we're done with this connection + } } } + else //create subterranean gates between two zones + { + //find point on the path between zones + float3 offset (posB.x - posA.x, posB.y - posA.y, 0); + + float distance = posB.dist2d(posA); + vstd::amax (distance, 0.5f); + offset /= distance; //get unit vector + float3 vec (0, 0, 0); + //use reduced size of underground zone - make sure gate does not stand on rock + int3 tile = posA; + int3 otherTile = tile; + + bool stop = false; + while (!stop) + { + vec += offset; //this vector may extend beyond line between zone centers, in case they are directly over each other + tile = posA + int3(vec.x, vec.y, 0); + float distanceFromA = posA.dist2d(tile); + float distanceFromB = posB.dist2d(tile); + + if (distanceFromA + distanceFromB > std::max(zoneA->getSize() + zoneB->getSize(), distance)) + break; //we are too far away to ever connect + + //if zone is underground, gate must fit within its (reduced) radius + if (distanceFromA > 5 && (!posA.z || distanceFromA < zoneA->getSize() - 3) && + distanceFromB > 5 && (!posB.z || distanceFromB < zoneB->getSize() - 3)) + { + otherTile = tile; + otherTile.z = posB.z; + + if (vstd::contains(tiles, tile) && vstd::contains(otherZoneTiles, otherTile)) + { + bool withinZone = true; + + foreach_neighbour (tile, [&withinZone, &tiles](int3 &pos) + { + if (!vstd::contains(tiles, pos)) + withinZone = false; + }); + foreach_neighbour (otherTile, [&withinZone, &otherZoneTiles](int3 &pos) + { + if (!vstd::contains(otherZoneTiles, pos)) + withinZone = false; + }); + + if (withinZone) + { + auto gate1 = new CGTeleport; + gate1->ID = Obj::SUBTERRANEAN_GATE; + gate1->subID = 0; + zoneA->placeAndGuardObject(this, gate1, tile, connection.getGuardStrength()); + auto gate2 = new CGTeleport(*gate1); + zoneB->placeAndGuardObject(this, gate2, otherTile, connection.getGuardStrength()); + + stop = true; //we are done, go to next connection + } + } + } + } + if (stop) + continue; + } if (!guardPos.valid()) { auto teleport1 = new CGTeleport; @@ -299,28 +371,35 @@ bool CMapGenerator::isBlocked(const int3 &tile) const bool CMapGenerator::shouldBeBlocked(const int3 &tile) const { if (!map->isInTheMap(tile)) - throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); + throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); return tiles[tile.x][tile.y][tile.z].shouldBeBlocked(); } bool CMapGenerator::isPossible(const int3 &tile) const { if (!map->isInTheMap(tile)) - throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); + throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); return tiles[tile.x][tile.y][tile.z].isPossible(); } bool CMapGenerator::isFree(const int3 &tile) const { if (!map->isInTheMap(tile)) - throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); + throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); return tiles[tile.x][tile.y][tile.z].isFree(); } +bool CMapGenerator::isUsed(const int3 &tile) const +{ + if (!map->isInTheMap(tile)) + throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); + + return tiles[tile.x][tile.y][tile.z].isUsed(); +} void CMapGenerator::setOccupied(const int3 &tile, ETileType::ETileType state) { if (!map->isInTheMap(tile)) - throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); + throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); tiles[tile.x][tile.y][tile.z].setOccupied(state); } @@ -328,7 +407,7 @@ void CMapGenerator::setOccupied(const int3 &tile, ETileType::ETileType state) CTileInfo CMapGenerator::getTile(const int3& tile) const { if (!map->isInTheMap(tile)) - throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); + throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); return tiles[tile.x][tile.y][tile.z]; } @@ -336,7 +415,7 @@ CTileInfo CMapGenerator::getTile(const int3& tile) const void CMapGenerator::setNearestObjectDistance(int3 &tile, int value) { if (!map->isInTheMap(tile)) - throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); + throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); tiles[tile.x][tile.y][tile.z].setNearestObjectDistance(value); } @@ -344,12 +423,29 @@ void CMapGenerator::setNearestObjectDistance(int3 &tile, int value) int CMapGenerator::getNearestObjectDistance(const int3 &tile) const { if (!map->isInTheMap(tile)) - throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); + throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); return tiles[tile.x][tile.y][tile.z].getNearestObjectDistance(); } int CMapGenerator::getNextMonlithIndex() { - return monolithIndex++; + if (monolithIndex >= VLC->objtypeh->knownSubObjects(Obj::MONOLITH_TWO_WAY).size()) + throw rmgException(boost::to_string(boost::format("There is no Monolith Two Way with index %d available!") % monolithIndex)); + else + return monolithIndex++; +} + +void CMapGenerator::registerZone (TFaction faction) +{ + zonesPerFaction[faction]++; + zonesTotal++; +} +ui32 CMapGenerator::getZoneCount(TFaction faction) +{ + return zonesPerFaction[faction]; +} +ui32 CMapGenerator::getTotalZoneCount() const +{ + return zonesTotal; } diff --git a/lib/rmg/CMapGenerator.h b/lib/rmg/CMapGenerator.h index 81586eb78..0f4ee12c5 100644 --- a/lib/rmg/CMapGenerator.h +++ b/lib/rmg/CMapGenerator.h @@ -70,6 +70,7 @@ public: bool shouldBeBlocked(const int3 &tile) const; bool isPossible(const int3 &tile) const; bool isFree(const int3 &tile) const; + bool isUsed(const int3 &tile) const; void setOccupied(const int3 &tile, ETileType::ETileType state); CTileInfo getTile(const int3 & tile) const; @@ -77,9 +78,14 @@ public: void setNearestObjectDistance(int3 &tile, int value); int getNextMonlithIndex(); + void registerZone (TFaction faction); + ui32 getZoneCount(TFaction faction); + ui32 getTotalZoneCount() const; private: std::map zones; + std::map zonesPerFaction; + ui32 zonesTotal; //zones that have their main town only CTileInfo*** tiles; diff --git a/lib/rmg/CRmgTemplateZone.cpp b/lib/rmg/CRmgTemplateZone.cpp index a26858ea5..dabe4ba50 100644 --- a/lib/rmg/CRmgTemplateZone.cpp +++ b/lib/rmg/CRmgTemplateZone.cpp @@ -19,12 +19,14 @@ #include "../CCreatureHandler.h" #include "../CSpellHandler.h" //for choosing random spells -#include "../mapObjects/CObjectClassesHandler.h" +#include "../mapObjects/CommonConstructors.h" +#include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h #include "../mapObjects/CGPandoraBox.h" #include "../mapObjects/CRewardableObject.h" class CMap; class CMapEditManager; +//class CGObjectInstance; CRmgTemplateZone::CTownInfo::CTownInfo() : townCount(0), castleCount(0), townDensity(0), castleDensity(0) { @@ -109,6 +111,10 @@ bool CTileInfo::isFree() const { return occupied == ETileType::FREE; } +bool CTileInfo::isUsed() const +{ + return occupied == ETileType::USED; +} void CTileInfo::setOccupied(ETileType::ETileType value) { occupied = value; @@ -354,6 +360,24 @@ std::set CRmgTemplateZone::getTileInfo () const return tileinfo; } +void CRmgTemplateZone::discardDistantTiles (CMapGenerator* gen, float distance) +{ + //TODO: mark tiles beyond zone as unavailable, but allow to connect with adjacent zones + + //for (auto tile : tileinfo) + //{ + // if (tile.dist2d(this->pos) > distance) + // { + // gen->setOccupied(tile, ETileType::USED); + // //gen->setOccupied(tile, ETileType::BLOCKED); //fixme: crash at rendering? + // } + //} + vstd::erase_if (tileinfo, [distance, this](const int3 &tile) -> bool + { + return tile.dist2d(this->pos) > distance; + }); +} + void CRmgTemplateZone::createBorder(CMapGenerator* gen) { for (auto tile : tileinfo) @@ -374,12 +398,13 @@ void CRmgTemplateZone::createBorder(CMapGenerator* gen) void CRmgTemplateZone::fractalize(CMapGenerator* gen) { - std::vector clearedTiles; + std::vector clearedTiles (freePaths.begin(), freePaths.end()); std::set possibleTiles; std::set tilesToClear; //will be set clear std::set tilesToIgnore; //will be erased in this iteration - const float minDistance = std::sqrt(totalDensity); + //the more treasure density, the greater distance between paths. Scaling is experimental. + const float minDistance = std::sqrt(totalDensity * 3); for (auto tile : tileinfo) { if (gen->isFree(tile)) @@ -387,10 +412,7 @@ void CRmgTemplateZone::fractalize(CMapGenerator* gen) else if (gen->isPossible(tile)) possibleTiles.insert(tile); } - if (clearedTiles.empty()) //this should come from zone connections - { - clearedTiles.push_back(pos); //zone center should be always clear - } + assert (clearedTiles.size()); //this should come from zone connections while (possibleTiles.size()) { @@ -440,6 +462,34 @@ void CRmgTemplateZone::fractalize(CMapGenerator* gen) tilesToClear.clear(); //empty this container tilesToIgnore.clear(); } + + for (auto tile : clearedTiles) + { + freePaths.insert(tile); + } + + + + if (0) //enable to debug + { + std::ofstream out(boost::to_string(boost::format("zone %d") % id)); + int levels = gen->map->twoLevel ? 2 : 1; + int width = gen->map->width; + int height = gen->map->height; + for (int k = 0; k < levels; k++) + { + for(int j=0; jinfoStream() << boost::format ("Zone %d subdivided fractally") %id; } @@ -514,13 +564,13 @@ void CRmgTemplateZone::addRequiredObject(CGObjectInstance * obj, si32 strength) requiredObjects.push_back(std::make_pair(obj, strength)); } -bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength) +bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength, bool clearSurroundingTiles, bool zoneGuard) { //precalculate actual (randomized) monster strength based on this post //http://forum.vcmi.eu/viewtopic.php?p=12426#12426 int mapMonsterStrength = gen->mapGenOptions->getMonsterStrength(); - int monsterStrength = zoneMonsterStrength + mapMonsterStrength - 1; //array index from 0 to 4 + int monsterStrength = (zoneGuard ? 0 : zoneMonsterStrength) + mapMonsterStrength - 1; //array index from 0 to 4 static const int value1[] = {2500, 1500, 1000, 500, 0}; static const int value2[] = {7500, 7500, 7500, 5000, 5000}; static const float multiplier1[] = {0.5, 0.75, 1.0, 1.5, 1.5}; @@ -566,16 +616,32 @@ bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength) //will be set during initialization guard->putStack(SlotID(0), hlp); + //logGlobal->traceStream() << boost::format ("Adding stack of %d %s. Map monster strenght %d, zone monster strength %d, base monster value %d") + // % amount % VLC->creh->creatures[creId]->namePl % mapMonsterStrength % zoneMonsterStrength % strength; + placeObject(gen, guard, pos); + + if (clearSurroundingTiles) + { + //do not spawn anything near monster + gen->foreach_neighbour (pos, [gen](int3 pos) + { + if (gen->isPossible(pos)) + gen->setOccupied(pos, ETileType::FREE); + }); + } + return true; } bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos) { + CTreasurePileInfo info; + std::map treasures; std::set boundary; int3 guardPos (-1,-1,-1); - int3 nextTreasurePos = pos; + info.nextTreasurePos = pos; //default values int maxValue = 5000; @@ -601,9 +667,8 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos) CGObjectInstance * object = nullptr; while (currentValue < minValue) { - //TODO: this works only for 1-tile objects - //make sure our shape is consistent - treasures[nextTreasurePos] = nullptr; + treasures[info.nextTreasurePos] = nullptr; + for (auto treasurePos : treasures) { gen->foreach_neighbour (treasurePos.first, [gen, &boundary](int3 pos) @@ -616,6 +681,7 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos) //leaving only boundary around objects vstd::erase_if_present (boundary, treasurePos.first); } + for (auto tile : boundary) { //we can't extend boundary anymore @@ -625,22 +691,46 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos) int remaining = maxValue - currentValue; - auto oi = getRandomObject(gen, remaining); + ObjectInfo oi = getRandomObject(gen, info, remaining); object = oi.generateObject(); if (!object) + { + vstd::erase_if_present(treasures, info.nextTreasurePos); break; + } + else + { + //remove from possible objects + auto oiptr = std::find(possibleObjects.begin(), possibleObjects.end(), oi); + assert (oiptr != possibleObjects.end()); + oiptr->maxPerZone--; + //TODO + + //update treasure pile area + int3 visitablePos = info.nextTreasurePos; + + //TODO: actually we need to check is object is either !blockVisit or removable after visit - this means object below can be accessed + if (oi.templ.isVisitableFromTop()) + info.visitableFromTopPositions.insert(visitablePos); //can be accessed from any direction + else + info.visitableFromBottomPositions.insert(visitablePos); //can be accessed only from bottom or side + + for (auto blockedOffset : oi.templ.getBlockedOffsets()) + { + int3 blockPos = info.nextTreasurePos + blockedOffset + oi.templ.getVisitableOffset(); //object will be moved to align vistable pos to treasure pos + info.occupiedPositions.insert(blockPos); + info.blockedPositions.insert(blockPos); + } + info.occupiedPositions.insert(visitablePos); + } currentValue += oi.value; - treasures[nextTreasurePos] = object; + treasures[info.nextTreasurePos] = object; //now find place for next object int3 placeFound(-1,-1,-1); - //FIXME: find out why teh following code causes crashes - //std::vector boundaryVec(boundary.begin(), boundary.end()); - //RandomGeneratorUtil::randomShuffle(boundaryVec, gen->rand); - //for (auto tile : boundaryVec) for (auto tile : boundary) { if (gen->isPossible(tile)) //we can place new treasure only on possible tile @@ -659,25 +749,50 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos) } } if (placeFound.valid()) - nextTreasurePos = placeFound; + info.nextTreasurePos = placeFound; } + if (treasures.size()) { - //find object closest to zone center, then con nect it to the middle of the zone - int3 zoneCenter = getPos(); + //find object closest to zone center, then connect it to the middle of the zone + int3 closestFreeTile (-1,-1,-1); + if (info.visitableFromBottomPositions.size()) //get random treasure tile, starting from objects accessible only from bottom + closestFreeTile = findClosestTile (freePaths, *RandomGeneratorUtil::nextItem(info.visitableFromBottomPositions, gen->rand)); + else + closestFreeTile = findClosestTile (freePaths, *RandomGeneratorUtil::nextItem(info.visitableFromTopPositions, gen->rand)); + int3 closestTile = int3(-1,-1,-1); float minDistance = 1e10; - for (auto treasure : treasures) + for (auto visitablePos : info.visitableFromBottomPositions) //objects that are not visitable from top must be accessible from bottom or side { - if (zoneCenter.dist2d(treasure.first) < minDistance) + if (closestFreeTile.dist2d(visitablePos) < minDistance) { - closestTile = treasure.first; - minDistance = zoneCenter.dist2d(treasure.first); + closestTile = visitablePos + int3 (0, 1, 0); //start below object (y+1), possibly even outside the map (?) + minDistance = closestFreeTile.dist2d(visitablePos); + } + } + if (!closestTile.valid()) + { + for (auto visitablePos : info.visitableFromTopPositions) //all objects are accessible from any direction + { + if (closestFreeTile.dist2d(visitablePos) < minDistance) + { + closestTile = visitablePos; + minDistance = closestFreeTile.dist2d(visitablePos); + } } } assert (closestTile.valid()); - if (!crunchPath (gen, closestTile, getPos(), id)) //make sure pile is connected to the middle of zone + + for (auto tile : info.occupiedPositions) { + if (gen->map->isInTheMap(tile)) //pile boundary may reach map border + gen->setOccupied(tile, ETileType::BLOCKED); //so that crunch path doesn't cut through objects + } + + if (!crunchPath (gen, closestTile, closestFreeTile, id)) + { + //we can't connect this pile, just block it off and start over for (auto treasure : treasures) { if (gen->isPossible(treasure.first)) @@ -686,6 +801,25 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos) return true; } + //update boundary around our objects, including knowledge about objects visitable from bottom + + boundary.clear(); + for (auto tile : info.visitableFromBottomPositions) + { + gen->foreach_neighbour (tile, [tile, &boundary](int3 pos) + { + if (pos.y >= tile.y) //don't block these objects from above + boundary.insert(pos); + }); + } + for (auto tile : info.visitableFromTopPositions) + { + gen->foreach_neighbour (tile, [&boundary](int3 pos) + { + boundary.insert(pos); + }); + } + for (auto tile : boundary) //guard must be standing there { if (gen->isFree(tile)) //this tile could be already blocked, don't place a monster here @@ -699,15 +833,22 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos) { for (auto treasure : treasures) { - placeObject(gen, treasure.second, treasure.first - treasure.second->getVisitableOffset()); + int3 visitableOffset = treasure.second->getVisitableOffset(); + placeObject(gen, treasure.second, treasure.first + visitableOffset); } - if (addMonster(gen, guardPos, currentValue)) + if (addMonster(gen, guardPos, currentValue, false)) {//block only if the object is guarded for (auto tile : boundary) { if (gen->isPossible(tile)) gen->setOccupied (tile, ETileType::BLOCKED); } + //do not spawn anything near monster + gen->foreach_neighbour (guardPos, [gen](int3 pos) + { + if (gen->isPossible(pos)) + gen->setOccupied(pos, ETileType::FREE); + }); } } else //we couldn't make a connection to this location, block it @@ -746,8 +887,15 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen) town->builtBuildings.insert(BuildingID::FORT); town->builtBuildings.insert(BuildingID::DEFAULT); - if (!totalTowns) //first town in zone goes in the middle - placeObject(gen, town, getPos() + town->getVisitableOffset()); + if (!totalTowns) + { + //first town in zone sets the facton of entire zone + town->subID = townType; + //register MAIN town of zone + gen->registerZone(town->subID); + //first town in zone goes in the middle + placeAndGuardObject(gen, town, getPos() + town->getVisitableOffset(), 0); + } else addRequiredObject (town); totalTowns++; @@ -776,9 +924,12 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen) town->tempOwner = player; town->builtBuildings.insert(BuildingID::FORT); town->builtBuildings.insert(BuildingID::DEFAULT); - placeObject(gen, town, getPos() + town->getVisitableOffset()); //towns are big objects and should be centered around visitable position + //towns are big objects and should be centered around visitable position + placeAndGuardObject(gen, town, getPos() + town->getVisitableOffset(), 0); //generate no guards, but free path to entrance totalTowns++; + //register MAIN town of zone only + gen->registerZone (town->subID); logGlobal->traceStream() << "Fill player info " << player_id; @@ -819,7 +970,23 @@ void CRmgTemplateZone::initTerrainType (CMapGenerator* gen) else terrainType = *RandomGeneratorUtil::nextItem(terrainTypes, gen->rand); - //paint zone with matching terrain + //TODO: allow new types of terrain? + if (pos.z) + { + if (terrainType != ETerrainType::LAVA) + terrainType = ETerrainType::SUBTERRANEAN; + } + else + { + if (terrainType == ETerrainType::SUBTERRANEAN) + terrainType = ETerrainType::DIRT; + } + + paintZoneTerrain (gen, terrainType); +} + +void CRmgTemplateZone::paintZoneTerrain (CMapGenerator* gen, ETerrainType terrainType) +{ std::vector tiles; for (auto tile : tileinfo) { @@ -893,8 +1060,8 @@ bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen) } logGlobal->traceStream() << "Place found"; - placeObject(gen, obj.first, pos); - guardObject (gen, obj.first, obj.second); + placeObject (gen, obj.first, pos); + guardObject (gen, obj.first, obj.second, (obj.first->ID == Obj::MONOLITH_TWO_WAY)); } return true; } @@ -916,7 +1083,40 @@ void CRmgTemplateZone::createTreasures(CMapGenerator* gen) } void CRmgTemplateZone::createObstacles(CMapGenerator* gen) -{ +{ + if (pos.z) //underground + { + std::vector rockTiles; + + for (auto tile : tileinfo) + { + if (gen->shouldBeBlocked(tile)) + { + bool placeRock = true; + gen->foreach_neighbour (tile, [gen, &placeRock](int3 &pos) + { + if (!(gen->shouldBeBlocked(pos) || gen->isPossible(pos))) + placeRock = false; + }); + if (placeRock) + { + rockTiles.push_back(tile); + } + } + } + gen->editManager->getTerrainSelection().setSelection(rockTiles); + gen->editManager->drawTerrain(ETerrainType::ROCK, &gen->rand); + //for (auto tile : rockTiles) + //{ + // gen->setOccupied (tile, ETileType::USED); + // gen->foreach_neighbour (tile, [gen](int3 &pos) + // { + // if (!gen->isUsed(pos)) + // gen->setOccupied (pos, ETileType::BLOCKED); + // }); + //} + } + //get all possible obstacles for this terrain for (auto primaryID : VLC->objtypeh->knownObjects()) { @@ -940,7 +1140,7 @@ void CRmgTemplateZone::createObstacles(CMapGenerator* gen) auto tryToPlaceObstacleHere = [this, gen](int3& tile)-> bool { auto temp = *RandomGeneratorUtil::nextItem(possibleObstacles, gen->rand); - int3 obstaclePos = tile + temp.getBlockMapOffset(); + int3 obstaclePos = tile - temp.getBlockMapOffset(); if (canObstacleBePlacedHere(gen, temp, obstaclePos)) //can be placed here { auto obj = VLC->objtypeh->getHandlerFor(temp.id, temp.subid)->create(temp); @@ -966,9 +1166,12 @@ void CRmgTemplateZone::createObstacles(CMapGenerator* gen) bool CRmgTemplateZone::fill(CMapGenerator* gen) { - addAllPossibleObjects (gen); initTownType(gen); initTerrainType(gen); + + freePaths.insert(pos); //zone center should be always clear to allow other tiles to connect + + addAllPossibleObjects (gen); placeMines(gen); createRequiredObjects(gen); fractalize(gen); //after required objects are created and linked with their own paths @@ -1017,19 +1220,45 @@ bool CRmgTemplateZone::findPlaceForTreasurePile(CMapGenerator* gen, si32 min_dis bool CRmgTemplateZone::canObstacleBePlacedHere(CMapGenerator* gen, ObjectTemplate &temp, int3 &pos) { + if (!gen->map->isInTheMap(pos)) //blockmap may fit in the map, but botom-right corner does not + return false; + auto tilesBlockedByObject = temp.getBlockedOffsets(); - bool allTilesAvailable = true; for (auto blockingTile : tilesBlockedByObject) { int3 t = pos + blockingTile; if (!gen->map->isInTheMap(t) || !(gen->isPossible(t) || gen->shouldBeBlocked(t))) { - allTilesAvailable = false; //if at least one tile is not possible, object can't be placed here - break; + return false; //if at least one tile is not possible, object can't be placed here } } - return allTilesAvailable; + return true; +} + +bool CRmgTemplateZone::isAccessibleFromAnywhere (CMapGenerator* gen, ObjectTemplate &appearance, int3 &tile, const std::set &tilesBlockedByObject) const +{ + bool accessible = false; + for (int x = -1; x < 2; x++) + { + for (int y = -1; y <2; y++) + { + if (x && y) //check only if object is visitable from another tile + { + int3 offset = appearance.getVisitableOffset() + int3(x, y, 0); + if (!vstd::contains(tilesBlockedByObject, offset)) + { + int3 nearbyPos = tile + offset; + if (gen->map->isInTheMap(nearbyPos)) + { + if (appearance.isVisitableFrom(x, y) && !gen->isBlocked(nearbyPos)) + accessible = true; + } + } + } + }; + } + return accessible; } bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance* obj, si32 min_dist, int3 &pos) @@ -1057,25 +1286,7 @@ bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance* for (auto tile : tileinfo) { //object must be accessible from at least one surounding tile - bool accessible = false; - for (int x = -1; x < 2; x++) - for (int y = -1; y <2; y++) - { - if (x && y) //check only if object is visitable from another tile - { - int3 offset = obj->getVisitableOffset() + int3(x, y, 0); - if (!vstd::contains(tilesBlockedByObject, offset)) - { - int3 nearbyPos = tile + offset; - if (gen->map->isInTheMap(nearbyPos)) - { - if (obj->appearance.isVisitableFrom(x, y) && !gen->isBlocked(nearbyPos)) - accessible = true; - } - } - } - }; - if (!accessible) + if (!isAccessibleFromAnywhere(gen, obj->appearance, tile, tilesBlockedByObject)) continue; auto ti = gen->getTile(tile); @@ -1111,7 +1322,7 @@ bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance* void CRmgTemplateZone::checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos) { if (!gen->map->isInTheMap(pos)) - throw rmgException(boost::to_string(boost::format("Position of object %d at %s is outside the map") % object->id % object->pos())); + throw rmgException(boost::to_string(boost::format("Position of object %d at %s is outside the map") % object->id % pos)); object->pos = pos; if (object->isVisitable() && !gen->map->isInTheMap(object->visitablePos())) @@ -1124,9 +1335,10 @@ void CRmgTemplateZone::checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance* if (object->appearance.id == Obj::NO_OBJ) { - auto templates = VLC->objtypeh->getHandlerFor(object->ID, object->subID)->getTemplates(gen->map->getTile(pos).terType); + auto terrainType = gen->map->getTile(pos).terType; + auto templates = VLC->objtypeh->getHandlerFor(object->ID, object->subID)->getTemplates(terrainType); if (templates.empty()) - throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s") %object->ID %object->subID %pos)); + throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") %object->ID %object->subID %pos %terrainType)); object->appearance = templates.front(); } @@ -1160,6 +1372,12 @@ void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object, } } +void CRmgTemplateZone::placeAndGuardObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, si32 str, bool zoneGuard) +{ + placeObject(gen, object, pos); + guardObject(gen, object, str, zoneGuard); +} + std::vector CRmgTemplateZone::getAccessibleOffsets (CMapGenerator* gen, CGObjectInstance* object) { //get all tiles from which this object can be accessed @@ -1186,7 +1404,7 @@ std::vector CRmgTemplateZone::getAccessibleOffsets (CMapGenerator* gen, CG return tiles; } -bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str) +bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str, bool zoneGuard) { logGlobal->traceStream() << boost::format("Guard object at %s") % object->pos(); @@ -1197,7 +1415,7 @@ bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object, for (auto tile : tiles) { //crunching path may fail if center of teh zone is dirrectly over wide object - if (crunchPath (gen, tile, getPos(), id)) //make sure object is accessible before surrounding it with blocked tiles + if (crunchPath (gen, tile, findClosestTile(freePaths, tile), id)) //make sure object is accessible before surrounding it with blocked tiles { guardTile = tile; break; @@ -1209,7 +1427,7 @@ bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object, return false; } - if (addMonster (gen, guardTile, str)) //do not place obstacles around unguarded object + if (addMonster (gen, guardTile, str, false, zoneGuard)) //do not place obstacles around unguarded object { for (auto pos : tiles) { @@ -1222,14 +1440,17 @@ bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object, else //allow no guard or other object in front of this object { for (auto tile : tiles) - gen->setOccupied (tile, ETileType::FREE); + if (gen->isPossible(pos)) + gen->setOccupied (tile, ETileType::FREE); } return true; } -ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value) +ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, CTreasurePileInfo &info, ui32 value) { + //int objectsVisitableFromBottom = 0; //for debug + std::vector> tresholds; ui32 total = 0; @@ -1238,19 +1459,106 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value) //roulette wheel for (auto oi : possibleObjects) { - if (oi.value >= minValue && oi.value <= value) + if (oi.value >= minValue && oi.value <= value && oi.maxPerZone > 0) { - //TODO: check place for non-removable object - //problem: we need at least template for the object that does not yet exist + int3 newVisitableOffset = oi.templ.getVisitableOffset(); //visitablePos assumes object will be shifter by visitableOffset + int3 newVisitablePos = info.nextTreasurePos; + + if (!oi.templ.isVisitableFromTop()) + { + //objectsVisitableFromBottom++; + //there must be free tiles under object + auto blockedOffsets = oi.templ.getBlockedOffsets(); + if (!isAccessibleFromAnywhere(gen, oi.templ, newVisitablePos, blockedOffsets)) + continue; + } + + //NOTE: y coordinate grows downwards + if (info.visitableFromBottomPositions.size() + info.visitableFromTopPositions.size()) //do not try to match first object in zone + { + bool fitsHere = false; + + if (oi.templ.isVisitableFromTop()) //new can be accessed from any direction + { + for (auto tile : info.visitableFromTopPositions) + { + int3 actualTile = tile + newVisitableOffset; + if (newVisitablePos.areNeighbours(actualTile)) //we access other removable object from any position + { + fitsHere = true; + break; + } + } + for (auto tile : info.visitableFromBottomPositions) + { + int3 actualTile = tile + newVisitableOffset; + if (newVisitablePos.areNeighbours(actualTile) && newVisitablePos.y >= actualTile.y) //we access existing static object from side or bottom only + { + fitsHere = true; + break; + } + } + } + else //if new object is not visitable from top, it must be accessible from below or side + { + for (auto tile : info.visitableFromTopPositions) + { + int3 actualTile = tile + newVisitableOffset; + if (newVisitablePos.areNeighbours(actualTile) && newVisitablePos.y <= actualTile.y) //we access existing removable object from top or side only + { + fitsHere = true; + break; + } + } + for (auto tile : info.visitableFromBottomPositions) + { + int3 actualTile = tile + newVisitableOffset; + if (newVisitablePos.areNeighbours(actualTile) && newVisitablePos.y == actualTile.y) //we access other static object from side only + { + fitsHere = true; + break; + } + } + } + if (!fitsHere) + continue; + } + + //now check blockmap, including our already reserved pile area + + bool fitsBlockmap = true; + + + std::set blockedOffsets = oi.templ.getBlockedOffsets(); + blockedOffsets.insert (newVisitableOffset); + for (auto blockingTile : blockedOffsets) + { + int3 t = info.nextTreasurePos + newVisitableOffset + blockingTile; + if (!gen->map->isInTheMap(t) || vstd::contains(info.occupiedPositions, t)) + { + fitsBlockmap = false; //if at least one tile is not possible, object can't be placed here + break; + } + if (!(gen->isPossible(t) || gen->isBlocked(t))) //blocked tiles of object may cover blocked tiles, but not used or free tiles + { + fitsBlockmap = false; + break; + } + } + if (!fitsBlockmap) + continue; + total += oi.probability; tresholds.push_back (std::make_pair (total, oi)); } } - //TODO: generate pandora box with gold if the value is very high + //logGlobal->infoStream() << boost::format ("Number of objects visitable from bottom: %d") % objectsVisitableFromBottom; + + //Generate pandora Box with gold if the value is extremely high + ObjectInfo oi; if (tresholds.empty()) { - ObjectInfo oi; if (minValue > 20000) //we don't have object valuable enough { oi.generateObject = [minValue]() -> CGObjectInstance * @@ -1261,6 +1569,7 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value) obj->resources[Res::GOLD] = minValue; return obj; }; + oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); oi.value = minValue; oi.probability = 0; } @@ -1270,6 +1579,7 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value) { return nullptr; }; + oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); //TODO: null template or something? should be never used, but hell knows oi.value = 0; oi.probability = 0; } @@ -1288,121 +1598,79 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value) void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen) { - //TODO: move typical objects to config - ObjectInfo oi; + oi.maxPerMap = std::numeric_limits().max(); - static const Res::ERes preciousRes[] = {Res::ERes::CRYSTAL, Res::ERes::GEMS, Res::ERes::MERCURY, Res::ERes::SULFUR}; + for (auto primaryID : VLC->objtypeh->knownObjects()) + { + for (auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID)) + { + auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID); + if (!handler->isStaticObject() && handler->getRMGInfo().value) + { + for (auto temp : handler->getTemplates()) + { + if (temp.canBePlacedAt(terrainType)) + { + oi.generateObject = [gen, temp]() -> CGObjectInstance * + { + return VLC->objtypeh->getHandlerFor(temp.id, temp.subid)->create(temp); + }; + oi.value = handler->getRMGInfo().value; + oi.probability = handler->getRMGInfo().rarity; + oi.templ = temp; + oi.maxPerZone = handler->getRMGInfo().zoneLimit; + possibleObjects.push_back (oi); + } + } + } + } + } + + //all following objects are unlimited + oi.maxPerZone = std::numeric_limits().max(); + + //dwellings + + auto subObjects = VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1); + + //don't spawn original "neutral" dwellings that got replaced by Conflux dwellings in AB + static int elementalConfluxROE[] = {7, 13, 16, 47}; for (int i = 0; i < 4; i++) + vstd::erase_if_present(subObjects, elementalConfluxROE[i]); + + for (auto secondaryID : subObjects) { - oi.generateObject = [i, gen]() -> CGObjectInstance * + auto dwellingHandler = dynamic_cast(VLC->objtypeh->getHandlerFor(Obj::CREATURE_GENERATOR1, secondaryID).get()); + auto creatures = dwellingHandler->getProducedCreatures(); + if (creatures.empty()) + continue; + + auto cre = creatures.front(); + if (cre->faction == townType) { - auto obj = new CGResource(); - obj->ID = Obj::RESOURCE; - obj->subID = static_cast(preciousRes[i]); - obj->amount = 0; - return obj; - }; - oi.value = 1400; - oi.probability = 300; - possibleObjects.push_back (oi); + oi.value = cre->AIValue * cre->growth * (1 + (float)(gen->getZoneCount(cre->faction)) / gen->getTotalZoneCount() + (float)(gen->getZoneCount(cre->faction) / 2)); //TODO: include town count in formula + oi.probability = 40; + + for (auto temp : dwellingHandler->getTemplates()) + { + if (temp.canBePlacedAt(terrainType)) + { + oi.generateObject = [gen, temp, secondaryID, dwellingHandler]() -> CGObjectInstance * + { + auto obj = VLC->objtypeh->getHandlerFor(Obj::CREATURE_GENERATOR1, secondaryID)->create(temp); + //dwellingHandler->configureObject(obj, gen->rand); + obj->tempOwner = PlayerColor::NEUTRAL; + return obj; + }; + + oi.templ = temp; + possibleObjects.push_back (oi); + } + } + } } - static const Res::ERes woodOre[] = {Res::ERes::WOOD, Res::ERes::ORE}; - for (int i = 0; i < 2; i++) - { - oi.generateObject = [i, gen]() -> CGObjectInstance * - { - auto obj = new CGResource(); - obj->ID = Obj::RESOURCE; - obj->subID = static_cast(woodOre[i]); - obj->amount = 0; - return obj; - }; - oi.value = 1400; - oi.probability = 300; - possibleObjects.push_back (oi); - } - - oi.generateObject = [gen]() -> CGObjectInstance * - { - auto obj = new CGResource(); - obj->ID = Obj::RESOURCE; - obj->subID = static_cast(Res::ERes::GOLD); - obj->amount = 0; - return obj; - }; - oi.value = 750; - oi.probability = 300; - possibleObjects.push_back (oi); - - oi.generateObject = [gen]() -> CGObjectInstance * - { - auto obj = new CGPickable(); - obj->ID = Obj::TREASURE_CHEST; - obj->subID = 0; - return obj; - }; - oi.value = 1500; - oi.probability = 1000; - possibleObjects.push_back (oi); - - oi.generateObject = [gen]() -> CGObjectInstance * - { - auto obj = new CGArtifact(); - obj->ID = Obj::RANDOM_TREASURE_ART; - obj->subID = 0; - auto a = new CArtifactInstance(); - gen->map->addNewArtifactInstance(a); - obj->storedArtifact = a; - return obj; - }; - oi.value = 2000; - oi.probability = 150; - possibleObjects.push_back (oi); - - oi.generateObject = [gen]() -> CGObjectInstance * - { - auto obj = new CGArtifact(); - obj->ID = Obj::RANDOM_MINOR_ART; - obj->subID = 0; - auto a = new CArtifactInstance(); - gen->map->addNewArtifactInstance(a); - obj->storedArtifact = a; - return obj; - }; - oi.value = 5000; - oi.probability = 150; - possibleObjects.push_back (oi); - - oi.generateObject = [gen]() -> CGObjectInstance * - { - auto obj = new CGArtifact(); - obj->ID = Obj::RANDOM_MAJOR_ART; - obj->subID = 0; - auto a = new CArtifactInstance(); - gen->map->addNewArtifactInstance(a); - obj->storedArtifact = a; - return obj; - }; - oi.value = 10000; - oi.probability = 150; - possibleObjects.push_back (oi); - - oi.generateObject = [gen]() -> CGObjectInstance * - { - auto obj = new CGArtifact(); - obj->ID = Obj::RANDOM_RELIC_ART; - obj->subID = 0; - auto a = new CArtifactInstance(); - gen->map->addNewArtifactInstance(a); - obj->storedArtifact = a; - return obj; - }; - oi.value = 20000; - oi.probability = 150; - possibleObjects.push_back (oi); - static const int scrollValues[] = {500, 2000, 3000, 4000, 5000}; for (int i = 0; i < 5; i++) @@ -1428,43 +1696,192 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen) obj->storedArtifact = a; return obj; }; + oi.setTemplate (Obj::SPELL_SCROLL, 0, terrainType); oi.value = scrollValues[i]; oi.probability = 30; possibleObjects.push_back (oi); } - //non-removable object for test - //oi.generateObject = [gen]() -> CGObjectInstance * - //{ - // auto obj = new CGMagicWell(); - // obj->ID = Obj::MAGIC_WELL; - // obj->subID = 0; - // return obj; - //}; - //oi.value = 250; - //oi.probability = 100; - //possibleObjects.push_back (oi); + //pandora box with gold + for (int i = 1; i < 5; i++) + { + oi.generateObject = [i]() -> CGObjectInstance * + { + auto obj = new CGPandoraBox(); + obj->ID = Obj::PANDORAS_BOX; + obj->subID = 0; + obj->resources[Res::GOLD] = i * 5000; + return obj; + }; + oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType); + oi.value = i * 5000;; + oi.probability = 5; + possibleObjects.push_back (oi); + } - //oi.generateObject = [gen]() -> CGObjectInstance * - //{ - // auto obj = new CGObelisk(); - // obj->ID = Obj::OBELISK; - // obj->subID = 0; - // return obj; - //}; - //oi.value = 3500; - //oi.probability = 200; - //possibleObjects.push_back (oi); + //pandora box with experience + for (int i = 1; i < 5; i++) + { + oi.generateObject = [i]() -> CGObjectInstance * + { + auto obj = new CGPandoraBox(); + obj->ID = Obj::PANDORAS_BOX; + obj->subID = 0; + obj->gainedExp = i * 5000; + return obj; + }; + oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType); + oi.value = i * 6000;; + oi.probability = 20; + possibleObjects.push_back (oi); + } - //oi.generateObject = [gen]() -> CGObjectInstance * - //{ - // auto obj = new CBank(); - // obj->ID = Obj::CREATURE_BANK; - // obj->subID = 5; //naga bank - // return obj; - //}; - //oi.value = 3000; - //oi.probability = 100; - //possibleObjects.push_back (oi); - + //pandora box with creatures + static const int tierValues[] = {5000, 7000, 9000, 12000, 16000, 21000, 27000}; + + for (auto creature : VLC->creh->creatures) + { + if (!creature->special && VLC->townh->factions[creature->faction]->nativeTerrain == terrainType) + { + int actualTier = creature->level > 7 ? 6 : creature->level-1; + int creaturesAmount = tierValues[actualTier] / creature->AIValue; + if (creaturesAmount <= 5) + { + } + else if (creaturesAmount <= 12) + { + (creaturesAmount /= 2) *= 2; + } + else if (creaturesAmount <= 50) + { + creaturesAmount = boost::math::round((float)creaturesAmount / 5) * 5; + } + else if (creaturesAmount <= 12) + { + creaturesAmount = boost::math::round((float)creaturesAmount / 10) * 10; + } + + oi.generateObject = [creature, creaturesAmount]() -> CGObjectInstance * + { + auto obj = new CGPandoraBox(); + obj->ID = Obj::PANDORAS_BOX; + obj->subID = 0; + auto stack = new CStackInstance(creature, creaturesAmount); + obj->creatures.putStack(SlotID(0), stack); + return obj; + }; + oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType); + oi.value = (2 * (creature->AIValue) * creaturesAmount * (1 + (float)(gen->getZoneCount(creature->faction)) / gen->getTotalZoneCount()))/3; //TODO: count number of towns on the map + oi.probability = 3; + possibleObjects.push_back (oi); + } + } + + //Pandora with 12 spells of certain level + for (int i = 1; i <= GameConstants::SPELL_LEVELS; i++) + { + oi.generateObject = [i, gen]() -> CGObjectInstance * + { + auto obj = new CGPandoraBox(); + obj->ID = Obj::PANDORAS_BOX; + obj->subID = 0; + + std::vector spells; + for (auto spell : VLC->spellh->objects) + { + if (!spell->isSpecialSpell() && spell->level == i) + spells.push_back(spell); + } + + RandomGeneratorUtil::randomShuffle(spells, gen->rand); + for (int j = 0; j < std::min(12, spells.size()); j++) + { + obj->spells.push_back (spells[j]->id); + } + + return obj; + }; + oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType); + oi.value = (i + 1) * 2500; //5000 - 15000 + oi.probability = 2; + possibleObjects.push_back (oi); + } + + //Pandora with 15 spells of certain school + for (int i = 1; i <= 4; i++) + { + oi.generateObject = [i, gen]() -> CGObjectInstance * + { + auto obj = new CGPandoraBox(); + obj->ID = Obj::PANDORAS_BOX; + obj->subID = 0; + + std::vector spells; + for (auto spell : VLC->spellh->objects) + { + if (!spell->isSpecialSpell()) + { + bool school = false; //TODO: we could have better interface for iterating schools + switch (i) + { + case 1: + school = spell->air; + case 2: + school = spell->earth; + case 3: + school = spell->fire; + case 4: + school = spell->water; + } + if (school) + spells.push_back(spell); + } + } + + RandomGeneratorUtil::randomShuffle(spells, gen->rand); + for (int j = 0; j < std::min(15, spells.size()); j++) + { + obj->spells.push_back (spells[j]->id); + } + + return obj; + }; + oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType); + oi.value = 15000; + oi.probability = 2; + possibleObjects.push_back (oi); + } + + // Pandora box with 60 random spells + + oi.generateObject = [gen]() -> CGObjectInstance * + { + auto obj = new CGPandoraBox(); + obj->ID = Obj::PANDORAS_BOX; + obj->subID = 0; + + std::vector spells; + for (auto spell : VLC->spellh->objects) + { + if (!spell->isSpecialSpell()) + spells.push_back(spell); + } + + RandomGeneratorUtil::randomShuffle(spells, gen->rand); + for (int j = 0; j < std::min(60, spells.size()); j++) + { + obj->spells.push_back (spells[j]->id); + } + + return obj; + }; + oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType); + oi.value = 3000; + oi.probability = 2; + possibleObjects.push_back (oi); } + +void ObjectInfo::setTemplate (si32 type, si32 subtype, ETerrainType terrainType) +{ + templ = VLC->objtypeh->getHandlerFor(type, subtype)->getTemplates(terrainType).front(); +} \ No newline at end of file diff --git a/lib/rmg/CRmgTemplateZone.h b/lib/rmg/CRmgTemplateZone.h index 03532719d..d35d7f4e8 100644 --- a/lib/rmg/CRmgTemplateZone.h +++ b/lib/rmg/CRmgTemplateZone.h @@ -16,6 +16,7 @@ #include "float3.h" #include "../int3.h" #include "../ResourceSet.h" //for TResource (?) +#include "../mapObjects/ObjectTemplate.h" class CMapGenerator; class CTileInfo; @@ -45,6 +46,7 @@ public: bool shouldBeBlocked() const; bool isPossible() const; bool isFree() const; + bool isUsed() const; void setOccupied(ETileType::ETileType value); ETerrainType getTerrainType() const; void setTerrainType(ETerrainType value); @@ -66,9 +68,25 @@ public: struct DLL_LINKAGE ObjectInfo { + ObjectTemplate templ; ui32 value; ui16 probability; + ui32 maxPerZone; + ui32 maxPerMap; std::function generateObject; + + void setTemplate (si32 type, si32 subtype, ETerrainType terrain); + + bool operator==(const ObjectInfo& oi) const { return (templ == oi.templ); } +}; + +struct DLL_LINKAGE CTreasurePileInfo +{ + std::set visitableFromBottomPositions; //can be visited only from bottom or side + std::set visitableFromTopPositions; //they can be visited from any direction + std::set blockedPositions; + std::set occupiedPositions; //blocked + visitable + int3 nextTreasurePos; }; /// The CRmgTemplateZone describes a zone in a template. @@ -128,13 +146,15 @@ public: void addTile (const int3 &pos); std::set getTileInfo () const; + void discardDistantTiles (CMapGenerator* gen, float distance); void addRequiredObject(CGObjectInstance * obj, si32 guardStrength=0); - bool addMonster(CMapGenerator* gen, int3 &pos, si32 strength); + bool addMonster(CMapGenerator* gen, int3 &pos, si32 strength, bool clearSurroundingTiles = true, bool zoneGuard = false); bool createTreasurePile (CMapGenerator* gen, int3 &pos); bool fill (CMapGenerator* gen); bool placeMines (CMapGenerator* gen); void initTownType (CMapGenerator* gen); + void paintZoneTerrain (CMapGenerator* gen, ETerrainType terrainType); void initTerrainType (CMapGenerator* gen); void createBorder(CMapGenerator* gen); void fractalize(CMapGenerator* gen); @@ -152,7 +172,9 @@ public: std::vector getTreasureInfo(); std::set* getFreePaths(); - ObjectInfo getRandomObject (CMapGenerator* gen, ui32 value); + ObjectInfo getRandomObject (CMapGenerator* gen, CTreasurePileInfo &info, ui32 value); + + void placeAndGuardObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, si32 str, bool zoneGuard = false); private: //template info @@ -189,10 +211,11 @@ private: bool pointIsIn(int x, int y); void addAllPossibleObjects (CMapGenerator* gen); //add objects, including zone-specific, to possibleObjects + bool isAccessibleFromAnywhere (CMapGenerator* gen, ObjectTemplate &appearance, int3 &tile, const std::set &tilesBlockedByObject) const; bool findPlaceForObject(CMapGenerator* gen, CGObjectInstance* obj, si32 min_dist, int3 &pos); bool findPlaceForTreasurePile(CMapGenerator* gen, si32 min_dist, int3 &pos); bool canObstacleBePlacedHere(CMapGenerator* gen, ObjectTemplate &temp, int3 &pos); void checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos); void placeObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos); - bool guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str); + bool guardObject(CMapGenerator* gen, CGObjectInstance* object, si32 str, bool zoneGuard = false); }; diff --git a/lib/rmg/CZonePlacer.cpp b/lib/rmg/CZonePlacer.cpp index 86a8fd072..0471f9373 100644 --- a/lib/rmg/CZonePlacer.cpp +++ b/lib/rmg/CZonePlacer.cpp @@ -43,7 +43,8 @@ void CZonePlacer::placeZones(shared_ptr mapGenOptions, CRandomGe //some relaxation-simmulated annealing algorithm const int iterations = 100; - float temperature = 1e-2;; + float temperatureConstant = 1e-2; + float currentTemperature = 2; //geater temperature - stronger gravity, weaker pushing away const float temperatureModifier = 0.99; logGlobal->infoStream() << "Starting zone placement"; @@ -52,8 +53,7 @@ void CZonePlacer::placeZones(shared_ptr mapGenOptions, CRandomGe int height = mapGenOptions->getHeight(); auto zones = gen->getZones(); - - //TODO: consider underground zones + bool underground = mapGenOptions->getHasTwoLevels(); /* let's assume we try to fit N circular zones with radius = size on a map @@ -62,13 +62,37 @@ void CZonePlacer::placeZones(shared_ptr mapGenOptions, CRandomGe prescaler = sqrt((WH)/(sum(n^2)*pi)) */ + std::vector> zonesVector (zones.begin(), zones.end()); + assert (zonesVector.size()); + + RandomGeneratorUtil::randomShuffle(zonesVector, *rand); + TRmgTemplateZoneId firstZone = zones.begin()->first; //we want lowest ID here + bool undergroundFlag = false; + float totalSize = 0; - for (auto zone : zones) + for (auto zone : zonesVector) { + //even distribution for surface / underground zones. Surface zones always have priority. + int level = 0; + if (underground) //only then consider underground zones + { + if (zone.first == firstZone) + { + level = 0; + } + else + { + level = undergroundFlag; + undergroundFlag = !undergroundFlag; //toggle underground on/off + } + } + totalSize += (zone.second->getSize() * zone.second->getSize()); - zone.second->setCenter (float3(rand->nextDouble(0.2,0.8), rand->nextDouble(0.2,0.8), 0)); //start away from borders + zone.second->setCenter (float3(rand->nextDouble(0.2, 0.8), rand->nextDouble(0.2, 0.8), level)); //start away from borders } //prescale zones + if (underground) //map is twice as big, so zones occupy only half of normal space + totalSize /= 2; float prescaler = sqrt ((width * height) / (totalSize * 3.14f)); float mapSize = sqrt (width * height); for (auto zone : zones) @@ -95,63 +119,66 @@ void CZonePlacer::placeZones(shared_ptr mapGenOptions, CRandomGe for (auto con : zone.second->getConnections()) { auto otherZone = zones[con]; - float distance = pos.dist2d (otherZone->getCenter()); + float3 otherZoneCenter = otherZone->getCenter(); + float distance = pos.dist2d (otherZoneCenter); float minDistance = (zone.second->getSize() + otherZone->getSize())/mapSize; //scale down to (0,1) coordinates if (distance > minDistance) { - forceVector += (otherZone->getCenter() - pos) / getDistance(distance); //positive value + //WARNING: compiler used to 'optimize' that line so it never actually worked + forceVector += (((otherZoneCenter - pos) / getDistance(distance)) * currentTemperature); //positive value } } //separate overlaping zones for (auto otherZone : zones) { - if (zone == otherZone) + float3 otherZoneCenter = otherZone.second->getCenter(); + //zones on different levels don't push away + if (zone == otherZone || pos.z != otherZoneCenter.z) continue; - float distance = pos.dist2d (otherZone.second->getCenter()); + float distance = pos.dist2d (otherZoneCenter); float minDistance = (zone.second->getSize() + otherZone.second->getSize())/mapSize; if (distance < minDistance) { - forceVector -= (otherZone.second->getCenter() - pos) / getDistance(distance); //negative value + forceVector -= (otherZoneCenter - pos) / getDistance(distance) / currentTemperature; //negative value } } //move zones away from boundaries - float3 boundary(0,0,pos.z); float size = zone.second->getSize() / mapSize; + + auto pushAwayFromBoundary = [&forceVector, pos, currentTemperature, &getDistance](float x, float y) + { + float3 boundary = float3 (x, y, pos.z); + float distance = pos.dist2d(boundary); + forceVector -= (boundary - pos) / getDistance(distance) / currentTemperature; //negative value + }; if (pos.x < size) { - boundary = float3 (0, pos.y, pos.z); - float distance = pos.dist2d(boundary); - forceVector -= (boundary - pos) / getDistance(distance); //negative value + pushAwayFromBoundary(0, pos.y); } if (pos.x > 1-size) { - boundary = float3 (1, pos.y, pos.z); - float distance = pos.dist2d(boundary); - forceVector -= (boundary - pos) / getDistance(distance); //negative value + pushAwayFromBoundary(1, pos.y); } if (pos.y < size) { - boundary = float3 (pos.x, 0, pos.z); - float distance = pos.dist2d(boundary); - forceVector -= (boundary - pos) / getDistance(distance); //negative value + pushAwayFromBoundary(pos.x, 0); } if (pos.y > 1-size) { - boundary = float3 (pos.x, 1, pos.z); - float distance = pos.dist2d(boundary); - forceVector -= (boundary - pos) / getDistance(distance); //negative value + pushAwayFromBoundary(pos.x, 1); } - forces[zone.second] = forceVector; + forceVector.z = 0; //operator - doesn't preserve z coordinate :/ + forces[zone.second] = forceVector * temperatureConstant; } //update positions for (auto zone : forces) { - zone.first->setCenter (zone.first->getCenter() + zone.second * temperature); + zone.first->setCenter (zone.first->getCenter() + zone.second); } - temperature *= temperatureModifier; //decrease temperature (needed?) + currentTemperature *= temperatureModifier; //decrease temperature (needed?) } for (auto zone : zones) //finalize zone positions { @@ -213,7 +240,10 @@ void CZonePlacer::assignZones(shared_ptr mapGenOptions) int3 pos(i, j, k); for (auto zone : zones) { - distances.push_back (std::make_pair(zone.second, metric(pos, zone.second->getPos()))); + if (zone.second->getPos().z == k) + distances.push_back (std::make_pair(zone.second, metric(pos, zone.second->getPos()))); + else + distances.push_back (std::make_pair(zone.second, std::numeric_limits::max())); } boost::sort (distances, compareByDistance); distances.front().first->addTile(pos); //closest tile belongs to zone @@ -230,7 +260,18 @@ void CZonePlacer::assignZones(shared_ptr mapGenOptions) total += tile; } int size = tiles.size(); + assert (size); zone.second->setPos (int3(total.x/size, total.y/size, total.z/size)); + + //TODO: similiar for islands + if (zone.second->getPos().z) + { + zone.second->discardDistantTiles(gen, zone.second->getSize() + 1); + + //make sure that terrain inside zone is not a rock + //FIXME: reorder actions? + zone.second->paintZoneTerrain (gen, ETerrainType::SUBTERRANEAN); + } } logGlobal->infoStream() << "Finished zone colouring"; }