mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-21 21:17:49 +02:00
vcmi: setup moats using MoatAbility
Setup moats using moat ability, need playtest for now. -3 to defence not added for now.
This commit is contained in:
parent
6c5f5dba75
commit
aab5b47038
config
factions
castle.jsonconflux.jsondungeon.jsonfortress.jsoninferno.jsonnecropolis.jsonrampart.jsonstronghold.jsontower.json
gameConfig.jsonschemas
spells
lib
server
@ -147,8 +147,7 @@
|
||||
"horde" : [ 2, -1 ],
|
||||
"mageGuild" : 4,
|
||||
"warMachine" : "ballista",
|
||||
"moatDamage" : 70,
|
||||
"moatHexes" : [ 11, 28, 44, 61, 77, 111, 129, 146, 164, 181 ],
|
||||
"moatAbility" : "core:spell.castleMoat",
|
||||
// primaryResource not specified so town get both Wood and Ore for resource bonus
|
||||
|
||||
"buildings" :
|
||||
|
@ -152,8 +152,7 @@
|
||||
"mageGuild" : 5,
|
||||
"primaryResource" : "mercury",
|
||||
"warMachine" : "ballista",
|
||||
"moatDamage" : 70,
|
||||
"moatHexes" : [ 11, 28, 44, 61, 77, 111, 129, 146, 164, 181 ],
|
||||
"moatAbility" : "core:spell.castleMoat",
|
||||
|
||||
"buildings" :
|
||||
{
|
||||
|
@ -148,8 +148,8 @@
|
||||
"mageGuild" : 5,
|
||||
"primaryResource" : "sulfur",
|
||||
"warMachine" : "ballista",
|
||||
"moatDamage" : 90,
|
||||
"moatHexes" : [ 11, 28, 44, 61, 77, 111, 129, 146, 164, 181 ],
|
||||
"moatAbility" : "core:spell.dungeonMoat",
|
||||
|
||||
|
||||
"buildings" :
|
||||
{
|
||||
|
@ -147,8 +147,7 @@
|
||||
"horde" : [ 0, -1 ],
|
||||
"mageGuild" : 3,
|
||||
"warMachine" : "firstAidTent",
|
||||
"moatDamage" : 90,
|
||||
"moatHexes" : [ 10, 11, 27, 28, 43, 44, 60, 61, 76, 77, 94, 110, 111, 128, 129, 145, 146, 163, 164, 180, 181 ],
|
||||
"moatAbility" : "core:spell.fortressMoat",
|
||||
// primaryResource not specified so town get both Wood and Ore for resource bonus
|
||||
|
||||
"buildings" :
|
||||
|
@ -149,8 +149,7 @@
|
||||
"mageGuild" : 5,
|
||||
"primaryResource" : "mercury",
|
||||
"warMachine" : "ammoCart",
|
||||
"moatDamage" : 90,
|
||||
"moatHexes" : [ 11, 28, 44, 61, 77, 111, 129, 146, 164, 181 ],
|
||||
"moatAbility" : "core:spell.infernoMoat",
|
||||
|
||||
"buildings" :
|
||||
{
|
||||
|
@ -152,8 +152,7 @@
|
||||
"horde" : [ 0, -1 ],
|
||||
"mageGuild" : 5,
|
||||
"warMachine" : "firstAidTent",
|
||||
"moatDamage" : 70,
|
||||
"moatHexes" : [ 11, 28, 44, 61, 77, 111, 129, 146, 164, 181 ],
|
||||
"moatAbility" : "core:spell.necropolisMoat",
|
||||
// primaryResource not specified so town get both Wood and Ore for resource bonus
|
||||
|
||||
"buildings" :
|
||||
|
@ -152,8 +152,7 @@
|
||||
"mageGuild" : 5,
|
||||
"primaryResource" : "crystal",
|
||||
"warMachine" : "firstAidTent",
|
||||
"moatDamage" : 70,
|
||||
"moatHexes" : [ 11, 28, 44, 61, 77, 111, 129, 146, 164, 181 ],
|
||||
"moatAbility" : "core:spell.rampartMoat",
|
||||
|
||||
"buildings" :
|
||||
{
|
||||
|
@ -145,8 +145,7 @@
|
||||
"horde" : [ 0, -1 ],
|
||||
"mageGuild" : 3,
|
||||
"warMachine" : "ammoCart",
|
||||
"moatDamage" : 70,
|
||||
"moatHexes" : [ 11, 28, 44, 61, 77, 111, 129, 146, 164, 181 ],
|
||||
"moatAbility" : "core:spell.strongholdMoat",
|
||||
// primaryResource not specified so town get both Wood and Ore for resource bonus
|
||||
|
||||
"buildings" :
|
||||
|
@ -147,8 +147,7 @@
|
||||
"primaryResource" : "gems",
|
||||
"mageGuild" : 5,
|
||||
"warMachine" : "ammoCart",
|
||||
"moatDamage" : 0, //TODO: minefield
|
||||
"moatHexes" : [ 11, 28, 44, 61, 77, 111, 129, 146, 164, 181 ],
|
||||
"moatAbility" : "core:spell.towerMoat",
|
||||
|
||||
"buildings" :
|
||||
{
|
||||
|
@ -80,6 +80,7 @@
|
||||
"config/spells/timed.json",
|
||||
"config/spells/ability.json",
|
||||
"config/spells/vcmiAbility.json"
|
||||
"config/spells/moats.json"
|
||||
],
|
||||
"skills" :
|
||||
[
|
||||
|
@ -109,7 +109,7 @@
|
||||
"additionalProperties" : false,
|
||||
"required" : [
|
||||
"mapObject", "buildingsIcons", "buildings", "creatures", "guildWindow", "names",
|
||||
"hallBackground", "hallSlots", "horde", "mageGuild", "moatDamage", "defaultTavern", "tavernVideo", "guildBackground", "musicTheme", "siege", "structures", "townBackground", "warMachine"
|
||||
"hallBackground", "hallSlots", "horde", "mageGuild", "moatAbility", "defaultTavern", "tavernVideo", "guildBackground", "musicTheme", "siege", "structures", "townBackground", "warMachine"
|
||||
],
|
||||
"description": "town",
|
||||
"properties":{
|
||||
@ -230,14 +230,9 @@
|
||||
"type":"number",
|
||||
"description": "Maximal level of mage guild"
|
||||
},
|
||||
"moatDamage": {
|
||||
"type":"number",
|
||||
"description": "Damage dealt to creature that entered town moat during siege"
|
||||
},
|
||||
"moatHexes": {
|
||||
"type" : "array",
|
||||
"description" : "Numbers of battlefield hexes affected by moat during siege",
|
||||
"items" : { "type" : "number" }
|
||||
"moatAbility": {
|
||||
"type":"string",
|
||||
"description": "Identifier of ability to use as town moat during siege"
|
||||
},
|
||||
"musicTheme": {
|
||||
"type":"string",
|
||||
|
677
config/spells/moats.json
Normal file
677
config/spells/moats.json
Normal file
@ -0,0 +1,677 @@
|
||||
{
|
||||
"castleMoatTrigger" :
|
||||
{
|
||||
"targetType" : "CREATURE",
|
||||
"type": "ability",
|
||||
"name": "Moat",
|
||||
"school": {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base": {
|
||||
"power" : 0,
|
||||
"range" : "0",
|
||||
"description" : "", //For validation
|
||||
"cost" : 0, //For validation
|
||||
"aiValue" : 0, //For validation
|
||||
"battleEffects" : {
|
||||
"directDamage" : {
|
||||
"type":"core:damage"
|
||||
}
|
||||
},
|
||||
"targetModifier":{"smart":false}
|
||||
},
|
||||
"none" : {
|
||||
},
|
||||
"basic" : {
|
||||
},
|
||||
"advanced" : {
|
||||
},
|
||||
"expert" : {
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"damage": true,
|
||||
"negative": true,
|
||||
"special": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
}
|
||||
},
|
||||
"castleMoat": {
|
||||
"targetType" : "NO_TARGET",
|
||||
"type": "ability",
|
||||
"name": "Moat",
|
||||
"school" : {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"defaultGainChance": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base":{
|
||||
"description" : "",
|
||||
"aiValue" : 0,
|
||||
"power" : 0,
|
||||
"cost" : 0,
|
||||
"targetModifier":{"smart":false},
|
||||
"battleEffects":{
|
||||
"moat":{
|
||||
"type":"core:moat",
|
||||
"hidden" : false,
|
||||
"trap" : true,
|
||||
"trigger" : true,
|
||||
"triggerAbility" : "core:castleMoatTrigger",
|
||||
"dispellable" : false,
|
||||
"removeOnTrigger" : false,
|
||||
"moatDamage" : 70,
|
||||
"moatHexes" : [[11, 28, 44, 61, 77, 111, 129, 146, 164, 181]],
|
||||
"defender" :{
|
||||
}
|
||||
}
|
||||
},
|
||||
"range" : "X"
|
||||
},
|
||||
"none" :{
|
||||
},
|
||||
"basic" :{
|
||||
},
|
||||
"advanced" :{
|
||||
},
|
||||
"expert" :{
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"indifferent": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
"nonMagical" : true
|
||||
}
|
||||
},
|
||||
"rampartMoatTrigger" :
|
||||
{
|
||||
"targetType" : "CREATURE",
|
||||
"type": "ability",
|
||||
"name": "Brambles",
|
||||
"school": {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base": {
|
||||
"power" : 0,
|
||||
"range" : "0",
|
||||
"description" : "", //For validation
|
||||
"cost" : 0, //For validation
|
||||
"aiValue" : 0, //For validation
|
||||
"battleEffects" : {
|
||||
"directDamage" : {
|
||||
"type":"core:damage"
|
||||
}
|
||||
},
|
||||
"targetModifier":{"smart":false}
|
||||
},
|
||||
"none" : {
|
||||
},
|
||||
"basic" : {
|
||||
},
|
||||
"advanced" : {
|
||||
},
|
||||
"expert" : {
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"damage": true,
|
||||
"negative": true,
|
||||
"special": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
}
|
||||
},
|
||||
"rampartMoat": {
|
||||
"targetType" : "NO_TARGET",
|
||||
"type": "ability",
|
||||
"name": "Brambles",
|
||||
"school" : {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"defaultGainChance": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base":{
|
||||
"description" : "",
|
||||
"aiValue" : 0,
|
||||
"power" : 0,
|
||||
"cost" : 0,
|
||||
"targetModifier":{"smart":false},
|
||||
"battleEffects":{
|
||||
"moat":{
|
||||
"type":"core:moat",
|
||||
"hidden" : false,
|
||||
"trap" : true,
|
||||
"trigger" : true,
|
||||
"triggerAbility" : "core:rampartMoatTrigger",
|
||||
"dispellable" : false,
|
||||
"removeOnTrigger" : false,
|
||||
"moatDamage" : 70,
|
||||
"moatHexes" : [[11, 28, 44, 61, 77, 111, 129, 146, 164, 181]],
|
||||
"defender" :{
|
||||
}
|
||||
}
|
||||
},
|
||||
"range" : "X"
|
||||
},
|
||||
"none" :{
|
||||
},
|
||||
"basic" :{
|
||||
},
|
||||
"advanced" :{
|
||||
},
|
||||
"expert" :{
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"indifferent": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
"nonMagical" : true
|
||||
}
|
||||
},
|
||||
"towerMoat": {
|
||||
"targetType" : "NO_TARGET",
|
||||
"type": "ability",
|
||||
"name": "Land Mine",
|
||||
"school" : {},
|
||||
"level": 3,
|
||||
"power": 0,
|
||||
"defaultGainChance": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base":{
|
||||
"description" : "",
|
||||
"aiValue" : 0,
|
||||
"power" : 0,
|
||||
"cost" : 0,
|
||||
"targetModifier":{"smart":false},
|
||||
"battleEffects":{
|
||||
"moat":{
|
||||
"type":"core:moat",
|
||||
"hidden" : true,
|
||||
"trap" : false,
|
||||
"trigger" : true,
|
||||
"triggerAbility" : "core:landMineTrigger",
|
||||
"dispellable" : true,
|
||||
"removeOnTrigger" : true,
|
||||
"moatDamage" : 150,
|
||||
"moatHexes" : [[11], [28], [44], [61], [77], [111], [129], [146], [164], [181]],
|
||||
"defender" :{
|
||||
"animation" : "C09SPF1",
|
||||
"appearAnimation" : "C09SPF0",
|
||||
"appearSound" : "LANDMINE"
|
||||
}
|
||||
}
|
||||
},
|
||||
"range" : "X"
|
||||
},
|
||||
"none" :{
|
||||
},
|
||||
"basic" :{
|
||||
},
|
||||
"advanced" :{
|
||||
},
|
||||
"expert" :{
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"indifferent": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
"nonMagical" : true
|
||||
}
|
||||
},
|
||||
"infernoMoatTrigger" :
|
||||
{
|
||||
"targetType" : "CREATURE",
|
||||
"type": "ability",
|
||||
"name": "Lava",
|
||||
"school": {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base": {
|
||||
"power" : 0,
|
||||
"range" : "0",
|
||||
"description" : "", //For validation
|
||||
"cost" : 0, //For validation
|
||||
"aiValue" : 0, //For validation
|
||||
"battleEffects" : {
|
||||
"directDamage" : {
|
||||
"type":"core:damage"
|
||||
}
|
||||
},
|
||||
"targetModifier":{"smart":false}
|
||||
},
|
||||
"none" : {
|
||||
},
|
||||
"basic" : {
|
||||
},
|
||||
"advanced" : {
|
||||
},
|
||||
"expert" : {
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"damage": true,
|
||||
"negative": true,
|
||||
"special": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
}
|
||||
},
|
||||
"infernoMoat": {
|
||||
"targetType" : "NO_TARGET",
|
||||
"type": "ability",
|
||||
"name": "Lava",
|
||||
"school" : {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"defaultGainChance": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base":{
|
||||
"description" : "",
|
||||
"aiValue" : 0,
|
||||
"power" : 0,
|
||||
"cost" : 0,
|
||||
"targetModifier":{"smart":false},
|
||||
"battleEffects":{
|
||||
"moat":{
|
||||
"type":"core:moat",
|
||||
"hidden" : false,
|
||||
"trap" : true,
|
||||
"trigger" : true,
|
||||
"triggerAbility" : "core:infernoMoatTrigger",
|
||||
"dispellable" : false,
|
||||
"removeOnTrigger" : false,
|
||||
"moatDamage" : 90,
|
||||
"moatHexes" : [[11, 28, 44, 61, 77, 111, 129, 146, 164, 181]],
|
||||
"defender" :{
|
||||
}
|
||||
}
|
||||
},
|
||||
"range" : "X"
|
||||
},
|
||||
"none" :{
|
||||
},
|
||||
"basic" :{
|
||||
},
|
||||
"advanced" :{
|
||||
},
|
||||
"expert" :{
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"indifferent": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
"nonMagical" : true
|
||||
}
|
||||
},
|
||||
"necropolisMoatTrigger" :
|
||||
{
|
||||
"targetType" : "CREATURE",
|
||||
"type": "ability",
|
||||
"name": "Boneyard",
|
||||
"school": {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base": {
|
||||
"power" : 0,
|
||||
"range" : "0",
|
||||
"description" : "", //For validation
|
||||
"cost" : 0, //For validation
|
||||
"aiValue" : 0, //For validation
|
||||
"battleEffects" : {
|
||||
"directDamage" : {
|
||||
"type":"core:damage"
|
||||
}
|
||||
},
|
||||
"targetModifier":{"smart":false}
|
||||
},
|
||||
"none" : {
|
||||
},
|
||||
"basic" : {
|
||||
},
|
||||
"advanced" : {
|
||||
},
|
||||
"expert" : {
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"damage": true,
|
||||
"negative": true,
|
||||
"special": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
}
|
||||
},
|
||||
"necropolisMoat": {
|
||||
"targetType" : "NO_TARGET",
|
||||
"type": "ability",
|
||||
"name": "Boneyard",
|
||||
"school" : {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"defaultGainChance": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base":{
|
||||
"description" : "",
|
||||
"aiValue" : 0,
|
||||
"power" : 0,
|
||||
"cost" : 0,
|
||||
"targetModifier":{"smart":false},
|
||||
"battleEffects":{
|
||||
"moat":{
|
||||
"type":"core:moat",
|
||||
"hidden" : true,
|
||||
"trap" : false,
|
||||
"trigger" : true,
|
||||
"triggerAbility" : "core:necropolisMoatTrigger",
|
||||
"dispellable" : false,
|
||||
"removeOnTrigger" : false,
|
||||
"moatDamage" : 70,
|
||||
"moatHexes" : [[11, 28, 44, 61, 77, 111, 129, 146, 164, 181]],
|
||||
"defender" :{
|
||||
}
|
||||
}
|
||||
},
|
||||
"range" : "X"
|
||||
},
|
||||
"none" :{
|
||||
},
|
||||
"basic" :{
|
||||
},
|
||||
"advanced" :{
|
||||
},
|
||||
"expert" :{
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"indifferent": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
"nonMagical" : true
|
||||
}
|
||||
},
|
||||
"dungeonMoatTrigger" :
|
||||
{
|
||||
"targetType" : "CREATURE",
|
||||
"type": "ability",
|
||||
"name": "Boiling Oil",
|
||||
"school": {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base": {
|
||||
"power" : 0,
|
||||
"range" : "0",
|
||||
"description" : "", //For validation
|
||||
"cost" : 0, //For validation
|
||||
"aiValue" : 0, //For validation
|
||||
"battleEffects" : {
|
||||
"directDamage" : {
|
||||
"type":"core:damage"
|
||||
}
|
||||
},
|
||||
"targetModifier":{"smart":false}
|
||||
},
|
||||
"none" : {
|
||||
},
|
||||
"basic" : {
|
||||
},
|
||||
"advanced" : {
|
||||
},
|
||||
"expert" : {
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"damage": true,
|
||||
"negative": true,
|
||||
"special": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
}
|
||||
},
|
||||
"dungeonMoat": {
|
||||
"targetType" : "NO_TARGET",
|
||||
"type": "ability",
|
||||
"name": "Boiling Oil",
|
||||
"school" : {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"defaultGainChance": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base":{
|
||||
"description" : "",
|
||||
"aiValue" : 0,
|
||||
"power" : 0,
|
||||
"cost" : 0,
|
||||
"targetModifier":{"smart":false},
|
||||
"battleEffects":{
|
||||
"moat":{
|
||||
"type":"core:moat",
|
||||
"hidden" : false,
|
||||
"trap" : true,
|
||||
"trigger" : true,
|
||||
"triggerAbility" : "core:dungeonMoatTrigger",
|
||||
"dispellable" : false,
|
||||
"removeOnTrigger" : false,
|
||||
"moatDamage" : 90,
|
||||
"moatHexes" : [[11, 28, 44, 61, 77, 111, 129, 146, 164, 181]],
|
||||
"defender" :{
|
||||
}
|
||||
}
|
||||
},
|
||||
"range" : "X"
|
||||
},
|
||||
"none" :{
|
||||
},
|
||||
"basic" :{
|
||||
},
|
||||
"advanced" :{
|
||||
},
|
||||
"expert" :{
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"indifferent": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
"nonMagical" : true
|
||||
}
|
||||
},
|
||||
"strongholdMoatTrigger" :
|
||||
{
|
||||
"targetType" : "CREATURE",
|
||||
"type": "ability",
|
||||
"name": "Wooden Spikes",
|
||||
"school": {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base": {
|
||||
"power" : 0,
|
||||
"range" : "0",
|
||||
"description" : "", //For validation
|
||||
"cost" : 0, //For validation
|
||||
"aiValue" : 0, //For validation
|
||||
"battleEffects" : {
|
||||
"directDamage" : {
|
||||
"type":"core:damage"
|
||||
}
|
||||
},
|
||||
"targetModifier":{"smart":false}
|
||||
},
|
||||
"none" : {
|
||||
},
|
||||
"basic" : {
|
||||
},
|
||||
"advanced" : {
|
||||
},
|
||||
"expert" : {
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"damage": true,
|
||||
"negative": true,
|
||||
"special": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
}
|
||||
},
|
||||
"strongholdMoat": {
|
||||
"targetType" : "NO_TARGET",
|
||||
"type": "ability",
|
||||
"name": "Wooden Spikes",
|
||||
"school" : {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"defaultGainChance": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base":{
|
||||
"description" : "",
|
||||
"aiValue" : 0,
|
||||
"power" : 0,
|
||||
"cost" : 0,
|
||||
"targetModifier":{"smart":false},
|
||||
"battleEffects":{
|
||||
"moat":{
|
||||
"type":"core:moat",
|
||||
"hidden" : false,
|
||||
"trap" : true,
|
||||
"trigger" : true,
|
||||
"triggerAbility" : "core:strongholdMoatTrigger",
|
||||
"dispellable" : false,
|
||||
"removeOnTrigger" : false,
|
||||
"moatDamage" : 70,
|
||||
"moatHexes" : [[11, 28, 44, 61, 77, 111, 129, 146, 164, 181]],
|
||||
"defender" :{
|
||||
}
|
||||
}
|
||||
},
|
||||
"range" : "X"
|
||||
},
|
||||
"none" :{
|
||||
},
|
||||
"basic" :{
|
||||
},
|
||||
"advanced" :{
|
||||
},
|
||||
"expert" :{
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"indifferent": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
"nonMagical" : true
|
||||
}
|
||||
},
|
||||
"fortressMoatTrigger" :
|
||||
{
|
||||
"targetType" : "CREATURE",
|
||||
"type": "ability",
|
||||
"name": "Boiling Tar",
|
||||
"school": {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base": {
|
||||
"power" : 0,
|
||||
"range" : "0",
|
||||
"description" : "", //For validation
|
||||
"cost" : 0, //For validation
|
||||
"aiValue" : 0, //For validation
|
||||
"battleEffects" : {
|
||||
"directDamage" : {
|
||||
"type":"core:damage"
|
||||
}
|
||||
},
|
||||
"targetModifier":{"smart":false}
|
||||
},
|
||||
"none" : {
|
||||
},
|
||||
"basic" : {
|
||||
},
|
||||
"advanced" : {
|
||||
},
|
||||
"expert" : {
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"damage": true,
|
||||
"negative": true,
|
||||
"special": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
}
|
||||
},
|
||||
"fortressMoat": {
|
||||
"targetType" : "NO_TARGET",
|
||||
"type": "ability",
|
||||
"name": "Boiling Tar",
|
||||
"school" : {},
|
||||
"level": 0,
|
||||
"power": 0,
|
||||
"defaultGainChance": 0,
|
||||
"gainChance": {},
|
||||
"levels" : {
|
||||
"base":{
|
||||
"description" : "",
|
||||
"aiValue" : 0,
|
||||
"power" : 0,
|
||||
"cost" : 0,
|
||||
"targetModifier":{"smart":false},
|
||||
"battleEffects":{
|
||||
"moat":{
|
||||
"type":"core:moat",
|
||||
"hidden" : false,
|
||||
"trap" : true,
|
||||
"trigger" : true,
|
||||
"triggerAbility" : "core:fortressMoatTrigger",
|
||||
"dispellable" : false,
|
||||
"removeOnTrigger" : false,
|
||||
"moatDamage" : 90,
|
||||
"moatHexes" : [[10, 11, 27, 28, 43, 44, 60, 61, 76, 77, 94, 110, 111, 128, 129, 145, 146, 163, 164, 180, 181]],
|
||||
"defender" :{
|
||||
}
|
||||
}
|
||||
},
|
||||
"range" : "X"
|
||||
},
|
||||
"none" :{
|
||||
},
|
||||
"basic" :{
|
||||
},
|
||||
"advanced" :{
|
||||
},
|
||||
"expert" :{
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
"indifferent": true
|
||||
},
|
||||
"targetCondition" : {
|
||||
"nonMagical" : true
|
||||
}
|
||||
}
|
||||
}
|
@ -173,7 +173,7 @@ void CFaction::serializeJson(JsonSerializeFormat & handler)
|
||||
|
||||
|
||||
CTown::CTown()
|
||||
: faction(nullptr), mageLevel(0), primaryRes(0), moatDamage(0), defaultTavernChance(0)
|
||||
: faction(nullptr), mageLevel(0), primaryRes(0), moatAbility(SpellID::NONE), defaultTavernChance(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -882,9 +882,6 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
|
||||
|
||||
warMachinesToLoad[town] = source["warMachine"];
|
||||
|
||||
town->moatDamage = static_cast<si32>(source["moatDamage"].Float());
|
||||
town->moatHexes = source["moatHexes"].convertTo<std::vector<BattleHex> >();
|
||||
|
||||
town->mageLevel = static_cast<ui32>(source["mageGuild"].Float());
|
||||
|
||||
town->namesCount = 0;
|
||||
@ -894,6 +891,11 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
|
||||
town->namesCount += 1;
|
||||
}
|
||||
|
||||
VLC->modh->identifiers.requestIdentifier(source["moatAbility"], [=](si32 ability)
|
||||
{
|
||||
town->moatAbility = SpellID(ability);
|
||||
});
|
||||
|
||||
// Horde building creature level
|
||||
for(const JsonNode &node : source["horde"].Vector())
|
||||
town->hordeLvl[static_cast<int>(town->hordeLvl.size())] = static_cast<int>(node.Float());
|
||||
|
@ -271,8 +271,7 @@ public:
|
||||
ui32 mageLevel; //max available mage guild level
|
||||
ui16 primaryRes;
|
||||
ArtifactID warMachine;
|
||||
si32 moatDamage;
|
||||
std::vector<BattleHex> moatHexes;
|
||||
SpellID moatAbility;
|
||||
// default chance for hero of specific class to appear in tavern, if field "tavern" was not set
|
||||
// resulting chance = sqrt(town.chance * heroClass.chance)
|
||||
ui32 defaultTavernChance;
|
||||
@ -339,8 +338,7 @@ public:
|
||||
h & primaryRes;
|
||||
h & warMachine;
|
||||
h & clientInfo;
|
||||
h & moatDamage;
|
||||
h & moatHexes;
|
||||
h & moatAbility;
|
||||
h & defaultTavernChance;
|
||||
}
|
||||
|
||||
|
@ -451,12 +451,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
|
||||
curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, BattleHex::CASTLE_BOTTOM_TOWER);
|
||||
}
|
||||
|
||||
//moat
|
||||
auto moat = std::make_shared<MoatObstacle>();
|
||||
moat->ID = curB->town->subID;
|
||||
moat->obstacleType = CObstacleInstance::MOAT;
|
||||
moat->uniqueID = static_cast<si32>(curB->obstacles.size());
|
||||
curB->obstacles.push_back(moat);
|
||||
//Moat generating is done on server
|
||||
}
|
||||
|
||||
std::stable_sort(stacks.begin(),stacks.end(),cmpst);
|
||||
|
@ -162,6 +162,7 @@ void SpellCreatedObstacle::serializeJson(JsonSerializeFormat & handler)
|
||||
handler.serializeInt("spellLevel", spellLevel);
|
||||
handler.serializeInt("casterSide", casterSide);
|
||||
handler.serializeInt("minimalDamage", minimalDamage);
|
||||
handler.serializeInt("type", obstacleType);
|
||||
|
||||
handler.serializeBool("hidden", hidden);
|
||||
handler.serializeBool("revealed", revealed);
|
||||
@ -201,7 +202,7 @@ int SpellCreatedObstacle::getAnimationYOffset(int imageHeight) const
|
||||
{
|
||||
int offset = imageHeight % 42;
|
||||
|
||||
if(obstacleType == CObstacleInstance::SPELL_CREATED)
|
||||
if(obstacleType == CObstacleInstance::SPELL_CREATED || obstacleType == CObstacleInstance::MOAT)
|
||||
{
|
||||
offset += animationYOffset;
|
||||
}
|
||||
|
@ -58,11 +58,6 @@ struct DLL_LINKAGE CObstacleInstance
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE MoatObstacle : CObstacleInstance
|
||||
{
|
||||
std::vector<BattleHex> getAffectedTiles() const override; //for special effects (not blocking)
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE SpellCreatedObstacle : CObstacleInstance
|
||||
{
|
||||
int32_t turnsRemaining;
|
||||
|
@ -203,7 +203,6 @@ void registerTypesMapObjects2(Serializer &s)
|
||||
s.template registerType<CArtifactInstance, CCombinedArtifactInstance>();
|
||||
|
||||
//s.template registerType<CObstacleInstance>();
|
||||
s.template registerType<CObstacleInstance, MoatObstacle>();
|
||||
s.template registerType<CObstacleInstance, SpellCreatedObstacle>();
|
||||
}
|
||||
template<typename Serializer>
|
||||
|
@ -15,21 +15,12 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
namespace spells
|
||||
{
|
||||
|
||||
ObstacleCasterProxy::ObstacleCasterProxy(PlayerColor owner_, const Caster * hero_, const SpellCreatedObstacle * obs_):
|
||||
ProxyCaster(hero_),
|
||||
owner(std::move(owner_)),
|
||||
obs(*obs_)
|
||||
ObstacleCasterProxy::ObstacleCasterProxy(PlayerColor owner_, const Caster * hero_, const SpellCreatedObstacle & obs_):
|
||||
SilentCaster(owner_, hero_),
|
||||
obs(obs_)
|
||||
{
|
||||
}
|
||||
|
||||
int32_t ObstacleCasterProxy::getCasterUnitId() const
|
||||
{
|
||||
if(actualCaster)
|
||||
return actualCaster->getCasterUnitId();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t ObstacleCasterProxy::getSpellSchoolLevel(const Spell * spell, int32_t * outSelectedSchool) const
|
||||
{
|
||||
return obs.spellLevel;
|
||||
@ -44,16 +35,8 @@ int64_t ObstacleCasterProxy::getSpellBonus(const Spell * spell, int64_t base, co
|
||||
{
|
||||
if(actualCaster)
|
||||
return std::max<int64_t>(actualCaster->getSpellBonus(spell, base, affectedStack), obs.minimalDamage);
|
||||
else
|
||||
return std::max<int64_t>(base, obs.minimalDamage);
|
||||
}
|
||||
|
||||
int64_t ObstacleCasterProxy::getSpecificSpellBonus(const Spell * spell, int64_t base) const
|
||||
{
|
||||
if(actualCaster)
|
||||
return actualCaster->getSpecificSpellBonus(spell, base);
|
||||
else
|
||||
return base;
|
||||
return std::max<int64_t>(base, obs.minimalDamage);
|
||||
}
|
||||
|
||||
int32_t ObstacleCasterProxy::getEffectPower(const Spell * spell) const
|
||||
@ -74,25 +57,35 @@ int64_t ObstacleCasterProxy::getEffectValue(const Spell * spell) const
|
||||
return obs.minimalDamage;
|
||||
}
|
||||
|
||||
PlayerColor ObstacleCasterProxy::getCasterOwner() const
|
||||
SilentCaster::SilentCaster(PlayerColor owner_, const Caster * hero_):
|
||||
ProxyCaster(hero_),
|
||||
owner(std::move(owner_))
|
||||
{
|
||||
}
|
||||
|
||||
void SilentCaster::getCasterName(MetaString & text) const
|
||||
{
|
||||
logGlobal->error("Unexpected call to SilentCaster::getCasterName");
|
||||
}
|
||||
|
||||
void SilentCaster::getCastDescription(const Spell * spell, const std::vector<const battle::Unit *> & attacked, MetaString & text) const
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
void SilentCaster::spendMana(ServerCallback * server, const int spellCost) const
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
PlayerColor SilentCaster::getCasterOwner() const
|
||||
{
|
||||
if(actualCaster)
|
||||
return actualCaster->getCasterOwner();
|
||||
|
||||
return owner;
|
||||
}
|
||||
|
||||
void ObstacleCasterProxy::getCasterName(MetaString & text) const
|
||||
{
|
||||
logGlobal->error("Unexpected call to ObstacleCasterProxy::getCasterName");
|
||||
}
|
||||
|
||||
void ObstacleCasterProxy::getCastDescription(const Spell * spell, const std::vector<const battle::Unit *> & attacked, MetaString & text) const
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
void ObstacleCasterProxy::spendMana(ServerCallback * server, const int spellCost) const
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
}
|
||||
VCMI_LIB_NAMESPACE_END
|
@ -17,26 +17,32 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
namespace spells
|
||||
{
|
||||
|
||||
class DLL_LINKAGE ObstacleCasterProxy : public ProxyCaster
|
||||
class DLL_LINKAGE SilentCaster : public ProxyCaster
|
||||
{
|
||||
protected:
|
||||
const PlayerColor owner;
|
||||
public:
|
||||
ObstacleCasterProxy(PlayerColor owner_, const Caster * hero_, const SpellCreatedObstacle * obs_);
|
||||
SilentCaster(PlayerColor owner_, const Caster * caster);
|
||||
|
||||
int32_t getCasterUnitId() const override;
|
||||
int32_t getSpellSchoolLevel(const Spell * spell, int32_t * outSelectedSchool = nullptr) const override;
|
||||
int32_t getEffectLevel(const Spell * spell) const override;
|
||||
int64_t getSpellBonus(const Spell * spell, int64_t base, const battle::Unit * affectedStack) const override;
|
||||
int64_t getSpecificSpellBonus(const Spell * spell, int64_t base) const override;
|
||||
int32_t getEffectPower(const Spell * spell) const override;
|
||||
int32_t getEnchantPower(const Spell * spell) const override;
|
||||
int64_t getEffectValue(const Spell * spell) const override;
|
||||
PlayerColor getCasterOwner() const override;
|
||||
void getCasterName(MetaString & text) const override;
|
||||
void getCastDescription(const Spell * spell, const std::vector<const battle::Unit *> & attacked, MetaString & text) const override;
|
||||
void spendMana(ServerCallback * server, const int spellCost) const override;
|
||||
PlayerColor getCasterOwner() const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE ObstacleCasterProxy : public SilentCaster
|
||||
{
|
||||
public:
|
||||
ObstacleCasterProxy(PlayerColor owner_, const Caster * hero_, const SpellCreatedObstacle & obs_);
|
||||
|
||||
int32_t getSpellSchoolLevel(const Spell * spell, int32_t * outSelectedSchool = nullptr) const override;
|
||||
int32_t getEffectLevel(const Spell * spell) const override;
|
||||
int64_t getSpellBonus(const Spell * spell, int64_t base, const battle::Unit * affectedStack) const override;
|
||||
int32_t getEffectPower(const Spell * spell) const override;
|
||||
int32_t getEnchantPower(const Spell * spell) const override;
|
||||
int64_t getEffectValue(const Spell * spell) const override;
|
||||
|
||||
private:
|
||||
const PlayerColor owner;
|
||||
const SpellCreatedObstacle & obs;
|
||||
};
|
||||
|
||||
|
@ -31,6 +31,23 @@ namespace effects
|
||||
|
||||
VCMI_REGISTER_SPELL_EFFECT(Moat, EFFECT_NAME);
|
||||
|
||||
static void serializeMoatHexes(JsonSerializeFormat & handler, const std::string & fieldName, std::vector<std::vector<BattleHex>> & moatHexes)
|
||||
{
|
||||
{
|
||||
JsonArraySerializer outer = handler.enterArray(fieldName);
|
||||
outer.syncSize(moatHexes, JsonNode::JsonType::DATA_VECTOR);
|
||||
|
||||
for(size_t outerIndex = 0; outerIndex < outer.size(); outerIndex++)
|
||||
{
|
||||
JsonArraySerializer inner = outer.enterArray(outerIndex);
|
||||
inner.syncSize(moatHexes.at(outerIndex), JsonNode::JsonType::DATA_INTEGER);
|
||||
|
||||
for(size_t innerIndex = 0; innerIndex < inner.size(); innerIndex++)
|
||||
inner.serializeInt(innerIndex, moatHexes.at(outerIndex).at(innerIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Moat::serializeJsonEffect(JsonSerializeFormat & handler)
|
||||
{
|
||||
handler.serializeBool("hidden", hidden);
|
||||
@ -39,14 +56,8 @@ void Moat::serializeJsonEffect(JsonSerializeFormat & handler)
|
||||
handler.serializeBool("removeOnTrigger", removeOnTrigger);
|
||||
handler.serializeBool("dispellable", dispellable);
|
||||
handler.serializeInt("moatDamage", moatDamage);
|
||||
serializeMoatHexes(handler, "moatHexes", moatHexes);
|
||||
handler.serializeId("triggerAbility", triggerAbility, SpellID::NONE);
|
||||
{
|
||||
JsonArraySerializer customSizeJson = handler.enterArray("moatHexes");
|
||||
customSizeJson.syncSize(moatHexes, JsonNode::JsonType::DATA_INTEGER);
|
||||
|
||||
for(size_t index = 0; index < customSizeJson.size(); index++)
|
||||
customSizeJson.serializeInt(index, moatHexes.at(index));
|
||||
}
|
||||
handler.serializeStruct("defender", sideOptions); //Moats are defender only
|
||||
}
|
||||
|
||||
@ -57,10 +68,6 @@ void Moat::apply(ServerCallback * server, const Mechanics * m, const EffectTarge
|
||||
if(m->isMassive() && m->battle()->battleGetSiegeLevel() >= CGTownInstance::CITADEL)
|
||||
{
|
||||
EffectTarget moat;
|
||||
moat.reserve(moatHexes.size());
|
||||
for(const auto & tile : moatHexes)
|
||||
moat.emplace_back(tile);
|
||||
|
||||
placeObstacles(server, m, moat);
|
||||
}
|
||||
}
|
||||
@ -80,11 +87,11 @@ void Moat::placeObstacles(ServerCallback * server, const Mechanics * m, const Ef
|
||||
if(one->uniqueID >= obstacleIdToGive)
|
||||
obstacleIdToGive = one->uniqueID + 1;
|
||||
|
||||
for(const Destination & destination : target)
|
||||
for(const auto & destination : moatHexes) //Moat hexes can be different obstacles
|
||||
{
|
||||
SpellCreatedObstacle obstacle;
|
||||
obstacle.uniqueID = obstacleIdToGive++;
|
||||
obstacle.pos = destination.hexValue;
|
||||
obstacle.pos = destination.at(0);
|
||||
obstacle.obstacleType = dispellable ? CObstacleInstance::SPELL_CREATED : CObstacleInstance::MOAT;
|
||||
obstacle.ID = triggerAbility;
|
||||
|
||||
@ -102,7 +109,7 @@ void Moat::placeObstacles(ServerCallback * server, const Mechanics * m, const Ef
|
||||
obstacle.appearSound = sideOptions.appearSound; //For dispellable moats
|
||||
obstacle.appearAnimation = sideOptions.appearAnimation; //For dispellable moats
|
||||
obstacle.animation = sideOptions.animation;
|
||||
obstacle.customSize.emplace_back(obstacle.pos); //All moat hexes are different obstacles
|
||||
obstacle.customSize.insert(obstacle.customSize.end(),destination.cbegin(), destination.cend());
|
||||
obstacle.animationYOffset = sideOptions.offsetY;
|
||||
pack.changes.emplace_back();
|
||||
obstacle.toInfo(pack.changes.back());
|
||||
|
@ -23,7 +23,7 @@ class Moat : public Obstacle
|
||||
{
|
||||
private:
|
||||
ObstacleSideOptions sideOptions; //Defender only
|
||||
std::vector<BattleHex> moatHexes;
|
||||
std::vector<std::vector<BattleHex>> moatHexes;
|
||||
bool dispellable; //For Tower landmines
|
||||
int moatDamage; // Minimal moat damage
|
||||
public:
|
||||
|
@ -5222,12 +5222,9 @@ bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackI
|
||||
{
|
||||
if(!curStack->alive())
|
||||
return false;
|
||||
bool containDamageFromMoat = false;
|
||||
bool movementStopped = false;
|
||||
for(auto & obstacle : getAllAffectedObstaclesByStack(curStack, passed))
|
||||
{
|
||||
if(obstacle->obstacleType == CObstacleInstance::SPELL_CREATED)
|
||||
{
|
||||
//helper info
|
||||
const SpellCreatedObstacle * spellObstacle = dynamic_cast<const SpellCreatedObstacle *>(obstacle.get());
|
||||
const ui8 side = curStack->side;
|
||||
@ -5262,7 +5259,7 @@ bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackI
|
||||
};
|
||||
auto shouldReveal = !spellObstacle->hidden || !gs->curB->battleIsObstacleVisibleForSide(*obstacle, (BattlePerspective::BattlePerspective)side);
|
||||
const auto * hero = gs->curB->battleGetFightingHero(spellObstacle->casterSide);
|
||||
auto caster = spells::ObstacleCasterProxy(gs->curB->sides.at(spellObstacle->casterSide).color, hero, spellObstacle);
|
||||
auto caster = spells::ObstacleCasterProxy(gs->curB->sides.at(spellObstacle->casterSide).color, hero, *spellObstacle);
|
||||
const auto * sp = SpellID(spellObstacle->ID).toSpell();
|
||||
if(sp)
|
||||
{
|
||||
@ -5279,27 +5276,6 @@ bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackI
|
||||
}
|
||||
else if(shouldReveal)
|
||||
revealObstacles(*spellObstacle);
|
||||
}
|
||||
}
|
||||
else if(obstacle->obstacleType == CObstacleInstance::MOAT)
|
||||
{
|
||||
auto town = gs->curB->town;
|
||||
int damage = (town == nullptr) ? 0 : town->town->moatDamage;
|
||||
if(!containDamageFromMoat)
|
||||
{
|
||||
containDamageFromMoat = true;
|
||||
|
||||
BattleStackAttacked bsa;
|
||||
bsa.damageAmount = damage;
|
||||
bsa.stackAttacked = curStack->ID;
|
||||
bsa.attackerID = -1;
|
||||
curStack->prepareAttacked(bsa, getRandomGenerator());
|
||||
|
||||
StacksInjured si;
|
||||
si.stacks.push_back(bsa);
|
||||
sendAndApply(&si);
|
||||
sendGenericKilledLog(curStack, bsa.killedAmount, false);
|
||||
}
|
||||
}
|
||||
|
||||
if(!curStack->alive())
|
||||
@ -6347,6 +6323,17 @@ void CGameHandler::runBattle()
|
||||
assert(gs->curB);
|
||||
//TODO: pre-tactic stuff, call scripts etc.
|
||||
|
||||
//Moat should be initialized here, because only here we can use spellcasting
|
||||
if (gs->curB->town && gs->curB->town->fortLevel() >= CGTownInstance::CITADEL)
|
||||
{
|
||||
const auto * h = gs->curB->battleGetFightingHero(BattleSide::DEFENDER);
|
||||
const auto * actualCaster = h ? static_cast<const spells::Caster*>(h) : nullptr;
|
||||
auto moatCaster = spells::SilentCaster(gs->curB->getSidePlayer(BattleSide::DEFENDER), actualCaster);
|
||||
auto cast = spells::BattleCast(gs->curB, &moatCaster, spells::Mode::PASSIVE, gs->curB->town->town->moatAbility.toSpell());
|
||||
auto target = spells::Target();
|
||||
cast.cast(spellEnv, target);
|
||||
}
|
||||
|
||||
//tactic round
|
||||
{
|
||||
while (gs->curB->tacticDistance && !battleResult.get())
|
||||
|
Loading…
x
Reference in New Issue
Block a user