mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-17 01:32:21 +02:00
Merge pull request #1822 from rilian-la-te/spell-mechanics-v3
Bonus refactoring, part3 (save-incompatible)
This commit is contained in:
@ -201,8 +201,6 @@
|
|||||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}%几率造成双倍基础伤害",
|
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}%几率造成双倍基础伤害",
|
||||||
"core.bonus.DRAGON_NATURE.name": "龙",
|
"core.bonus.DRAGON_NATURE.name": "龙",
|
||||||
"core.bonus.DRAGON_NATURE.description": "生物拥有龙的特性",
|
"core.bonus.DRAGON_NATURE.description": "生物拥有龙的特性",
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "魔法直伤免疫",
|
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "免疫直接造成伤害的魔法",
|
|
||||||
"core.bonus.EARTH_IMMUNITY.name": "土系免疫",
|
"core.bonus.EARTH_IMMUNITY.name": "土系免疫",
|
||||||
"core.bonus.EARTH_IMMUNITY.description": "免疫所有土系魔法",
|
"core.bonus.EARTH_IMMUNITY.description": "免疫所有土系魔法",
|
||||||
"core.bonus.ENCHANTER.name": "强化师",
|
"core.bonus.ENCHANTER.name": "强化师",
|
||||||
|
@ -201,8 +201,6 @@
|
|||||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "Has a ${val}% chance of dealing double base damage when attacking",
|
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "Has a ${val}% chance of dealing double base damage when attacking",
|
||||||
"core.bonus.DRAGON_NATURE.name": "Dragon",
|
"core.bonus.DRAGON_NATURE.name": "Dragon",
|
||||||
"core.bonus.DRAGON_NATURE.description": "Creature has a Dragon Nature",
|
"core.bonus.DRAGON_NATURE.description": "Creature has a Dragon Nature",
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Direct Damage Immunity",
|
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Immune to direct damage spells",
|
|
||||||
"core.bonus.EARTH_IMMUNITY.name": "Earth immunity",
|
"core.bonus.EARTH_IMMUNITY.name": "Earth immunity",
|
||||||
"core.bonus.EARTH_IMMUNITY.description": "Immune to all spells from the school of Earth magic",
|
"core.bonus.EARTH_IMMUNITY.description": "Immune to all spells from the school of Earth magic",
|
||||||
"core.bonus.ENCHANTER.name": "Enchanter",
|
"core.bonus.ENCHANTER.name": "Enchanter",
|
||||||
|
@ -196,8 +196,6 @@
|
|||||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% Chance auf doppelten Schaden",
|
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% Chance auf doppelten Schaden",
|
||||||
"core.bonus.DRAGON_NATURE.name": "Drache",
|
"core.bonus.DRAGON_NATURE.name": "Drache",
|
||||||
"core.bonus.DRAGON_NATURE.description": "Kreatur hat eine Drachennatur",
|
"core.bonus.DRAGON_NATURE.description": "Kreatur hat eine Drachennatur",
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Direkte Schadensimmunität",
|
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Immun gegen Direktschadenszauber",
|
|
||||||
"core.bonus.EARTH_IMMUNITY.name": "Erdimmunität",
|
"core.bonus.EARTH_IMMUNITY.name": "Erdimmunität",
|
||||||
"core.bonus.EARTH_IMMUNITY.description": "Immun gegen alle Zauber der Erdschule",
|
"core.bonus.EARTH_IMMUNITY.description": "Immun gegen alle Zauber der Erdschule",
|
||||||
"core.bonus.ENCHANTER.name": "Verzauberer",
|
"core.bonus.ENCHANTER.name": "Verzauberer",
|
||||||
|
@ -175,8 +175,6 @@
|
|||||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% szans na podwójne obrażenia",
|
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% szans na podwójne obrażenia",
|
||||||
"core.bonus.DRAGON_NATURE.name": "Smok",
|
"core.bonus.DRAGON_NATURE.name": "Smok",
|
||||||
"core.bonus.DRAGON_NATURE.description": "Stworzenie posiada smoczą naturę",
|
"core.bonus.DRAGON_NATURE.description": "Stworzenie posiada smoczą naturę",
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Odporność na bezpośrednie obrażenia",
|
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Odporny na czary zadające bezpośrednie obrażenia",
|
|
||||||
"core.bonus.EARTH_IMMUNITY.name": "Odporność na ziemię",
|
"core.bonus.EARTH_IMMUNITY.name": "Odporność na ziemię",
|
||||||
"core.bonus.EARTH_IMMUNITY.description": "Odporny na wszystkie czary szkoły ziemi",
|
"core.bonus.EARTH_IMMUNITY.description": "Odporny na wszystkie czary szkoły ziemi",
|
||||||
"core.bonus.ENCHANTER.name": "Czarodziej",
|
"core.bonus.ENCHANTER.name": "Czarodziej",
|
||||||
|
@ -199,8 +199,6 @@
|
|||||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "Шанс ${val}% на двойной урон",
|
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "Шанс ${val}% на двойной урон",
|
||||||
"core.bonus.DRAGON_NATURE.name": "Дракон",
|
"core.bonus.DRAGON_NATURE.name": "Дракон",
|
||||||
"core.bonus.DRAGON_NATURE.description": "Это существо - дракон",
|
"core.bonus.DRAGON_NATURE.description": "Это существо - дракон",
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Иммунитет к магии прямого урона",
|
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Заклинания прямого урона не могут быть применены",
|
|
||||||
"core.bonus.EARTH_IMMUNITY.name": "Иммунитет к земле",
|
"core.bonus.EARTH_IMMUNITY.name": "Иммунитет к земле",
|
||||||
"core.bonus.EARTH_IMMUNITY.description": "Иммунитет ко всем заклинаниям Магии Земли",
|
"core.bonus.EARTH_IMMUNITY.description": "Иммунитет ко всем заклинаниям Магии Земли",
|
||||||
"core.bonus.ENCHANTER.name": "Заклинатель (массовое)",
|
"core.bonus.ENCHANTER.name": "Заклинатель (массовое)",
|
||||||
|
@ -201,8 +201,6 @@
|
|||||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% de probabilidad de doble daño",
|
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% de probabilidad de doble daño",
|
||||||
"core.bonus.DRAGON_NATURE.name": "Dragón",
|
"core.bonus.DRAGON_NATURE.name": "Dragón",
|
||||||
"core.bonus.DRAGON_NATURE.description": "La criatura tiene la naturaleza de dragón",
|
"core.bonus.DRAGON_NATURE.description": "La criatura tiene la naturaleza de dragón",
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Inmunidad al Daño Directo",
|
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Inmune a hechizos de daño directo",
|
|
||||||
"core.bonus.EARTH_IMMUNITY.name": "Inmunidad a la Tierra",
|
"core.bonus.EARTH_IMMUNITY.name": "Inmunidad a la Tierra",
|
||||||
"core.bonus.EARTH_IMMUNITY.description": "Inmune a todos los hechizos de la escuela de tierra",
|
"core.bonus.EARTH_IMMUNITY.description": "Inmune a todos los hechizos de la escuela de tierra",
|
||||||
"core.bonus.ENCHANTER.name": "Encantador",
|
"core.bonus.ENCHANTER.name": "Encantador",
|
||||||
|
@ -175,8 +175,6 @@
|
|||||||
"core.bonus.DOUBLE_DAMAGE_CHANCE.description" : "${val}% шанс нанести подвійної шкоди",
|
"core.bonus.DOUBLE_DAMAGE_CHANCE.description" : "${val}% шанс нанести подвійної шкоди",
|
||||||
"core.bonus.DRAGON_NATURE.name" : "Дракон",
|
"core.bonus.DRAGON_NATURE.name" : "Дракон",
|
||||||
"core.bonus.DRAGON_NATURE.description" : "Істота має драконячу природу",
|
"core.bonus.DRAGON_NATURE.description" : "Істота має драконячу природу",
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name" : "Імунітет до прямої шкоди",
|
|
||||||
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description" : "Імунітет до заклять, що завдають прямої шкоди",
|
|
||||||
"core.bonus.EARTH_IMMUNITY.name" : "Імунітет Землі",
|
"core.bonus.EARTH_IMMUNITY.name" : "Імунітет Землі",
|
||||||
"core.bonus.EARTH_IMMUNITY.description" : "Імунітет до всіх заклять школи Землі",
|
"core.bonus.EARTH_IMMUNITY.description" : "Імунітет до всіх заклять школи Землі",
|
||||||
"core.bonus.ENCHANTER.name" : "Чарівник",
|
"core.bonus.ENCHANTER.name" : "Чарівник",
|
||||||
|
@ -387,7 +387,7 @@ void ClientCommandManager::handleBonusesCommand(std::istringstream & singleWordB
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
};
|
};
|
||||||
printCommandMessage("Bonuses of " + LOCPLINT->localState->getCurrentArmy()->getObjectName() + "\n");
|
printCommandMessage("Bonuses of " + LOCPLINT->localState->getCurrentArmy()->getObjectName() + "\n");
|
||||||
printCommandMessage(format(LOCPLINT->localState->getCurrentArmy()->getBonusList()) + "\n");
|
printCommandMessage(format(*LOCPLINT->localState->getCurrentArmy()->getAllBonuses(Selector::all, Selector::all)) + "\n");
|
||||||
|
|
||||||
printCommandMessage("\nInherited bonuses:\n");
|
printCommandMessage("\nInherited bonuses:\n");
|
||||||
TCNodes parents;
|
TCNodes parents;
|
||||||
|
@ -329,13 +329,12 @@ void ApplyClientNetPackVisitor::visitGiveBonus(GiveBonus & pack)
|
|||||||
case GiveBonus::ETarget::HERO:
|
case GiveBonus::ETarget::HERO:
|
||||||
{
|
{
|
||||||
const CGHeroInstance *h = gs.getHero(ObjectInstanceID(pack.id));
|
const CGHeroInstance *h = gs.getHero(ObjectInstanceID(pack.id));
|
||||||
callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroBonusChanged, h, *h->getBonusList().back(), true);
|
callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroBonusChanged, h, pack.bonus, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GiveBonus::ETarget::PLAYER:
|
case GiveBonus::ETarget::PLAYER:
|
||||||
{
|
{
|
||||||
const PlayerState *p = gs.getPlayerState(PlayerColor(pack.id));
|
callInterfaceIfPresent(cl, PlayerColor(pack.id), &IGameEventsReceiver::playerBonusChanged, pack.bonus, true);
|
||||||
callInterfaceIfPresent(cl, PlayerColor(pack.id), &IGameEventsReceiver::playerBonusChanged, *p->getBonusList().back(), true);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -82,11 +82,11 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
for(ui8 schoolId = 0; schoolId < 4; schoolId++)
|
for(auto schoolId = 0; schoolId < GameConstants::DEFAULT_SCHOOLS; schoolId++)
|
||||||
{
|
{
|
||||||
if(A->school.at((ESpellSchool)schoolId) && !B->school.at((ESpellSchool)schoolId))
|
if(A->school.at(SpellSchool(schoolId)) && !B->school.at(SpellSchool(schoolId)))
|
||||||
return true;
|
return true;
|
||||||
if(!A->school.at((ESpellSchool)schoolId) && B->school.at((ESpellSchool)schoolId))
|
if(!A->school.at(SpellSchool(schoolId)) && B->school.at(SpellSchool(schoolId)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,7 +320,7 @@ void CSpellWindow::computeSpellsPerArea()
|
|||||||
for(const CSpell * spell : mySpells)
|
for(const CSpell * spell : mySpells)
|
||||||
{
|
{
|
||||||
if(spell->isCombat() ^ !battleSpellsOnly
|
if(spell->isCombat() ^ !battleSpellsOnly
|
||||||
&& ((selectedTab == 4) || spell->school.at((ESpellSchool)selectedTab))
|
&& ((selectedTab == 4) || spell->school.at(SpellSchool(selectedTab)))
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
spellsCurSite.push_back(spell);
|
spellsCurSite.push_back(spell);
|
||||||
|
@ -1184,7 +1184,8 @@
|
|||||||
{
|
{
|
||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "AIR_SPELL_DMG_PREMY",
|
"type" : "SPELL_DAMAGE",
|
||||||
|
"subtype" : "spellSchool.air",
|
||||||
"val" : 50,
|
"val" : 50,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -1196,7 +1197,8 @@
|
|||||||
{
|
{
|
||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "EARTH_SPELL_DMG_PREMY",
|
"type" : "SPELL_DAMAGE",
|
||||||
|
"subtype" : "spellSchool.earth",
|
||||||
"val" : 50,
|
"val" : 50,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -1208,7 +1210,8 @@
|
|||||||
{
|
{
|
||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "FIRE_SPELL_DMG_PREMY",
|
"type" : "SPELL_DAMAGE",
|
||||||
|
"subtype" : "spellSchool.fire",
|
||||||
"val" : 50,
|
"val" : 50,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -1220,7 +1223,8 @@
|
|||||||
{
|
{
|
||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "WATER_SPELL_DMG_PREMY",
|
"type" : "SPELL_DAMAGE",
|
||||||
|
"subtype" : "spellSchool.water",
|
||||||
"val" : 50,
|
"val" : 50,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -1271,7 +1275,8 @@
|
|||||||
{
|
{
|
||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "FIRE_SPELLS",
|
"type" : "SPELLS_OF_SCHOOL",
|
||||||
|
"subtype" : 1,
|
||||||
"val" : 0,
|
"val" : 0,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -1283,7 +1288,8 @@
|
|||||||
{
|
{
|
||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "AIR_SPELLS",
|
"type" : "SPELLS_OF_SCHOOL",
|
||||||
|
"subtype" : 0,
|
||||||
"val" : 0,
|
"val" : 0,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -1295,7 +1301,8 @@
|
|||||||
{
|
{
|
||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "WATER_SPELLS",
|
"type" : "SPELLS_OF_SCHOOL",
|
||||||
|
"subtype" : 2,
|
||||||
"val" : 0,
|
"val" : 0,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -1307,7 +1314,8 @@
|
|||||||
{
|
{
|
||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "EARTH_SPELLS",
|
"type" : "SPELLS_OF_SCHOOL",
|
||||||
|
"subtype" : 3,
|
||||||
"val" : 0,
|
"val" : 0,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -2433,8 +2441,8 @@
|
|||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type" : "DIRECT_DAMAGE_IMMUNITY",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"val" : 0,
|
"val" : 100,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
"bonuses": [
|
"bonuses": [
|
||||||
{
|
{
|
||||||
"type" : "MAGIC_SCHOOL_SKILL",
|
"type" : "MAGIC_SCHOOL_SKILL",
|
||||||
"subtype" : 0,
|
"subtype" : "spellSchool.any",
|
||||||
"val" : 3,
|
"val" : 3,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -29,7 +29,7 @@
|
|||||||
"bonuses": [
|
"bonuses": [
|
||||||
{
|
{
|
||||||
"type" : "MAGIC_SCHOOL_SKILL",
|
"type" : "MAGIC_SCHOOL_SKILL",
|
||||||
"subtype" : 2,
|
"subtype" : "spellSchool.fire",
|
||||||
"val" : 3,
|
"val" : 3,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"bonuses": [
|
"bonuses": [
|
||||||
{
|
{
|
||||||
"type" : "MAGIC_SCHOOL_SKILL",
|
"type" : "MAGIC_SCHOOL_SKILL",
|
||||||
"subtype" : 8,
|
"subtype" : "spellSchool.earth",
|
||||||
"val" : 3,
|
"val" : 3,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@
|
|||||||
"bonuses": [
|
"bonuses": [
|
||||||
{
|
{
|
||||||
"type" : "MAGIC_SCHOOL_SKILL",
|
"type" : "MAGIC_SCHOOL_SKILL",
|
||||||
"subtype" : 1,
|
"subtype" : "spellSchool.air",
|
||||||
"val" : 3,
|
"val" : 3,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@
|
|||||||
"bonuses": [
|
"bonuses": [
|
||||||
{
|
{
|
||||||
"type" : "MAGIC_SCHOOL_SKILL",
|
"type" : "MAGIC_SCHOOL_SKILL",
|
||||||
"subtype" : 4,
|
"subtype" : "spellSchool.water",
|
||||||
"val" : 3,
|
"val" : 3,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
|
@ -116,14 +116,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"DIRECT_DAMAGE_IMMUNITY":
|
|
||||||
{
|
|
||||||
"graphics":
|
|
||||||
{
|
|
||||||
"icon": "zvs/Lib1.res/E_SPDIR"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"DISGUISED":
|
"DISGUISED":
|
||||||
{
|
{
|
||||||
"hidden": true
|
"hidden": true
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
"magicResistance" :
|
"magicResistance" :
|
||||||
{
|
{
|
||||||
"type" : "SPELL_DAMAGE_REDUCTION",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype" : -1,
|
"subtype" : "spellSchool.any",
|
||||||
"val" : 85
|
"val" : 85
|
||||||
},
|
},
|
||||||
"nonliving" :
|
"nonliving" :
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"magicResistance" :
|
"magicResistance" :
|
||||||
{
|
{
|
||||||
"type" : "SPELL_DAMAGE_REDUCTION",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype" : -1,
|
"subtype" : "spellSchool.any",
|
||||||
"val" : 95
|
"val" : 95
|
||||||
},
|
},
|
||||||
"nonliving" :
|
"nonliving" :
|
||||||
|
@ -105,7 +105,7 @@
|
|||||||
"magicResistance" :
|
"magicResistance" :
|
||||||
{
|
{
|
||||||
"type" : "SPELL_DAMAGE_REDUCTION",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype" : -1,
|
"subtype" : "spellSchool.any",
|
||||||
"val" : 50
|
"val" : 50
|
||||||
},
|
},
|
||||||
"nonliving" :
|
"nonliving" :
|
||||||
@ -137,7 +137,7 @@
|
|||||||
"magicResistance" :
|
"magicResistance" :
|
||||||
{
|
{
|
||||||
"type" : "SPELL_DAMAGE_REDUCTION",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype" : -1,
|
"subtype" : "spellSchool.any",
|
||||||
"val" : 75
|
"val" : 75
|
||||||
},
|
},
|
||||||
"nonliving" :
|
"nonliving" :
|
||||||
|
@ -215,6 +215,7 @@
|
|||||||
"spellDamage" :
|
"spellDamage" :
|
||||||
{
|
{
|
||||||
"type" : "SPELL_DAMAGE",
|
"type" : "SPELL_DAMAGE",
|
||||||
|
"subtype" : "spellSchool.any",
|
||||||
"val" : 100,
|
"val" : 100,
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
},
|
},
|
||||||
|
@ -186,6 +186,7 @@
|
|||||||
"sorcery" : {
|
"sorcery" : {
|
||||||
"targetSourceType" : "SECONDARY_SKILL",
|
"targetSourceType" : "SECONDARY_SKILL",
|
||||||
"type" : "SPELL_DAMAGE",
|
"type" : "SPELL_DAMAGE",
|
||||||
|
"subtype" : "spellSchool.any",
|
||||||
"updater" : "TIMES_HERO_LEVEL",
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
"val" : 5,
|
"val" : 5,
|
||||||
"valueType" : "PERCENT_TO_TARGET_TYPE"
|
"valueType" : "PERCENT_TO_TARGET_TYPE"
|
||||||
|
@ -262,6 +262,7 @@
|
|||||||
"sorcery" : {
|
"sorcery" : {
|
||||||
"targetSourceType" : "SECONDARY_SKILL",
|
"targetSourceType" : "SECONDARY_SKILL",
|
||||||
"type" : "SPELL_DAMAGE",
|
"type" : "SPELL_DAMAGE",
|
||||||
|
"subtype" : "spellSchool.any",
|
||||||
"updater" : "TIMES_HERO_LEVEL",
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
"val" : 5,
|
"val" : 5,
|
||||||
"valueType" : "PERCENT_TO_TARGET_TYPE"
|
"valueType" : "PERCENT_TO_TARGET_TYPE"
|
||||||
|
@ -261,6 +261,7 @@
|
|||||||
"sorcery" : {
|
"sorcery" : {
|
||||||
"targetSourceType" : "SECONDARY_SKILL",
|
"targetSourceType" : "SECONDARY_SKILL",
|
||||||
"type" : "SPELL_DAMAGE",
|
"type" : "SPELL_DAMAGE",
|
||||||
|
"subtype" : "spellSchool.any",
|
||||||
"updater" : "TIMES_HERO_LEVEL",
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
"val" : 5,
|
"val" : 5,
|
||||||
"valueType" : "PERCENT_TO_TARGET_TYPE"
|
"valueType" : "PERCENT_TO_TARGET_TYPE"
|
||||||
|
@ -192,6 +192,7 @@
|
|||||||
"sorcery" : {
|
"sorcery" : {
|
||||||
"targetSourceType" : "SECONDARY_SKILL",
|
"targetSourceType" : "SECONDARY_SKILL",
|
||||||
"type" : "SPELL_DAMAGE",
|
"type" : "SPELL_DAMAGE",
|
||||||
|
"subtype" : "spellSchool.any",
|
||||||
"updater" : "TIMES_HERO_LEVEL",
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
"val" : 5,
|
"val" : 5,
|
||||||
"valueType" : "PERCENT_TO_TARGET_TYPE"
|
"valueType" : "PERCENT_TO_TARGET_TYPE"
|
||||||
|
@ -135,6 +135,7 @@
|
|||||||
"sorcery" : {
|
"sorcery" : {
|
||||||
"targetSourceType" : "SECONDARY_SKILL",
|
"targetSourceType" : "SECONDARY_SKILL",
|
||||||
"type" : "SPELL_DAMAGE",
|
"type" : "SPELL_DAMAGE",
|
||||||
|
"subtype" : "spellSchool.any",
|
||||||
"updater" : "TIMES_HERO_LEVEL",
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
"val" : 5,
|
"val" : 5,
|
||||||
"valueType" : "PERCENT_TO_TARGET_TYPE"
|
"valueType" : "PERCENT_TO_TARGET_TYPE"
|
||||||
@ -239,6 +240,7 @@
|
|||||||
"sorcery" : {
|
"sorcery" : {
|
||||||
"targetSourceType" : "SECONDARY_SKILL",
|
"targetSourceType" : "SECONDARY_SKILL",
|
||||||
"type" : "SPELL_DAMAGE",
|
"type" : "SPELL_DAMAGE",
|
||||||
|
"subtype" : "spellSchool.any",
|
||||||
"updater" : "TIMES_HERO_LEVEL",
|
"updater" : "TIMES_HERO_LEVEL",
|
||||||
"val" : 5,
|
"val" : 5,
|
||||||
"valueType" : "PERCENT_TO_TARGET_TYPE"
|
"valueType" : "PERCENT_TO_TARGET_TYPE"
|
||||||
|
@ -406,7 +406,7 @@
|
|||||||
"base" : {
|
"base" : {
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"main" : {
|
"main" : {
|
||||||
"subtype" : 2,
|
"subtype" : "spellSchool.fire",
|
||||||
"type" : "MAGIC_SCHOOL_SKILL",
|
"type" : "MAGIC_SCHOOL_SKILL",
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -434,7 +434,7 @@
|
|||||||
"base" : {
|
"base" : {
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"main" : {
|
"main" : {
|
||||||
"subtype" : 1,
|
"subtype" : "spellSchool.air",
|
||||||
"type" : "MAGIC_SCHOOL_SKILL",
|
"type" : "MAGIC_SCHOOL_SKILL",
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -462,7 +462,7 @@
|
|||||||
"base" : {
|
"base" : {
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"main" : {
|
"main" : {
|
||||||
"subtype" : 4,
|
"subtype" : "spellSchool.water",
|
||||||
"type" : "MAGIC_SCHOOL_SKILL",
|
"type" : "MAGIC_SCHOOL_SKILL",
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -490,7 +490,7 @@
|
|||||||
"base" : {
|
"base" : {
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"main" : {
|
"main" : {
|
||||||
"subtype" : 8,
|
"subtype" : "spellSchool.earth",
|
||||||
"type" : "MAGIC_SCHOOL_SKILL",
|
"type" : "MAGIC_SCHOOL_SKILL",
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
@ -737,6 +737,7 @@
|
|||||||
"effects" : {
|
"effects" : {
|
||||||
"main" : {
|
"main" : {
|
||||||
"type" : "SPELL_DAMAGE",
|
"type" : "SPELL_DAMAGE",
|
||||||
|
"subtype" : "spellSchool.any",
|
||||||
"valueType" : "BASE_NUMBER"
|
"valueType" : "BASE_NUMBER"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -425,9 +425,6 @@
|
|||||||
"indifferent": true
|
"indifferent": true
|
||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,6 @@
|
|||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"iceBolt" : {
|
"iceBolt" : {
|
||||||
@ -66,9 +63,6 @@
|
|||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lightningBolt" : {
|
"lightningBolt" : {
|
||||||
@ -95,9 +89,6 @@
|
|||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"implosion" : {
|
"implosion" : {
|
||||||
@ -125,7 +116,6 @@
|
|||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
"noneOf" : {
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal",
|
|
||||||
"bonus.SIEGE_WEAPON" : "absolute"
|
"bonus.SIEGE_WEAPON" : "absolute"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,7 +187,6 @@
|
|||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
"noneOf" : {
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -226,7 +215,6 @@
|
|||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
"noneOf" : {
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -255,7 +243,6 @@
|
|||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
"noneOf" : {
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -284,7 +271,6 @@
|
|||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
"noneOf" : {
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -313,7 +299,6 @@
|
|||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
"noneOf" : {
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal",
|
|
||||||
"bonus.SIEGE_WEAPON" : "absolute",
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
"bonus.UNDEAD" : "absolute"
|
"bonus.UNDEAD" : "absolute"
|
||||||
}
|
}
|
||||||
@ -345,9 +330,6 @@
|
|||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"allOf" : {
|
"allOf" : {
|
||||||
"bonus.UNDEAD" : "absolute"
|
"bonus.UNDEAD" : "absolute"
|
||||||
},
|
|
||||||
"noneOf" : {
|
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -375,9 +357,6 @@
|
|||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"titanBolt" : {
|
"titanBolt" : {
|
||||||
|
@ -105,9 +105,6 @@
|
|||||||
"special": true
|
"special": true
|
||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"landMine" : {
|
"landMine" : {
|
||||||
@ -163,9 +160,6 @@
|
|||||||
"indifferent": true
|
"indifferent": true
|
||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"forceField" : {
|
"forceField" : {
|
||||||
@ -287,9 +281,6 @@
|
|||||||
"special": true
|
"special": true
|
||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fireWall" : {
|
"fireWall" : {
|
||||||
@ -358,9 +349,6 @@
|
|||||||
"indifferent": true
|
"indifferent": true
|
||||||
},
|
},
|
||||||
"targetCondition" : {
|
"targetCondition" : {
|
||||||
"noneOf" : {
|
|
||||||
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"earthquake" : {
|
"earthquake" : {
|
||||||
|
@ -104,7 +104,7 @@
|
|||||||
"effects" : {
|
"effects" : {
|
||||||
"spellDamageReduction" : {
|
"spellDamageReduction" : {
|
||||||
"type" : "SPELL_DAMAGE_REDUCTION",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype" : 0,
|
"subtype" : "spellSchool.air",
|
||||||
"duration" : "N_TURNS",
|
"duration" : "N_TURNS",
|
||||||
"val" : 30
|
"val" : 30
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@
|
|||||||
"effects" : {
|
"effects" : {
|
||||||
"spellDamageReduction" : {
|
"spellDamageReduction" : {
|
||||||
"type" : "SPELL_DAMAGE_REDUCTION",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype" : 1,
|
"subtype" : "spellSchool.fire",
|
||||||
"duration" : "N_TURNS",
|
"duration" : "N_TURNS",
|
||||||
"val" : 30
|
"val" : 30
|
||||||
}
|
}
|
||||||
@ -190,7 +190,7 @@
|
|||||||
"effects" : {
|
"effects" : {
|
||||||
"spellDamageReduction" : {
|
"spellDamageReduction" : {
|
||||||
"type" : "SPELL_DAMAGE_REDUCTION",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype" : 2,
|
"subtype" : "spellSchool.water",
|
||||||
"duration" : "N_TURNS",
|
"duration" : "N_TURNS",
|
||||||
"val" : 30
|
"val" : 30
|
||||||
}
|
}
|
||||||
@ -233,7 +233,7 @@
|
|||||||
"effects" : {
|
"effects" : {
|
||||||
"spellDamageReduction" : {
|
"spellDamageReduction" : {
|
||||||
"type" : "SPELL_DAMAGE_REDUCTION",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype" : 3,
|
"subtype" : "spellSchool.earth",
|
||||||
"duration" : "N_TURNS",
|
"duration" : "N_TURNS",
|
||||||
"val" : 30
|
"val" : 30
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "VCMI_Lib.h"
|
#include "VCMI_Lib.h"
|
||||||
#include "GameConstants.h"
|
#include "GameConstants.h"
|
||||||
#include "GameSettings.h"
|
#include "GameSettings.h"
|
||||||
|
#include "JsonNode.h"
|
||||||
#include "bonuses/BonusList.h"
|
#include "bonuses/BonusList.h"
|
||||||
#include "bonuses/Bonus.h"
|
#include "bonuses/Bonus.h"
|
||||||
#include "bonuses/IBonusBearer.h"
|
#include "bonuses/IBonusBearer.h"
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include <vcmi/Entity.h>
|
#include <vcmi/Entity.h>
|
||||||
#include "BattleFieldHandler.h"
|
#include "BattleFieldHandler.h"
|
||||||
|
#include "JsonNode.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
@ -1059,7 +1059,7 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
|
|||||||
b.type = BonusType::FEAR; break;
|
b.type = BonusType::FEAR; break;
|
||||||
case 'g':
|
case 'g':
|
||||||
b.type = BonusType::SPELL_DAMAGE_REDUCTION;
|
b.type = BonusType::SPELL_DAMAGE_REDUCTION;
|
||||||
b.subtype = -1; //all magic schools
|
b.subtype = SpellSchool(ESpellSchool::ANY);
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
b.type = BonusType::CASTS; break;
|
b.type = BonusType::CASTS; break;
|
||||||
@ -1178,8 +1178,9 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
|
|||||||
b.subtype = 1; //not positive
|
b.subtype = 1; //not positive
|
||||||
break;
|
break;
|
||||||
case 'O':
|
case 'O':
|
||||||
b.type = BonusType::FIRE_IMMUNITY;
|
b.type = BonusType::SPELL_DAMAGE_REDUCTION;
|
||||||
b.subtype = 2; //only direct damage
|
b.subtype = SpellSchool(ESpellSchool::FIRE);
|
||||||
|
b.val = 100; //Full damage immunity
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
b.type = BonusType::FIRE_IMMUNITY;
|
b.type = BonusType::FIRE_IMMUNITY;
|
||||||
@ -1190,31 +1191,36 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigPars
|
|||||||
b.subtype = 1; //not positive
|
b.subtype = 1; //not positive
|
||||||
break;
|
break;
|
||||||
case 'W':
|
case 'W':
|
||||||
b.type = BonusType::WATER_IMMUNITY;
|
b.type = BonusType::SPELL_DAMAGE_REDUCTION;
|
||||||
b.subtype = 2; //only direct damage
|
b.subtype = SpellSchool(ESpellSchool::WATER);
|
||||||
|
b.val = 100; //Full damage immunity
|
||||||
break;
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
b.type = BonusType::WATER_IMMUNITY;
|
b.type = BonusType::WATER_IMMUNITY;
|
||||||
b.subtype = 0; //all
|
b.subtype = 0; //all
|
||||||
break;
|
break;
|
||||||
case 'E':
|
case 'E':
|
||||||
b.type = BonusType::EARTH_IMMUNITY;
|
b.type = BonusType::SPELL_DAMAGE_REDUCTION;
|
||||||
b.subtype = 2; //only direct damage
|
b.subtype = SpellSchool(ESpellSchool::EARTH);
|
||||||
|
b.val = 100; //Full damage immunity
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
b.type = BonusType::EARTH_IMMUNITY;
|
b.type = BonusType::EARTH_IMMUNITY;
|
||||||
b.subtype = 0; //all
|
b.subtype = 0; //all
|
||||||
break;
|
break;
|
||||||
case 'A':
|
case 'A':
|
||||||
b.type = BonusType::AIR_IMMUNITY;
|
b.type = BonusType::SPELL_DAMAGE_REDUCTION;
|
||||||
b.subtype = 2; //only direct damage
|
b.subtype = SpellSchool(ESpellSchool::AIR);
|
||||||
|
b.val = 100; //Full damage immunity
|
||||||
break;
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
b.type = BonusType::AIR_IMMUNITY;
|
b.type = BonusType::AIR_IMMUNITY;
|
||||||
b.subtype = 0; //all
|
b.subtype = 0; //all
|
||||||
break;
|
break;
|
||||||
case 'D':
|
case 'D':
|
||||||
b.type = BonusType::DIRECT_DAMAGE_IMMUNITY;
|
b.type = BonusType::SPELL_DAMAGE_REDUCTION;
|
||||||
|
b.subtype = SpellSchool(ESpellSchool::ANY);
|
||||||
|
b.val = 100; //Full damage immunity
|
||||||
break;
|
break;
|
||||||
case '0':
|
case '0':
|
||||||
b.type = BonusType::RECEPTIVE;
|
b.type = BonusType::RECEPTIVE;
|
||||||
|
@ -703,8 +703,6 @@ CGameState::CGameState()
|
|||||||
applier = std::make_shared<CApplier<CBaseForGSApply>>();
|
applier = std::make_shared<CApplier<CBaseForGSApply>>();
|
||||||
registerTypesClientPacks1(*applier);
|
registerTypesClientPacks1(*applier);
|
||||||
registerTypesClientPacks2(*applier);
|
registerTypesClientPacks2(*applier);
|
||||||
//objCaller = new CObjectCallersHandler();
|
|
||||||
globalEffects.setDescription("Global effects");
|
|
||||||
globalEffects.setNodeType(CBonusSystemNode::GLOBAL_EFFECTS);
|
globalEffects.setNodeType(CBonusSystemNode::GLOBAL_EFFECTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,9 +248,6 @@ class DLL_LINKAGE CHeroHandler : public CHandlerBase<HeroTypeID, HeroType, CHero
|
|||||||
public:
|
public:
|
||||||
CHeroClassHandler classes;
|
CHeroClassHandler classes;
|
||||||
|
|
||||||
//default costs of going through terrains. -1 means terrain is impassable
|
|
||||||
std::map<TerrainId, int> terrCosts;
|
|
||||||
|
|
||||||
ui32 level(ui64 experience) const; //calculates level corresponding to given experience amount
|
ui32 level(ui64 experience) const; //calculates level corresponding to given experience amount
|
||||||
ui64 reqExp(ui32 level) const; //calculates experience required for given level
|
ui64 reqExp(ui32 level) const; //calculates experience required for given level
|
||||||
|
|
||||||
@ -271,7 +268,6 @@ public:
|
|||||||
h & classes;
|
h & classes;
|
||||||
h & objects;
|
h & objects;
|
||||||
h & expPerLevel;
|
h & expPerLevel;
|
||||||
h & terrCosts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -745,6 +745,12 @@ void CModInfo::setEnabled(bool on)
|
|||||||
|
|
||||||
CModHandler::CModHandler() : content(std::make_shared<CContentHandler>())
|
CModHandler::CModHandler() : content(std::make_shared<CContentHandler>())
|
||||||
{
|
{
|
||||||
|
//TODO: moddable spell schools
|
||||||
|
for (auto i = 0; i < GameConstants::DEFAULT_SCHOOLS; ++i)
|
||||||
|
identifiers.registerObject(CModHandler::scopeBuiltin(), "spellSchool", SpellConfig::SCHOOL[i].jsonName, SpellConfig::SCHOOL[i].id);
|
||||||
|
|
||||||
|
identifiers.registerObject(CModHandler::scopeBuiltin(), "spellSchool", "any", SpellSchool(ESpellSchool::ANY));
|
||||||
|
|
||||||
for (int i = 0; i < GameConstants::RESOURCE_QUANTITY; ++i)
|
for (int i = 0; i < GameConstants::RESOURCE_QUANTITY; ++i)
|
||||||
{
|
{
|
||||||
identifiers.registerObject(CModHandler::scopeBuiltin(), "resource", GameConstants::RESOURCE_NAMES[i], i);
|
identifiers.registerObject(CModHandler::scopeBuiltin(), "resource", GameConstants::RESOURCE_NAMES[i], i);
|
||||||
|
@ -50,6 +50,7 @@ namespace GameConstants
|
|||||||
constexpr int CREATURES_PER_TOWN = 7; //without upgrades
|
constexpr int CREATURES_PER_TOWN = 7; //without upgrades
|
||||||
constexpr int SPELL_LEVELS = 5;
|
constexpr int SPELL_LEVELS = 5;
|
||||||
constexpr int SPELL_SCHOOL_LEVELS = 4;
|
constexpr int SPELL_SCHOOL_LEVELS = 4;
|
||||||
|
constexpr int DEFAULT_SCHOOLS = 4;
|
||||||
constexpr int CRE_LEVELS = 10; // number of creature experience levels
|
constexpr int CRE_LEVELS = 10; // number of creature experience levels
|
||||||
|
|
||||||
constexpr int HERO_GOLD_COST = 2500;
|
constexpr int HERO_GOLD_COST = 2500;
|
||||||
@ -1324,14 +1325,17 @@ class Obstacle : public BaseForID<Obstacle, si32>
|
|||||||
DLL_LINKAGE static Obstacle fromString(const std::string & identifier);
|
DLL_LINKAGE static Obstacle fromString(const std::string & identifier);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ESpellSchool: ui8
|
enum class ESpellSchool: int8_t
|
||||||
{
|
{
|
||||||
|
ANY = -1,
|
||||||
AIR = 0,
|
AIR = 0,
|
||||||
FIRE = 1,
|
FIRE = 1,
|
||||||
WATER = 2,
|
WATER = 2,
|
||||||
EARTH = 3
|
EARTH = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using SpellSchool = Identifier<ESpellSchool>;
|
||||||
|
|
||||||
enum class EMetaclass: ui8
|
enum class EMetaclass: ui8
|
||||||
{
|
{
|
||||||
INVALID = 0,
|
INVALID = 0,
|
||||||
|
@ -842,25 +842,12 @@ static BonusParams convertDeprecatedBonus(const JsonNode &ability)
|
|||||||
ability["subtype"].isNumber() ? ability["subtype"].Integer() : -1);
|
ability["subtype"].isNumber() ? ability["subtype"].Integer() : -1);
|
||||||
if(params.isConverted)
|
if(params.isConverted)
|
||||||
{
|
{
|
||||||
if(!params.valRelevant) {
|
if(ability["type"].String() == "SECONDARY_SKILL_PREMY" && bonusValueMap.find(ability["valueType"].String())->second == BonusValueType::PERCENT_TO_BASE) //assume secondary skill special
|
||||||
params.val = static_cast<si32>(ability["val"].Float());
|
|
||||||
params.valRelevant = true;
|
|
||||||
}
|
|
||||||
BonusValueType valueType = BonusValueType::ADDITIVE_VALUE;
|
|
||||||
if(!ability["valueType"].isNull())
|
|
||||||
valueType = bonusValueMap.find(ability["valueType"].String())->second;
|
|
||||||
|
|
||||||
if(ability["type"].String() == "SECONDARY_SKILL_PREMY" && valueType == BonusValueType::PERCENT_TO_BASE) //assume secondary skill special
|
|
||||||
{
|
{
|
||||||
params.valueType = BonusValueType::PERCENT_TO_TARGET_TYPE;
|
params.valueType = BonusValueType::PERCENT_TO_TARGET_TYPE;
|
||||||
params.targetType = BonusSource::SECONDARY_SKILL;
|
params.targetType = BonusSource::SECONDARY_SKILL;
|
||||||
params.targetTypeRelevant = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!params.valueTypeRelevant) {
|
|
||||||
params.valueType = valueType;
|
|
||||||
params.valueTypeRelevant = true;
|
|
||||||
}
|
|
||||||
logMod->warn("Please, use this bonus:\n%s\nConverted sucessfully!", params.toJson().toJson());
|
logMod->warn("Please, use this bonus:\n%s\nConverted sucessfully!", params.toJson().toJson());
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
@ -930,10 +917,10 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
b->type = params->type;
|
b->type = params->type;
|
||||||
b->val = params->val;
|
b->val = params->val.value_or(0);
|
||||||
b->valType = params->valueType;
|
b->valType = params->valueType.value_or(BonusValueType::ADDITIVE_VALUE);
|
||||||
if(params->targetTypeRelevant)
|
if(params->targetType)
|
||||||
b->targetSourceType = params->targetType;
|
b->targetSourceType = params->targetType.value();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
b->type = it->second;
|
b->type = it->second;
|
||||||
@ -975,7 +962,15 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
|||||||
switch (value->getType())
|
switch (value->getType())
|
||||||
{
|
{
|
||||||
case JsonNode::JsonType::DATA_STRING:
|
case JsonNode::JsonType::DATA_STRING:
|
||||||
b->duration = static_cast<BonusDuration>(parseByMap(bonusDurationMap, value, "duration type "));
|
b->duration = parseByMap(bonusDurationMap, value, "duration type ");
|
||||||
|
break;
|
||||||
|
case JsonNode::JsonType::DATA_VECTOR:
|
||||||
|
{
|
||||||
|
BonusDuration::Type dur = 0;
|
||||||
|
for (const JsonNode & d : value->Vector())
|
||||||
|
dur |= parseByMapN(bonusDurationMap, &d, "duration type ");
|
||||||
|
b->duration = dur;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logMod->error("Error! Wrong bonus duration format.");
|
logMod->error("Error! Wrong bonus duration format.");
|
||||||
@ -1065,31 +1060,26 @@ CSelector JsonUtils::parseSelector(const JsonNode & ability)
|
|||||||
ret = ret.And(Selector::subtype()(subtype));
|
ret = ret.And(Selector::subtype()(subtype));
|
||||||
}
|
}
|
||||||
value = &ability["sourceType"];
|
value = &ability["sourceType"];
|
||||||
BonusSource src = BonusSource::OTHER; //Fixes for GCC false maybe-uninitialized
|
std::optional<BonusSource> src = std::nullopt; //Fixes for GCC false maybe-uninitialized
|
||||||
si32 id = 0;
|
std::optional<si32> id = std::nullopt;
|
||||||
auto sourceIDRelevant = false;
|
|
||||||
auto sourceTypeRelevant = false;
|
|
||||||
if(value->isString())
|
if(value->isString())
|
||||||
{
|
{
|
||||||
auto it = bonusSourceMap.find(value->String());
|
auto it = bonusSourceMap.find(value->String());
|
||||||
if(it != bonusSourceMap.end())
|
if(it != bonusSourceMap.end())
|
||||||
{
|
|
||||||
src = it->second;
|
src = it->second;
|
||||||
sourceTypeRelevant = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
value = &ability["sourceID"];
|
value = &ability["sourceID"];
|
||||||
if(!value->isNull())
|
if(!value->isNull())
|
||||||
{
|
{
|
||||||
sourceIDRelevant = true;
|
id = -1;
|
||||||
resolveIdentifier(id, ability, "sourceID");
|
resolveIdentifier(*id, ability, "sourceID");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sourceIDRelevant && sourceTypeRelevant)
|
if(src && id)
|
||||||
ret = ret.And(Selector::source(src, id));
|
ret = ret.And(Selector::source(*src, *id));
|
||||||
else if(sourceTypeRelevant)
|
else if(src)
|
||||||
ret = ret.And(Selector::sourceTypeSel(src));
|
ret = ret.And(Selector::sourceTypeSel(*src));
|
||||||
|
|
||||||
|
|
||||||
value = &ability["targetSourceType"];
|
value = &ability["targetSourceType"];
|
||||||
|
@ -31,26 +31,6 @@
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
const std::set<std::string> deprecatedBonusSet = {
|
|
||||||
"SECONDARY_SKILL_PREMY",
|
|
||||||
"SECONDARY_SKILL_VAL2",
|
|
||||||
"MAXED_SPELL",
|
|
||||||
"LAND_MOVEMENT",
|
|
||||||
"SEA_MOVEMENT",
|
|
||||||
"SIGHT_RADIOUS",
|
|
||||||
"NO_TYPE",
|
|
||||||
"SPECIAL_SECONDARY_SKILL",
|
|
||||||
"FULL_HP_REGENERATION",
|
|
||||||
"KING1",
|
|
||||||
"KING2",
|
|
||||||
"KING3",
|
|
||||||
"BLOCK_MORALE",
|
|
||||||
"BLOCK_LUCK",
|
|
||||||
"SELF_MORALE",
|
|
||||||
"SELF_LUCK",
|
|
||||||
"DIRECT_DAMAGE_IMMUNITY"
|
|
||||||
};
|
|
||||||
|
|
||||||
//This constructor should be placed here to avoid side effects
|
//This constructor should be placed here to avoid side effects
|
||||||
CAddInfo::CAddInfo() = default;
|
CAddInfo::CAddInfo() = default;
|
||||||
|
|
||||||
@ -154,7 +134,7 @@ std::string Bonus::Description(std::optional<si32> customValue) const
|
|||||||
return str.str();
|
return str.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode subtypeToJson(BonusType type, int subtype)
|
static JsonNode subtypeToJson(BonusType type, int subtype)
|
||||||
{
|
{
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
@ -177,7 +157,7 @@ JsonNode subtypeToJson(BonusType type, int subtype)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode additionalInfoToJson(BonusType type, CAddInfo addInfo)
|
static JsonNode additionalInfoToJson(BonusType type, CAddInfo addInfo)
|
||||||
{
|
{
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
@ -216,7 +196,7 @@ JsonNode Bonus::toJsonNode() const
|
|||||||
if(effectRange != BonusLimitEffect::NO_LIMIT)
|
if(effectRange != BonusLimitEffect::NO_LIMIT)
|
||||||
root["effectRange"].String() = vstd::findKey(bonusLimitEffect, effectRange);
|
root["effectRange"].String() = vstd::findKey(bonusLimitEffect, effectRange);
|
||||||
if(duration != BonusDuration::PERMANENT)
|
if(duration != BonusDuration::PERMANENT)
|
||||||
root["duration"].String() = vstd::findKey(bonusDurationMap, duration);
|
root["duration"] = BonusDuration::toJson(duration);
|
||||||
if(turnsRemain)
|
if(turnsRemain)
|
||||||
root["turns"].Integer() = turnsRemain;
|
root["turns"].Integer() = turnsRemain;
|
||||||
if(limiter)
|
if(limiter)
|
||||||
@ -228,7 +208,7 @@ JsonNode Bonus::toJsonNode() const
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bonus::Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype):
|
Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype):
|
||||||
duration(Duration),
|
duration(Duration),
|
||||||
type(Type),
|
type(Type),
|
||||||
subtype(Subtype),
|
subtype(Subtype),
|
||||||
@ -241,7 +221,7 @@ Bonus::Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val,
|
|||||||
targetSourceType = BonusSource::OTHER;
|
targetSourceType = BonusSource::OTHER;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bonus::Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, BonusValueType ValType):
|
Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, BonusValueType ValType):
|
||||||
duration(Duration),
|
duration(Duration),
|
||||||
type(Type),
|
type(Type),
|
||||||
subtype(Subtype),
|
subtype(Subtype),
|
||||||
@ -270,7 +250,7 @@ DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus)
|
|||||||
#define printField(field) out << "\t" #field ": " << (int)bonus.field << "\n"
|
#define printField(field) out << "\t" #field ": " << (int)bonus.field << "\n"
|
||||||
printField(val);
|
printField(val);
|
||||||
printField(subtype);
|
printField(subtype);
|
||||||
printField(duration);
|
printField(duration.to_ulong());
|
||||||
printField(source);
|
printField(source);
|
||||||
printField(sid);
|
printField(sid);
|
||||||
if(bonus.additionalInfo != CAddInfo::NONE)
|
if(bonus.additionalInfo != CAddInfo::NONE)
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "BonusEnum.h"
|
#include "BonusEnum.h"
|
||||||
#include "../JsonNode.h"
|
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -21,6 +20,7 @@ class ILimiter;
|
|||||||
class IPropagator;
|
class IPropagator;
|
||||||
class IUpdater;
|
class IUpdater;
|
||||||
class BonusList;
|
class BonusList;
|
||||||
|
class CSelector;
|
||||||
|
|
||||||
using TBonusSubtype = int32_t;
|
using TBonusSubtype = int32_t;
|
||||||
using TBonusListPtr = std::shared_ptr<BonusList>;
|
using TBonusListPtr = std::shared_ptr<BonusList>;
|
||||||
@ -52,7 +52,7 @@ public:
|
|||||||
/// Struct for handling bonuses of several types. Can be transferred to any hero
|
/// Struct for handling bonuses of several types. Can be transferred to any hero
|
||||||
struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
||||||
{
|
{
|
||||||
BonusDuration duration = BonusDuration::PERMANENT; //uses BonusDuration values
|
BonusDuration::Type duration = BonusDuration::PERMANENT; //uses BonusDuration values
|
||||||
si16 turnsRemain = 0; //used if duration is N_TURNS, N_DAYS or ONE_WEEK
|
si16 turnsRemain = 0; //used if duration is N_TURNS, N_DAYS or ONE_WEEK
|
||||||
|
|
||||||
BonusType type = BonusType::NONE; //uses BonusType values - says to what is this bonus - 1 byte
|
BonusType type = BonusType::NONE; //uses BonusType values - says to what is this bonus - 1 byte
|
||||||
@ -75,8 +75,8 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
|||||||
|
|
||||||
std::string description;
|
std::string description;
|
||||||
|
|
||||||
Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype=-1);
|
Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype=-1);
|
||||||
Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype=-1, BonusValueType ValType = BonusValueType::ADDITIVE_VALUE);
|
Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype=-1, BonusValueType ValType = BonusValueType::ADDITIVE_VALUE);
|
||||||
Bonus() = default;
|
Bonus() = default;
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
@ -107,43 +107,53 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
|||||||
}
|
}
|
||||||
static bool NDays(const Bonus *hb)
|
static bool NDays(const Bonus *hb)
|
||||||
{
|
{
|
||||||
return hb->duration == BonusDuration::N_DAYS;
|
auto set = hb->duration & BonusDuration::N_DAYS;
|
||||||
|
return set.any();
|
||||||
}
|
}
|
||||||
static bool NTurns(const Bonus *hb)
|
static bool NTurns(const Bonus *hb)
|
||||||
{
|
{
|
||||||
return hb->duration == BonusDuration::N_TURNS;
|
auto set = hb->duration & BonusDuration::N_TURNS;
|
||||||
|
return set.any();
|
||||||
}
|
}
|
||||||
static bool OneDay(const Bonus *hb)
|
static bool OneDay(const Bonus *hb)
|
||||||
{
|
{
|
||||||
return hb->duration == BonusDuration::ONE_DAY;
|
auto set = hb->duration & BonusDuration::ONE_DAY;
|
||||||
|
return set.any();
|
||||||
}
|
}
|
||||||
static bool OneWeek(const Bonus *hb)
|
static bool OneWeek(const Bonus *hb)
|
||||||
{
|
{
|
||||||
return hb->duration == BonusDuration::ONE_WEEK;
|
auto set = hb->duration & BonusDuration::ONE_WEEK;
|
||||||
|
return set.any();
|
||||||
}
|
}
|
||||||
static bool OneBattle(const Bonus *hb)
|
static bool OneBattle(const Bonus *hb)
|
||||||
{
|
{
|
||||||
return hb->duration == BonusDuration::ONE_BATTLE;
|
auto set = hb->duration & BonusDuration::ONE_BATTLE;
|
||||||
|
return set.any();
|
||||||
}
|
}
|
||||||
static bool Permanent(const Bonus *hb)
|
static bool Permanent(const Bonus *hb)
|
||||||
{
|
{
|
||||||
return hb->duration == BonusDuration::PERMANENT;
|
auto set = hb->duration & BonusDuration::PERMANENT;
|
||||||
|
return set.any();
|
||||||
}
|
}
|
||||||
static bool UntilGetsTurn(const Bonus *hb)
|
static bool UntilGetsTurn(const Bonus *hb)
|
||||||
{
|
{
|
||||||
return hb->duration == BonusDuration::STACK_GETS_TURN;
|
auto set = hb->duration & BonusDuration::STACK_GETS_TURN;
|
||||||
|
return set.any();
|
||||||
}
|
}
|
||||||
static bool UntilAttack(const Bonus *hb)
|
static bool UntilAttack(const Bonus *hb)
|
||||||
{
|
{
|
||||||
return hb->duration == BonusDuration::UNTIL_ATTACK;
|
auto set = hb->duration & BonusDuration::UNTIL_ATTACK;
|
||||||
|
return set.any();
|
||||||
}
|
}
|
||||||
static bool UntilBeingAttacked(const Bonus *hb)
|
static bool UntilBeingAttacked(const Bonus *hb)
|
||||||
{
|
{
|
||||||
return hb->duration == BonusDuration::UNTIL_BEING_ATTACKED;
|
auto set = hb->duration & BonusDuration::UNTIL_BEING_ATTACKED;
|
||||||
|
return set.any();
|
||||||
}
|
}
|
||||||
static bool UntilCommanderKilled(const Bonus *hb)
|
static bool UntilCommanderKilled(const Bonus *hb)
|
||||||
{
|
{
|
||||||
return hb->duration == BonusDuration::COMMANDER_KILLED;
|
auto set = hb->duration & BonusDuration::COMMANDER_KILLED;
|
||||||
|
return set.any();
|
||||||
}
|
}
|
||||||
inline bool operator == (const BonusType & cf) const
|
inline bool operator == (const BonusType & cf) const
|
||||||
{
|
{
|
||||||
@ -178,6 +188,4 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
|||||||
|
|
||||||
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus);
|
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus);
|
||||||
|
|
||||||
extern DLL_LINKAGE const std::set<std::string> deprecatedBonusSet;
|
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
#include "BonusEnum.h"
|
#include "BonusEnum.h"
|
||||||
|
|
||||||
|
#include "../JsonNode.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
#define BONUS_NAME(x) { #x, BonusType::x },
|
#define BONUS_NAME(x) { #x, BonusType::x },
|
||||||
@ -30,7 +32,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
#undef BONUS_SOURCE
|
#undef BONUS_SOURCE
|
||||||
|
|
||||||
#define BONUS_ITEM(x) { #x, BonusDuration::x },
|
#define BONUS_ITEM(x) { #x, BonusDuration::x },
|
||||||
const std::map<std::string, BonusDuration> bonusDurationMap =
|
const std::map<std::string, BonusDuration::Type> bonusDurationMap =
|
||||||
{
|
{
|
||||||
BONUS_ITEM(PERMANENT)
|
BONUS_ITEM(PERMANENT)
|
||||||
BONUS_ITEM(ONE_BATTLE)
|
BONUS_ITEM(ONE_BATTLE)
|
||||||
@ -55,4 +57,28 @@ const std::map<std::string, BonusLimitEffect> bonusLimitEffect =
|
|||||||
};
|
};
|
||||||
#undef BONUS_ITEM
|
#undef BONUS_ITEM
|
||||||
|
|
||||||
|
namespace BonusDuration
|
||||||
|
{
|
||||||
|
JsonNode toJson(const Type & duration)
|
||||||
|
{
|
||||||
|
std::vector<std::string> durationNames;
|
||||||
|
for(auto durBit = 0; durBit < duration.size(); durBit++)
|
||||||
|
{
|
||||||
|
if(duration[durBit])
|
||||||
|
durationNames.push_back(vstd::findKey(bonusDurationMap, duration & Type().set(durBit)));
|
||||||
|
}
|
||||||
|
if(durationNames.size() == 1)
|
||||||
|
{
|
||||||
|
return JsonUtils::stringNode(durationNames[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JsonNode node(JsonNode::JsonType::DATA_VECTOR);
|
||||||
|
for(const std::string & dur : durationNames)
|
||||||
|
node.Vector().push_back(JsonUtils::stringNode(dur));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class JsonNode;
|
||||||
|
|
||||||
#define BONUS_LIST \
|
#define BONUS_LIST \
|
||||||
BONUS_NAME(NONE) \
|
BONUS_NAME(NONE) \
|
||||||
BONUS_NAME(LEVEL_COUNTER) /* for commander artifacts*/ \
|
BONUS_NAME(LEVEL_COUNTER) /* for commander artifacts*/ \
|
||||||
@ -20,29 +22,22 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
BONUS_NAME(LUCK) \
|
BONUS_NAME(LUCK) \
|
||||||
BONUS_NAME(PRIMARY_SKILL) /*uses subtype to pick skill; additional info if set: 1 - only melee, 2 - only distance*/ \
|
BONUS_NAME(PRIMARY_SKILL) /*uses subtype to pick skill; additional info if set: 1 - only melee, 2 - only distance*/ \
|
||||||
BONUS_NAME(SIGHT_RADIUS) \
|
BONUS_NAME(SIGHT_RADIUS) \
|
||||||
BONUS_NAME(MANA_REGENERATION) /*points per turn apart from normal (1 + mysticism)*/ \
|
BONUS_NAME(MANA_REGENERATION) /*points per turn*/ \
|
||||||
BONUS_NAME(FULL_MANA_REGENERATION) /*all mana points are replenished every day*/ \
|
BONUS_NAME(FULL_MANA_REGENERATION) /*all mana points are replenished every day*/ \
|
||||||
BONUS_NAME(NONEVIL_ALIGNMENT_MIX) /*good and neutral creatures can be mixed without morale penalty*/ \
|
BONUS_NAME(NONEVIL_ALIGNMENT_MIX) /*good and neutral creatures can be mixed without morale penalty*/ \
|
||||||
BONUS_NAME(SURRENDER_DISCOUNT) /*%*/ \
|
BONUS_NAME(SURRENDER_DISCOUNT) /*%*/ \
|
||||||
BONUS_NAME(STACKS_SPEED) /*additional info - percent of speed bonus applied after direct bonuses; >0 - added, <0 - subtracted to this part*/ \
|
BONUS_NAME(STACKS_SPEED) /*additional info - percent of speed bonus applied after direct bonuses; >0 - added, <0 - subtracted to this part*/ \
|
||||||
BONUS_NAME(FLYING_MOVEMENT) /*value - penalty percentage*/ \
|
BONUS_NAME(FLYING_MOVEMENT) /*value - penalty percentage*/ \
|
||||||
BONUS_NAME(SPELL_DURATION) \
|
BONUS_NAME(SPELL_DURATION) \
|
||||||
BONUS_NAME(AIR_SPELL_DMG_PREMY) \
|
|
||||||
BONUS_NAME(EARTH_SPELL_DMG_PREMY) \
|
|
||||||
BONUS_NAME(FIRE_SPELL_DMG_PREMY) \
|
|
||||||
BONUS_NAME(WATER_SPELL_DMG_PREMY) \
|
|
||||||
BONUS_NAME(WATER_WALKING) /*value - penalty percentage*/ \
|
BONUS_NAME(WATER_WALKING) /*value - penalty percentage*/ \
|
||||||
BONUS_NAME(NEGATE_ALL_NATURAL_IMMUNITIES) \
|
BONUS_NAME(NEGATE_ALL_NATURAL_IMMUNITIES) \
|
||||||
BONUS_NAME(STACK_HEALTH) \
|
BONUS_NAME(STACK_HEALTH) \
|
||||||
BONUS_NAME(FIRE_SPELLS) \
|
|
||||||
BONUS_NAME(AIR_SPELLS) \
|
|
||||||
BONUS_NAME(WATER_SPELLS) \
|
|
||||||
BONUS_NAME(EARTH_SPELLS) \
|
|
||||||
BONUS_NAME(GENERATE_RESOURCE) /*daily value, uses subtype (resource type)*/ \
|
BONUS_NAME(GENERATE_RESOURCE) /*daily value, uses subtype (resource type)*/ \
|
||||||
BONUS_NAME(CREATURE_GROWTH) /*for legion artifacts: value - week growth bonus, subtype - monster level if aplicable*/ \
|
BONUS_NAME(CREATURE_GROWTH) /*for legion artifacts: value - week growth bonus, subtype - monster level if aplicable*/ \
|
||||||
BONUS_NAME(WHIRLPOOL_PROTECTION) /*hero won't lose army when teleporting through whirlpool*/ \
|
BONUS_NAME(WHIRLPOOL_PROTECTION) /*hero won't lose army when teleporting through whirlpool*/ \
|
||||||
BONUS_NAME(SPELL) /*hero knows spell, val - skill level (0 - 3), subtype - spell id*/ \
|
BONUS_NAME(SPELL) /*hero knows spell, val - skill level (0 - 3), subtype - spell id*/ \
|
||||||
BONUS_NAME(SPELLS_OF_LEVEL) /*hero knows all spells of given level, val - skill level; subtype - level*/ \
|
BONUS_NAME(SPELLS_OF_LEVEL) /*hero knows all spells of given level, val - skill level; subtype - level*/ \
|
||||||
|
BONUS_NAME(SPELLS_OF_SCHOOL) /*hero knows all spells of given school, subtype - spell school; 0 - air, 1 - fire, 2 - water, 3 - earth*/ \
|
||||||
BONUS_NAME(BATTLE_NO_FLEEING) /*for shackles of war*/ \
|
BONUS_NAME(BATTLE_NO_FLEEING) /*for shackles of war*/ \
|
||||||
BONUS_NAME(MAGIC_SCHOOL_SKILL) /* //eg. for magic plains terrain, subtype: school of magic (0 - all, 1 - fire, 2 - air, 4 - water, 8 - earth), value - level*/ \
|
BONUS_NAME(MAGIC_SCHOOL_SKILL) /* //eg. for magic plains terrain, subtype: school of magic (0 - all, 1 - fire, 2 - air, 4 - water, 8 - earth), value - level*/ \
|
||||||
BONUS_NAME(FREE_SHOOTING) /*stacks can shoot even if otherwise blocked (sharpshooter's bow effect)*/ \
|
BONUS_NAME(FREE_SHOOTING) /*stacks can shoot even if otherwise blocked (sharpshooter's bow effect)*/ \
|
||||||
@ -79,7 +74,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
BONUS_NAME(SPELL_LIKE_ATTACK) /*subtype - spell, value - spell level; range is taken from spell, but damage from creature; eg. magog*/ \
|
BONUS_NAME(SPELL_LIKE_ATTACK) /*subtype - spell, value - spell level; range is taken from spell, but damage from creature; eg. magog*/ \
|
||||||
BONUS_NAME(THREE_HEADED_ATTACK) /*eg. cerberus*/ \
|
BONUS_NAME(THREE_HEADED_ATTACK) /*eg. cerberus*/ \
|
||||||
BONUS_NAME(GENERAL_DAMAGE_PREMY) \
|
BONUS_NAME(GENERAL_DAMAGE_PREMY) \
|
||||||
BONUS_NAME(FIRE_IMMUNITY) /*subtype 0 - all, 1 - all except positive, 2 - only damage spells*/ \
|
BONUS_NAME(FIRE_IMMUNITY) /*subtype 0 - all, 1 - all except positive*/ \
|
||||||
BONUS_NAME(WATER_IMMUNITY) \
|
BONUS_NAME(WATER_IMMUNITY) \
|
||||||
BONUS_NAME(EARTH_IMMUNITY) \
|
BONUS_NAME(EARTH_IMMUNITY) \
|
||||||
BONUS_NAME(AIR_IMMUNITY) \
|
BONUS_NAME(AIR_IMMUNITY) \
|
||||||
@ -120,7 +115,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
BONUS_NAME(NO_MORALE) /*eg. when fighting on cursed ground*/ \
|
BONUS_NAME(NO_MORALE) /*eg. when fighting on cursed ground*/ \
|
||||||
BONUS_NAME(DARKNESS) /*val = radius */ \
|
BONUS_NAME(DARKNESS) /*val = radius */ \
|
||||||
BONUS_NAME(SPECIAL_SPELL_LEV) /*subtype = id, val = value per level in percent*/\
|
BONUS_NAME(SPECIAL_SPELL_LEV) /*subtype = id, val = value per level in percent*/\
|
||||||
BONUS_NAME(SPELL_DAMAGE) /*val = value, now works for sorcery*/\
|
BONUS_NAME(SPELL_DAMAGE) /*val = value, now works for sorcery, subtype - spell school; -1 - all, 0 - air, 1 - fire, 2 - water, 3 - earth*/\
|
||||||
BONUS_NAME(SPECIFIC_SPELL_DAMAGE) /*subtype = id of spell, val = value*/\
|
BONUS_NAME(SPECIFIC_SPELL_DAMAGE) /*subtype = id of spell, val = value*/\
|
||||||
BONUS_NAME(SPECIAL_PECULIAR_ENCHANT) /*blesses and curses with id = val dependent on unit's level, subtype = 0 or 1 for Coronius*/\
|
BONUS_NAME(SPECIAL_PECULIAR_ENCHANT) /*blesses and curses with id = val dependent on unit's level, subtype = 0 or 1 for Coronius*/\
|
||||||
BONUS_NAME(SPECIAL_UPGRADE) /*subtype = base, additionalInfo = target */\
|
BONUS_NAME(SPECIAL_UPGRADE) /*subtype = base, additionalInfo = target */\
|
||||||
@ -133,7 +128,6 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
BONUS_NAME(BIND_EFFECT) /*doesn't do anything particular, works as a marker)*/\
|
BONUS_NAME(BIND_EFFECT) /*doesn't do anything particular, works as a marker)*/\
|
||||||
BONUS_NAME(ACID_BREATH) /*additional val damage per creature after attack, additional info - chance in percent*/\
|
BONUS_NAME(ACID_BREATH) /*additional val damage per creature after attack, additional info - chance in percent*/\
|
||||||
BONUS_NAME(RECEPTIVE) /*accepts friendly spells even with immunity*/\
|
BONUS_NAME(RECEPTIVE) /*accepts friendly spells even with immunity*/\
|
||||||
BONUS_NAME(DIRECT_DAMAGE_IMMUNITY) /*direct damage spells, that is*/\
|
|
||||||
BONUS_NAME(CASTS) /*how many times creature can cast activated spell*/ \
|
BONUS_NAME(CASTS) /*how many times creature can cast activated spell*/ \
|
||||||
BONUS_NAME(SPECIFIC_SPELL_POWER) /* value used for Thunderbolt and Resurrection cast by units, subtype - spell id */\
|
BONUS_NAME(SPECIFIC_SPELL_POWER) /* value used for Thunderbolt and Resurrection cast by units, subtype - spell id */\
|
||||||
BONUS_NAME(CREATURE_SPELL_POWER) /* value per unit, divided by 100 (so faerie Dragons have 800)*/ \
|
BONUS_NAME(CREATURE_SPELL_POWER) /* value per unit, divided by 100 (so faerie Dragons have 800)*/ \
|
||||||
@ -221,18 +215,20 @@ enum class BonusType
|
|||||||
BONUS_LIST
|
BONUS_LIST
|
||||||
#undef BONUS_NAME
|
#undef BONUS_NAME
|
||||||
};
|
};
|
||||||
enum class BonusDuration : uint16_t //when bonus is automatically removed
|
namespace BonusDuration //when bonus is automatically removed
|
||||||
{
|
{
|
||||||
PERMANENT = 1,
|
using Type = std::bitset<10>;
|
||||||
ONE_BATTLE = 2, //at the end of battle
|
extern JsonNode toJson(const Type & duration);
|
||||||
ONE_DAY = 4, //at the end of day
|
constexpr Type PERMANENT = 1 << 0;
|
||||||
ONE_WEEK = 8, //at the end of week (bonus lasts till the end of week, thats NOT 7 days
|
constexpr Type ONE_BATTLE = 1 << 1; //at the end of battle
|
||||||
N_TURNS = 16, //used during battles, after battle bonus is always removed
|
constexpr Type ONE_DAY = 1 << 2; //at the end of day
|
||||||
N_DAYS = 32,
|
constexpr Type ONE_WEEK = 1 << 3; //at the end of week (bonus lasts till the end of week, thats NOT 7 days
|
||||||
UNTIL_BEING_ATTACKED = 64, /*removed after attack and counterattacks are performed*/
|
constexpr Type N_TURNS = 1 << 4; //used during battles, after battle bonus is always removed
|
||||||
UNTIL_ATTACK = 128, /*removed after attack and counterattacks are performed*/
|
constexpr Type N_DAYS = 1 << 5;
|
||||||
STACK_GETS_TURN = 256, /*removed when stack gets its turn - used for defensive stance*/
|
constexpr Type UNTIL_BEING_ATTACKED = 1 << 6; /*removed after attack and counterattacks are performed*/
|
||||||
COMMANDER_KILLED = 512
|
constexpr Type UNTIL_ATTACK = 1 << 7; /*removed after attack and counterattacks are performed*/
|
||||||
|
constexpr Type STACK_GETS_TURN = 1 << 8; /*removed when stack gets its turn - used for defensive stance*/
|
||||||
|
constexpr Type COMMANDER_KILLED = 1 << 9;
|
||||||
};
|
};
|
||||||
enum class BonusSource
|
enum class BonusSource
|
||||||
{
|
{
|
||||||
@ -258,7 +254,7 @@ enum class BonusValueType
|
|||||||
extern DLL_LINKAGE const std::map<std::string, BonusType> bonusNameMap;
|
extern DLL_LINKAGE const std::map<std::string, BonusType> bonusNameMap;
|
||||||
extern DLL_LINKAGE const std::map<std::string, BonusValueType> bonusValueMap;
|
extern DLL_LINKAGE const std::map<std::string, BonusValueType> bonusValueMap;
|
||||||
extern DLL_LINKAGE const std::map<std::string, BonusSource> bonusSourceMap;
|
extern DLL_LINKAGE const std::map<std::string, BonusSource> bonusSourceMap;
|
||||||
extern DLL_LINKAGE const std::map<std::string, BonusDuration> bonusDurationMap;
|
extern DLL_LINKAGE const std::map<std::string, BonusDuration::Type> bonusDurationMap;
|
||||||
extern DLL_LINKAGE const std::map<std::string, BonusLimitEffect> bonusLimitEffect;
|
extern DLL_LINKAGE const std::map<std::string, BonusLimitEffect> bonusLimitEffect;
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
@ -11,6 +11,8 @@
|
|||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "CBonusSystemNode.h"
|
#include "CBonusSystemNode.h"
|
||||||
|
|
||||||
|
#include "../JsonNode.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
BonusList::BonusList(bool BelongsToTree) : belongsToTree(BelongsToTree)
|
BonusList::BonusList(bool BelongsToTree) : belongsToTree(BelongsToTree)
|
||||||
|
@ -17,6 +17,34 @@
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
const std::set<std::string> deprecatedBonusSet = {
|
||||||
|
"SECONDARY_SKILL_PREMY",
|
||||||
|
"SECONDARY_SKILL_VAL2",
|
||||||
|
"MAXED_SPELL",
|
||||||
|
"LAND_MOVEMENT",
|
||||||
|
"SEA_MOVEMENT",
|
||||||
|
"SIGHT_RADIOUS",
|
||||||
|
"NO_TYPE",
|
||||||
|
"SPECIAL_SECONDARY_SKILL",
|
||||||
|
"FULL_HP_REGENERATION",
|
||||||
|
"KING1",
|
||||||
|
"KING2",
|
||||||
|
"KING3",
|
||||||
|
"BLOCK_MORALE",
|
||||||
|
"BLOCK_LUCK",
|
||||||
|
"SELF_MORALE",
|
||||||
|
"SELF_LUCK",
|
||||||
|
"DIRECT_DAMAGE_IMMUNITY",
|
||||||
|
"AIR_SPELL_DMG_PREMY",
|
||||||
|
"EARTH_SPELL_DMG_PREMY"
|
||||||
|
"FIRE_SPELL_DMG_PREMY"
|
||||||
|
"WATER_SPELL_DMG_PREMY",
|
||||||
|
"FIRE_SPELLS",
|
||||||
|
"AIR_SPELLS",
|
||||||
|
"WATER_SPELLS",
|
||||||
|
"EARTH_SPELLS"
|
||||||
|
};
|
||||||
|
|
||||||
BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr, int deprecatedSubtype):
|
BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr, int deprecatedSubtype):
|
||||||
isConverted(true)
|
isConverted(true)
|
||||||
{
|
{
|
||||||
@ -44,92 +72,79 @@ BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSu
|
|||||||
{
|
{
|
||||||
type = BonusType::MANA_PER_KNOWLEDGE;
|
type = BonusType::MANA_PER_KNOWLEDGE;
|
||||||
valueType = BonusValueType::PERCENT_TO_BASE;
|
valueType = BonusValueType::PERCENT_TO_BASE;
|
||||||
valueTypeRelevant = true;
|
|
||||||
}
|
}
|
||||||
else if(deprecatedSubtype == SecondarySkill::SORCERY || deprecatedSubtypeStr == "skill.sorcery")
|
else if(deprecatedSubtype == SecondarySkill::SORCERY || deprecatedSubtypeStr == "skill.sorcery")
|
||||||
|
{
|
||||||
type = BonusType::SPELL_DAMAGE;
|
type = BonusType::SPELL_DAMAGE;
|
||||||
|
subtype = SpellSchool(ESpellSchool::ANY);
|
||||||
|
}
|
||||||
else if(deprecatedSubtype == SecondarySkill::SCHOLAR || deprecatedSubtypeStr == "skill.scholar")
|
else if(deprecatedSubtype == SecondarySkill::SCHOLAR || deprecatedSubtypeStr == "skill.scholar")
|
||||||
type = BonusType::LEARN_MEETING_SPELL_LIMIT;
|
type = BonusType::LEARN_MEETING_SPELL_LIMIT;
|
||||||
else if(deprecatedSubtype == SecondarySkill::ARCHERY|| deprecatedSubtypeStr == "skill.archery")
|
else if(deprecatedSubtype == SecondarySkill::ARCHERY|| deprecatedSubtypeStr == "skill.archery")
|
||||||
{
|
{
|
||||||
subtype = 1;
|
subtype = 1;
|
||||||
subtypeRelevant = true;
|
|
||||||
type = BonusType::PERCENTAGE_DAMAGE_BOOST;
|
type = BonusType::PERCENTAGE_DAMAGE_BOOST;
|
||||||
}
|
}
|
||||||
else if(deprecatedSubtype == SecondarySkill::OFFENCE || deprecatedSubtypeStr == "skill.offence")
|
else if(deprecatedSubtype == SecondarySkill::OFFENCE || deprecatedSubtypeStr == "skill.offence")
|
||||||
{
|
{
|
||||||
subtype = 0;
|
subtype = 0;
|
||||||
subtypeRelevant = true;
|
|
||||||
type = BonusType::PERCENTAGE_DAMAGE_BOOST;
|
type = BonusType::PERCENTAGE_DAMAGE_BOOST;
|
||||||
}
|
}
|
||||||
else if(deprecatedSubtype == SecondarySkill::ARMORER || deprecatedSubtypeStr == "skill.armorer")
|
else if(deprecatedSubtype == SecondarySkill::ARMORER || deprecatedSubtypeStr == "skill.armorer")
|
||||||
{
|
{
|
||||||
subtype = -1;
|
subtype = -1;
|
||||||
subtypeRelevant = true;
|
|
||||||
type = BonusType::GENERAL_DAMAGE_REDUCTION;
|
type = BonusType::GENERAL_DAMAGE_REDUCTION;
|
||||||
}
|
}
|
||||||
else if(deprecatedSubtype == SecondarySkill::NAVIGATION || deprecatedSubtypeStr == "skill.navigation")
|
else if(deprecatedSubtype == SecondarySkill::NAVIGATION || deprecatedSubtypeStr == "skill.navigation")
|
||||||
{
|
{
|
||||||
subtype = 0;
|
subtype = 0;
|
||||||
subtypeRelevant = true;
|
|
||||||
valueType = BonusValueType::PERCENT_TO_BASE;
|
valueType = BonusValueType::PERCENT_TO_BASE;
|
||||||
valueTypeRelevant = true;
|
|
||||||
type = BonusType::MOVEMENT;
|
type = BonusType::MOVEMENT;
|
||||||
}
|
}
|
||||||
else if(deprecatedSubtype == SecondarySkill::LOGISTICS || deprecatedSubtypeStr == "skill.logistics")
|
else if(deprecatedSubtype == SecondarySkill::LOGISTICS || deprecatedSubtypeStr == "skill.logistics")
|
||||||
{
|
{
|
||||||
subtype = 1;
|
subtype = 1;
|
||||||
subtypeRelevant = true;
|
|
||||||
valueType = BonusValueType::PERCENT_TO_BASE;
|
valueType = BonusValueType::PERCENT_TO_BASE;
|
||||||
valueTypeRelevant = true;
|
|
||||||
type = BonusType::MOVEMENT;
|
type = BonusType::MOVEMENT;
|
||||||
}
|
}
|
||||||
else if(deprecatedSubtype == SecondarySkill::ESTATES || deprecatedSubtypeStr == "skill.estates")
|
else if(deprecatedSubtype == SecondarySkill::ESTATES || deprecatedSubtypeStr == "skill.estates")
|
||||||
{
|
{
|
||||||
type = BonusType::GENERATE_RESOURCE;
|
type = BonusType::GENERATE_RESOURCE;
|
||||||
subtype = GameResID(EGameResID::GOLD);
|
subtype = GameResID(EGameResID::GOLD);
|
||||||
subtypeRelevant = true;
|
|
||||||
}
|
}
|
||||||
else if(deprecatedSubtype == SecondarySkill::AIR_MAGIC || deprecatedSubtypeStr == "skill.airMagic")
|
else if(deprecatedSubtype == SecondarySkill::AIR_MAGIC || deprecatedSubtypeStr == "skill.airMagic")
|
||||||
{
|
{
|
||||||
type = BonusType::MAGIC_SCHOOL_SKILL;
|
type = BonusType::MAGIC_SCHOOL_SKILL;
|
||||||
subtypeRelevant = true;
|
subtype = SpellSchool(ESpellSchool::AIR);
|
||||||
subtype = 4;
|
|
||||||
}
|
}
|
||||||
else if(deprecatedSubtype == SecondarySkill::WATER_MAGIC || deprecatedSubtypeStr == "skill.waterMagic")
|
else if(deprecatedSubtype == SecondarySkill::WATER_MAGIC || deprecatedSubtypeStr == "skill.waterMagic")
|
||||||
{
|
{
|
||||||
type = BonusType::MAGIC_SCHOOL_SKILL;
|
type = BonusType::MAGIC_SCHOOL_SKILL;
|
||||||
subtypeRelevant = true;
|
subtype = SpellSchool(ESpellSchool::WATER);
|
||||||
subtype = 1;
|
|
||||||
}
|
}
|
||||||
else if(deprecatedSubtype == SecondarySkill::FIRE_MAGIC || deprecatedSubtypeStr == "skill.fireMagic")
|
else if(deprecatedSubtype == SecondarySkill::FIRE_MAGIC || deprecatedSubtypeStr == "skill.fireMagic")
|
||||||
{
|
{
|
||||||
type = BonusType::MAGIC_SCHOOL_SKILL;
|
type = BonusType::MAGIC_SCHOOL_SKILL;
|
||||||
subtypeRelevant = true;
|
subtype = SpellSchool(ESpellSchool::FIRE);
|
||||||
subtype = 2;
|
|
||||||
}
|
}
|
||||||
else if(deprecatedSubtype == SecondarySkill::EARTH_MAGIC || deprecatedSubtypeStr == "skill.earthMagic")
|
else if(deprecatedSubtype == SecondarySkill::EARTH_MAGIC || deprecatedSubtypeStr == "skill.earthMagic")
|
||||||
{
|
{
|
||||||
type = BonusType::MAGIC_SCHOOL_SKILL;
|
type = BonusType::MAGIC_SCHOOL_SKILL;
|
||||||
subtypeRelevant = true;
|
subtype = SpellSchool(ESpellSchool::EARTH);
|
||||||
subtype = 8;
|
|
||||||
}
|
}
|
||||||
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
|
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
|
||||||
{
|
{
|
||||||
type = BonusType::BONUS_DAMAGE_CHANCE;
|
type = BonusType::BONUS_DAMAGE_CHANCE;
|
||||||
subtypeRelevant = true;
|
|
||||||
subtypeStr = "core:creature.ballista";
|
subtypeStr = "core:creature.ballista";
|
||||||
}
|
}
|
||||||
else if (deprecatedSubtype == SecondarySkill::FIRST_AID || deprecatedSubtypeStr == "skill.firstAid")
|
else if (deprecatedSubtype == SecondarySkill::FIRST_AID || deprecatedSubtypeStr == "skill.firstAid")
|
||||||
{
|
{
|
||||||
type = BonusType::SPECIFIC_SPELL_POWER;
|
type = BonusType::SPECIFIC_SPELL_POWER;
|
||||||
subtypeRelevant = true;
|
|
||||||
subtypeStr = "core:spell.firstAid";
|
subtypeStr = "core:spell.firstAid";
|
||||||
}
|
}
|
||||||
else if (deprecatedSubtype == SecondarySkill::BALLISTICS || deprecatedSubtypeStr == "skill.ballistics")
|
else if (deprecatedSubtype == SecondarySkill::BALLISTICS || deprecatedSubtypeStr == "skill.ballistics")
|
||||||
{
|
{
|
||||||
type = BonusType::CATAPULT_EXTRA_SHOTS;
|
type = BonusType::CATAPULT_EXTRA_SHOTS;
|
||||||
subtypeRelevant = true;
|
|
||||||
subtypeStr = "core:spell.catapultShot";
|
subtypeStr = "core:spell.catapultShot";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -142,7 +157,6 @@ BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSu
|
|||||||
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
|
else if (deprecatedSubtype == SecondarySkill::ARTILLERY || deprecatedSubtypeStr == "skill.artillery")
|
||||||
{
|
{
|
||||||
type = BonusType::HERO_GRANTS_ATTACKS;
|
type = BonusType::HERO_GRANTS_ATTACKS;
|
||||||
subtypeRelevant = true;
|
|
||||||
subtypeStr = "core:creature.ballista";
|
subtypeStr = "core:creature.ballista";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -151,52 +165,41 @@ BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSu
|
|||||||
else if (deprecatedTypeStr == "SEA_MOVEMENT")
|
else if (deprecatedTypeStr == "SEA_MOVEMENT")
|
||||||
{
|
{
|
||||||
subtype = 0;
|
subtype = 0;
|
||||||
subtypeRelevant = true;
|
|
||||||
valueType = BonusValueType::ADDITIVE_VALUE;
|
valueType = BonusValueType::ADDITIVE_VALUE;
|
||||||
valueTypeRelevant = true;
|
|
||||||
type = BonusType::MOVEMENT;
|
type = BonusType::MOVEMENT;
|
||||||
}
|
}
|
||||||
else if (deprecatedTypeStr == "LAND_MOVEMENT")
|
else if (deprecatedTypeStr == "LAND_MOVEMENT")
|
||||||
{
|
{
|
||||||
subtype = 1;
|
subtype = 1;
|
||||||
subtypeRelevant = true;
|
|
||||||
valueType = BonusValueType::ADDITIVE_VALUE;
|
valueType = BonusValueType::ADDITIVE_VALUE;
|
||||||
valueTypeRelevant = true;
|
|
||||||
type = BonusType::MOVEMENT;
|
type = BonusType::MOVEMENT;
|
||||||
}
|
}
|
||||||
else if (deprecatedTypeStr == "MAXED_SPELL")
|
else if (deprecatedTypeStr == "MAXED_SPELL")
|
||||||
{
|
{
|
||||||
type = BonusType::SPELL;
|
type = BonusType::SPELL;
|
||||||
subtypeStr = deprecatedSubtypeStr;
|
subtypeStr = deprecatedSubtypeStr;
|
||||||
subtypeRelevant = true;
|
|
||||||
valueType = BonusValueType::INDEPENDENT_MAX;
|
valueType = BonusValueType::INDEPENDENT_MAX;
|
||||||
valueTypeRelevant = true;
|
|
||||||
val = 3;
|
val = 3;
|
||||||
valRelevant = true;
|
|
||||||
}
|
}
|
||||||
else if (deprecatedTypeStr == "FULL_HP_REGENERATION")
|
else if (deprecatedTypeStr == "FULL_HP_REGENERATION")
|
||||||
{
|
{
|
||||||
type = BonusType::HP_REGENERATION;
|
type = BonusType::HP_REGENERATION;
|
||||||
val = 100000; //very high value to always chose stack health
|
val = 100000; //very high value to always chose stack health
|
||||||
valRelevant = true;
|
|
||||||
}
|
}
|
||||||
else if (deprecatedTypeStr == "KING1")
|
else if (deprecatedTypeStr == "KING1")
|
||||||
{
|
{
|
||||||
type = BonusType::KING;
|
type = BonusType::KING;
|
||||||
val = 0;
|
val = 0;
|
||||||
valRelevant = true;
|
|
||||||
}
|
}
|
||||||
else if (deprecatedTypeStr == "KING2")
|
else if (deprecatedTypeStr == "KING2")
|
||||||
{
|
{
|
||||||
type = BonusType::KING;
|
type = BonusType::KING;
|
||||||
val = 2;
|
val = 2;
|
||||||
valRelevant = true;
|
|
||||||
}
|
}
|
||||||
else if (deprecatedTypeStr == "KING3")
|
else if (deprecatedTypeStr == "KING3")
|
||||||
{
|
{
|
||||||
type = BonusType::KING;
|
type = BonusType::KING;
|
||||||
val = 3;
|
val = 3;
|
||||||
valRelevant = true;
|
|
||||||
}
|
}
|
||||||
else if (deprecatedTypeStr == "SIGHT_RADIOUS")
|
else if (deprecatedTypeStr == "SIGHT_RADIOUS")
|
||||||
type = BonusType::SIGHT_RADIUS;
|
type = BonusType::SIGHT_RADIUS;
|
||||||
@ -204,17 +207,59 @@ BonusParams::BonusParams(std::string deprecatedTypeStr, std::string deprecatedSu
|
|||||||
{
|
{
|
||||||
type = BonusType::MORALE;
|
type = BonusType::MORALE;
|
||||||
val = 1;
|
val = 1;
|
||||||
valRelevant = true;
|
|
||||||
valueType = BonusValueType::INDEPENDENT_MAX;
|
valueType = BonusValueType::INDEPENDENT_MAX;
|
||||||
valueTypeRelevant = true;
|
|
||||||
}
|
}
|
||||||
else if (deprecatedTypeStr == "SELF_LUCK")
|
else if (deprecatedTypeStr == "SELF_LUCK")
|
||||||
{
|
{
|
||||||
type = BonusType::LUCK;
|
type = BonusType::LUCK;
|
||||||
val = 1;
|
val = 1;
|
||||||
valRelevant = true;
|
|
||||||
valueType = BonusValueType::INDEPENDENT_MAX;
|
valueType = BonusValueType::INDEPENDENT_MAX;
|
||||||
valueTypeRelevant = true;
|
}
|
||||||
|
else if (deprecatedTypeStr == "DIRECT_DAMAGE_IMMUNITY")
|
||||||
|
{
|
||||||
|
type = BonusType::SPELL_DAMAGE_REDUCTION;
|
||||||
|
subtype = SpellSchool(ESpellSchool::ANY);
|
||||||
|
val = 100;
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "AIR_SPELL_DMG_PREMY")
|
||||||
|
{
|
||||||
|
type = BonusType::SPELL_DAMAGE;
|
||||||
|
subtype = SpellSchool(ESpellSchool::AIR);
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "FIRE_SPELL_DMG_PREMY")
|
||||||
|
{
|
||||||
|
type = BonusType::SPELL_DAMAGE;
|
||||||
|
subtype = SpellSchool(ESpellSchool::FIRE);
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "WATER_SPELL_DMG_PREMY")
|
||||||
|
{
|
||||||
|
type = BonusType::SPELL_DAMAGE;
|
||||||
|
subtype = SpellSchool(ESpellSchool::WATER);
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "EARTH_SPELL_DMG_PREMY")
|
||||||
|
{
|
||||||
|
type = BonusType::SPELL_DAMAGE;
|
||||||
|
subtype = SpellSchool(ESpellSchool::EARTH);
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "AIR_SPELLS")
|
||||||
|
{
|
||||||
|
type = BonusType::SPELLS_OF_SCHOOL;
|
||||||
|
subtype = SpellSchool(ESpellSchool::AIR);
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "FIRE_SPELLS")
|
||||||
|
{
|
||||||
|
type = BonusType::SPELLS_OF_SCHOOL;
|
||||||
|
subtype = SpellSchool(ESpellSchool::FIRE);
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "WATER_SPELLS")
|
||||||
|
{
|
||||||
|
type = BonusType::SPELLS_OF_SCHOOL;
|
||||||
|
subtype = SpellSchool(ESpellSchool::WATER);
|
||||||
|
}
|
||||||
|
else if (deprecatedTypeStr == "EARTH_SPELLS")
|
||||||
|
{
|
||||||
|
type = BonusType::SPELLS_OF_SCHOOL;
|
||||||
|
subtype = SpellSchool(ESpellSchool::EARTH);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
isConverted = false;
|
isConverted = false;
|
||||||
@ -226,16 +271,16 @@ const JsonNode & BonusParams::toJson()
|
|||||||
if(ret.isNull())
|
if(ret.isNull())
|
||||||
{
|
{
|
||||||
ret["type"].String() = vstd::findKey(bonusNameMap, type);
|
ret["type"].String() = vstd::findKey(bonusNameMap, type);
|
||||||
if(subtypeRelevant && !subtypeStr.empty())
|
if(subtypeStr)
|
||||||
ret["subtype"].String() = subtypeStr;
|
ret["subtype"].String() = *subtypeStr;
|
||||||
else if(subtypeRelevant)
|
else if(subtype)
|
||||||
ret["subtype"].Integer() = subtype;
|
ret["subtype"].Integer() = *subtype;
|
||||||
if(valueTypeRelevant)
|
if(valueType)
|
||||||
ret["valueType"].String() = vstd::findKey(bonusValueMap, valueType);
|
ret["valueType"].String() = vstd::findKey(bonusValueMap, *valueType);
|
||||||
if(valRelevant)
|
if(val)
|
||||||
ret["val"].Float() = val;
|
ret["val"].Float() = *val;
|
||||||
if(targetTypeRelevant)
|
if(targetType)
|
||||||
ret["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetType);
|
ret["targetSourceType"].String() = vstd::findKey(bonusSourceMap, *targetType);
|
||||||
jsonCreated = true;
|
jsonCreated = true;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@ -244,16 +289,19 @@ const JsonNode & BonusParams::toJson()
|
|||||||
CSelector BonusParams::toSelector()
|
CSelector BonusParams::toSelector()
|
||||||
{
|
{
|
||||||
assert(isConverted);
|
assert(isConverted);
|
||||||
if(subtypeRelevant && !subtypeStr.empty())
|
if(subtypeStr)
|
||||||
JsonUtils::resolveIdentifier(subtype, toJson(), "subtype");
|
{
|
||||||
|
subtype = -1;
|
||||||
|
JsonUtils::resolveIdentifier(*subtype, toJson(), "subtype");
|
||||||
|
}
|
||||||
|
|
||||||
auto ret = Selector::type()(type);
|
auto ret = Selector::type()(type);
|
||||||
if(subtypeRelevant)
|
if(subtype)
|
||||||
ret = ret.And(Selector::subtype()(subtype));
|
ret = ret.And(Selector::subtype()(*subtype));
|
||||||
if(valueTypeRelevant)
|
if(valueType)
|
||||||
ret = ret.And(Selector::valueType(valueType));
|
ret = ret.And(Selector::valueType(*valueType));
|
||||||
if(targetTypeRelevant)
|
if(targetType)
|
||||||
ret = ret.And(Selector::targetSourceType()(targetType));
|
ret = ret.And(Selector::targetSourceType()(*targetType));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,15 +19,11 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
struct DLL_LINKAGE BonusParams {
|
struct DLL_LINKAGE BonusParams {
|
||||||
bool isConverted;
|
bool isConverted;
|
||||||
BonusType type = BonusType::NONE;
|
BonusType type = BonusType::NONE;
|
||||||
TBonusSubtype subtype = -1;
|
std::optional<TBonusSubtype> subtype = std::nullopt;
|
||||||
std::string subtypeStr;
|
std::optional<std::string> subtypeStr = std::nullopt;
|
||||||
bool subtypeRelevant = false;
|
std::optional<BonusValueType> valueType = std::nullopt;
|
||||||
BonusValueType valueType = BonusValueType::BASE_NUMBER;
|
std::optional<si32> val = std::nullopt;
|
||||||
bool valueTypeRelevant = false;
|
std::optional<BonusSource> targetType = std::nullopt;
|
||||||
si32 val = 0;
|
|
||||||
bool valRelevant = false;
|
|
||||||
BonusSource targetType = BonusSource::SECONDARY_SKILL;
|
|
||||||
bool targetTypeRelevant = false;
|
|
||||||
|
|
||||||
BonusParams(bool isConverted = true) : isConverted(isConverted) {};
|
BonusParams(bool isConverted = true) : isConverted(isConverted) {};
|
||||||
BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr = "", int deprecatedSubtype = 0);
|
BonusParams(std::string deprecatedTypeStr, std::string deprecatedSubtypeStr = "", int deprecatedSubtype = 0);
|
||||||
@ -38,4 +34,6 @@ private:
|
|||||||
bool jsonCreated = false;
|
bool jsonCreated = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern DLL_LINKAGE const std::set<std::string> deprecatedBonusSet;
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
@ -23,11 +23,6 @@ constexpr bool CBonusSystemNode::cachingEnabled = true;
|
|||||||
#define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents)
|
#define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents)
|
||||||
#define FOREACH_RED_CHILD(pname) TNodes lchildren; getRedChildren(lchildren); for(CBonusSystemNode *pname : lchildren)
|
#define FOREACH_RED_CHILD(pname) TNodes lchildren; getRedChildren(lchildren); for(CBonusSystemNode *pname : lchildren)
|
||||||
|
|
||||||
PlayerColor CBonusSystemNode::retrieveNodeOwner(const CBonusSystemNode * node)
|
|
||||||
{
|
|
||||||
return node ? node->getOwner() : PlayerColor::CANNOT_DETERMINE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector)
|
std::shared_ptr<Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector & selector)
|
||||||
{
|
{
|
||||||
auto ret = bonuses.getFirst(selector);
|
auto ret = bonuses.getFirst(selector);
|
||||||
@ -51,19 +46,15 @@ std::shared_ptr<const Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelecto
|
|||||||
|
|
||||||
void CBonusSystemNode::getParents(TCNodes & out) const /*retrieves list of parent nodes (nodes to inherit bonuses from) */
|
void CBonusSystemNode::getParents(TCNodes & out) const /*retrieves list of parent nodes (nodes to inherit bonuses from) */
|
||||||
{
|
{
|
||||||
for(const auto & elem : parents)
|
for(const auto * elem : parents)
|
||||||
{
|
out.insert(elem);
|
||||||
const CBonusSystemNode *parent = elem;
|
|
||||||
out.insert(parent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBonusSystemNode::getParents(TNodes &out)
|
void CBonusSystemNode::getParents(TNodes &out)
|
||||||
{
|
{
|
||||||
for (auto & elem : parents)
|
for (auto * elem : parents)
|
||||||
{
|
{
|
||||||
const CBonusSystemNode *parent = elem;
|
out.insert(elem);
|
||||||
out.insert(const_cast<CBonusSystemNode*>(parent));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,11 +209,6 @@ std::shared_ptr<Bonus> CBonusSystemNode::getUpdatedBonus(const std::shared_ptr<B
|
|||||||
return updater->createUpdatedBonus(b, * this);
|
return updater->createUpdatedBonus(b, * this);
|
||||||
}
|
}
|
||||||
|
|
||||||
CBonusSystemNode::CBonusSystemNode()
|
|
||||||
:CBonusSystemNode(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CBonusSystemNode::CBonusSystemNode(bool isHypotetic):
|
CBonusSystemNode::CBonusSystemNode(bool isHypotetic):
|
||||||
bonuses(true),
|
bonuses(true),
|
||||||
exportedBonuses(true),
|
exportedBonuses(true),
|
||||||
@ -245,7 +231,6 @@ CBonusSystemNode::CBonusSystemNode(CBonusSystemNode && other) noexcept:
|
|||||||
bonuses(std::move(other.bonuses)),
|
bonuses(std::move(other.bonuses)),
|
||||||
exportedBonuses(std::move(other.exportedBonuses)),
|
exportedBonuses(std::move(other.exportedBonuses)),
|
||||||
nodeType(other.nodeType),
|
nodeType(other.nodeType),
|
||||||
description(other.description),
|
|
||||||
cachedLast(0),
|
cachedLast(0),
|
||||||
isHypotheticNode(other.isHypotheticNode)
|
isHypotheticNode(other.isHypotheticNode)
|
||||||
{
|
{
|
||||||
@ -466,18 +451,13 @@ bool CBonusSystemNode::isIndependentNode() const
|
|||||||
|
|
||||||
std::string CBonusSystemNode::nodeName() const
|
std::string CBonusSystemNode::nodeName() const
|
||||||
{
|
{
|
||||||
return !description.empty()
|
return std::string("Bonus system node of type ") + typeid(*this).name();
|
||||||
? description
|
|
||||||
: std::string("Bonus system node of type ") + typeid(*this).name();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CBonusSystemNode::nodeShortInfo() const
|
std::string CBonusSystemNode::nodeShortInfo() const
|
||||||
{
|
{
|
||||||
std::ostringstream str;
|
std::ostringstream str;
|
||||||
str << "'" << typeid(* this).name() << "'";
|
str << "'" << typeid(* this).name() << "'";
|
||||||
description.length() > 0
|
|
||||||
? str << " (" << description << ")"
|
|
||||||
: str << " (no description)";
|
|
||||||
return str.str();
|
return str.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,21 +574,11 @@ CBonusSystemNode::ENodeTypes CBonusSystemNode::getNodeType() const
|
|||||||
return nodeType;
|
return nodeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BonusList& CBonusSystemNode::getBonusList() const
|
|
||||||
{
|
|
||||||
return bonuses;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TNodesVector& CBonusSystemNode::getParentNodes() const
|
const TNodesVector& CBonusSystemNode::getParentNodes() const
|
||||||
{
|
{
|
||||||
return parents;
|
return parents;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TNodesVector& CBonusSystemNode::getChildrenNodes() const
|
|
||||||
{
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::setNodeType(CBonusSystemNode::ENodeTypes type)
|
void CBonusSystemNode::setNodeType(CBonusSystemNode::ENodeTypes type)
|
||||||
{
|
{
|
||||||
nodeType = type;
|
nodeType = type;
|
||||||
@ -624,16 +594,6 @@ const BonusList & CBonusSystemNode::getExportedBonusList() const
|
|||||||
return exportedBonuses;
|
return exportedBonuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& CBonusSystemNode::getDescription() const
|
|
||||||
{
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::setDescription(const std::string &description)
|
|
||||||
{
|
|
||||||
this->description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBonusSystemNode::limitBonuses(const BonusList &allBonuses, BonusList &out) const
|
void CBonusSystemNode::limitBonuses(const BonusList &allBonuses, BonusList &out) const
|
||||||
{
|
{
|
||||||
assert(&allBonuses != &out); //todo should it work in-place?
|
assert(&allBonuses != &out); //todo should it work in-place?
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "GameConstants.h"
|
||||||
|
|
||||||
#include "BonusList.h"
|
#include "BonusList.h"
|
||||||
#include "IBonusBearer.h"
|
#include "IBonusBearer.h"
|
||||||
|
|
||||||
@ -35,7 +37,6 @@ private:
|
|||||||
TNodesVector children;
|
TNodesVector children;
|
||||||
|
|
||||||
ENodeTypes nodeType;
|
ENodeTypes nodeType;
|
||||||
std::string description;
|
|
||||||
bool isHypotheticNode;
|
bool isHypotheticNode;
|
||||||
|
|
||||||
static const bool cachingEnabled;
|
static const bool cachingEnabled;
|
||||||
@ -53,9 +54,31 @@ private:
|
|||||||
TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) const;
|
TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) const;
|
||||||
std::shared_ptr<Bonus> getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const;
|
std::shared_ptr<Bonus> getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const;
|
||||||
|
|
||||||
|
void getRedParents(TNodes &out); //retrieves list of red parent nodes (nodes bonuses propagate from)
|
||||||
|
void getRedAncestors(TNodes &out);
|
||||||
|
void getRedChildren(TNodes &out);
|
||||||
|
|
||||||
|
void getAllParents(TCNodes & out) const;
|
||||||
|
|
||||||
|
void newChildAttached(CBonusSystemNode & child);
|
||||||
|
void childDetached(CBonusSystemNode & child);
|
||||||
|
void propagateBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & source);
|
||||||
|
void unpropagateBonus(const std::shared_ptr<Bonus> & b);
|
||||||
|
bool actsAsBonusSourceOnly() const;
|
||||||
|
|
||||||
|
void newRedDescendant(CBonusSystemNode & descendant); //propagation needed
|
||||||
|
void removedRedDescendant(CBonusSystemNode & descendant); //de-propagation needed
|
||||||
|
|
||||||
|
std::string nodeShortInfo() const;
|
||||||
|
|
||||||
|
void exportBonus(const std::shared_ptr<Bonus> & b);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool isIndependentNode() const; //node is independent when it has no parents nor children
|
||||||
|
void exportBonuses();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CBonusSystemNode();
|
explicit CBonusSystemNode(bool isHypotetic = false);
|
||||||
explicit CBonusSystemNode(bool isHypotetic);
|
|
||||||
explicit CBonusSystemNode(ENodeTypes NodeType);
|
explicit CBonusSystemNode(ENodeTypes NodeType);
|
||||||
CBonusSystemNode(CBonusSystemNode && other) noexcept;
|
CBonusSystemNode(CBonusSystemNode && other) noexcept;
|
||||||
virtual ~CBonusSystemNode();
|
virtual ~CBonusSystemNode();
|
||||||
@ -68,12 +91,6 @@ public:
|
|||||||
|
|
||||||
//non-const interface
|
//non-const interface
|
||||||
void getParents(TNodes &out); //retrieves list of parent nodes (nodes to inherit bonuses from)
|
void getParents(TNodes &out); //retrieves list of parent nodes (nodes to inherit bonuses from)
|
||||||
|
|
||||||
void getRedParents(TNodes &out); //retrieves list of red parent nodes (nodes bonuses propagate from)
|
|
||||||
void getRedAncestors(TNodes &out);
|
|
||||||
void getRedChildren(TNodes &out);
|
|
||||||
void getAllParents(TCNodes & out) const;
|
|
||||||
static PlayerColor retrieveNodeOwner(const CBonusSystemNode * node);
|
|
||||||
std::shared_ptr<Bonus> getBonusLocalFirst(const CSelector & selector);
|
std::shared_ptr<Bonus> getBonusLocalFirst(const CSelector & selector);
|
||||||
|
|
||||||
void attachTo(CBonusSystemNode & parent);
|
void attachTo(CBonusSystemNode & parent);
|
||||||
@ -82,38 +99,23 @@ public:
|
|||||||
virtual void addNewBonus(const std::shared_ptr<Bonus>& b);
|
virtual void addNewBonus(const std::shared_ptr<Bonus>& b);
|
||||||
void accumulateBonus(const std::shared_ptr<Bonus>& b); //add value of bonus with same type/subtype or create new
|
void accumulateBonus(const std::shared_ptr<Bonus>& b); //add value of bonus with same type/subtype or create new
|
||||||
|
|
||||||
void newChildAttached(CBonusSystemNode & child);
|
|
||||||
void childDetached(CBonusSystemNode & child);
|
|
||||||
void propagateBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & source);
|
|
||||||
void unpropagateBonus(const std::shared_ptr<Bonus> & b);
|
|
||||||
void removeBonus(const std::shared_ptr<Bonus>& b);
|
void removeBonus(const std::shared_ptr<Bonus>& b);
|
||||||
void removeBonuses(const CSelector & selector);
|
void removeBonuses(const CSelector & selector);
|
||||||
void removeBonusesRecursive(const CSelector & s);
|
void removeBonusesRecursive(const CSelector & s);
|
||||||
void newRedDescendant(CBonusSystemNode & descendant); //propagation needed
|
|
||||||
void removedRedDescendant(CBonusSystemNode & descendant); //de-propagation needed
|
|
||||||
|
|
||||||
bool isIndependentNode() const; //node is independent when it has no parents nor children
|
|
||||||
bool actsAsBonusSourceOnly() const;
|
|
||||||
///updates count of remaining turns and removes outdated bonuses by selector
|
///updates count of remaining turns and removes outdated bonuses by selector
|
||||||
void reduceBonusDurations(const CSelector &s);
|
void reduceBonusDurations(const CSelector &s);
|
||||||
virtual std::string bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const {return "";}; //description or bonus name
|
virtual std::string bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const {return "";}; //description or bonus name
|
||||||
virtual std::string nodeName() const;
|
virtual std::string nodeName() const;
|
||||||
virtual std::string nodeShortInfo() const;
|
|
||||||
bool isHypothetic() const { return isHypotheticNode; }
|
bool isHypothetic() const { return isHypotheticNode; }
|
||||||
|
|
||||||
void deserializationFix();
|
void deserializationFix();
|
||||||
void exportBonus(const std::shared_ptr<Bonus> & b);
|
|
||||||
void exportBonuses();
|
|
||||||
|
|
||||||
const BonusList &getBonusList() const;
|
|
||||||
BonusList & getExportedBonusList();
|
BonusList & getExportedBonusList();
|
||||||
const BonusList & getExportedBonusList() const;
|
const BonusList & getExportedBonusList() const;
|
||||||
CBonusSystemNode::ENodeTypes getNodeType() const;
|
CBonusSystemNode::ENodeTypes getNodeType() const;
|
||||||
void setNodeType(CBonusSystemNode::ENodeTypes type);
|
void setNodeType(CBonusSystemNode::ENodeTypes type);
|
||||||
const TNodesVector &getParentNodes() const;
|
const TNodesVector & getParentNodes() const;
|
||||||
const TNodesVector &getChildrenNodes() const;
|
|
||||||
const std::string &getDescription() const;
|
|
||||||
void setDescription(const std::string &description);
|
|
||||||
|
|
||||||
static void treeHasChanged();
|
static void treeHasChanged();
|
||||||
|
|
||||||
@ -129,7 +131,6 @@ public:
|
|||||||
// h & bonuses;
|
// h & bonuses;
|
||||||
h & nodeType;
|
h & nodeType;
|
||||||
h & exportedBonuses;
|
h & exportedBonuses;
|
||||||
h & description;
|
|
||||||
BONUS_TREE_DESERIALIZATION_FIX
|
BONUS_TREE_DESERIALIZATION_FIX
|
||||||
//h & parents & children;
|
//h & parents & children;
|
||||||
}
|
}
|
||||||
|
@ -15,45 +15,21 @@
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
int IBonusBearer::valOfBonuses(BonusType type, int subtype) const
|
|
||||||
{
|
|
||||||
//This part is performance-critical
|
|
||||||
std::string cachingStr = "type_" + std::to_string(static_cast<int>(type)) + "_" + std::to_string(subtype);
|
|
||||||
|
|
||||||
CSelector s = Selector::type()(type);
|
|
||||||
if(subtype != -1)
|
|
||||||
s = s.And(Selector::subtype()(subtype));
|
|
||||||
|
|
||||||
return valOfBonuses(s, cachingStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
int IBonusBearer::valOfBonuses(const CSelector &selector, const std::string &cachingStr) const
|
int IBonusBearer::valOfBonuses(const CSelector &selector, const std::string &cachingStr) const
|
||||||
{
|
{
|
||||||
CSelector limit = nullptr;
|
TConstBonusListPtr hlp = getAllBonuses(selector, nullptr, nullptr, cachingStr);
|
||||||
TConstBonusListPtr hlp = getAllBonuses(selector, limit, nullptr, cachingStr);
|
|
||||||
return hlp->totalValue();
|
return hlp->totalValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IBonusBearer::hasBonus(const CSelector &selector, const std::string &cachingStr) const
|
bool IBonusBearer::hasBonus(const CSelector &selector, const std::string &cachingStr) const
|
||||||
{
|
{
|
||||||
//TODO: We don't need to count all bonuses and could break on first matching
|
//TODO: We don't need to count all bonuses and could break on first matching
|
||||||
return getBonuses(selector, cachingStr)->size() > 0;
|
return !getBonuses(selector, cachingStr)->empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IBonusBearer::hasBonus(const CSelector &selector, const CSelector &limit, const std::string &cachingStr) const
|
bool IBonusBearer::hasBonus(const CSelector &selector, const CSelector &limit, const std::string &cachingStr) const
|
||||||
{
|
{
|
||||||
return getBonuses(selector, limit, cachingStr)->size() > 0;
|
return !getBonuses(selector, limit, cachingStr)->empty();
|
||||||
}
|
|
||||||
|
|
||||||
bool IBonusBearer::hasBonusOfType(BonusType type, int subtype) const
|
|
||||||
{
|
|
||||||
//This part is performance-ciritcal
|
|
||||||
std::string cachingStr = "type_" + std::to_string(static_cast<int>(type)) + "_" + std::to_string(subtype);
|
|
||||||
|
|
||||||
CSelector s = Selector::type()(type);
|
|
||||||
if(subtype != -1)
|
|
||||||
s = s.And(Selector::subtype()(subtype));
|
|
||||||
|
|
||||||
return hasBonus(s, cachingStr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TConstBonusListPtr IBonusBearer::getBonuses(const CSelector &selector, const std::string &cachingStr) const
|
TConstBonusListPtr IBonusBearer::getBonuses(const CSelector &selector, const std::string &cachingStr) const
|
||||||
@ -66,6 +42,46 @@ TConstBonusListPtr IBonusBearer::getBonuses(const CSelector &selector, const CSe
|
|||||||
return getAllBonuses(selector, limit, nullptr, cachingStr);
|
return getAllBonuses(selector, limit, nullptr, cachingStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int IBonusBearer::valOfBonuses(BonusType type) const
|
||||||
|
{
|
||||||
|
//This part is performance-critical
|
||||||
|
std::string cachingStr = "type_" + std::to_string(static_cast<int>(type));
|
||||||
|
|
||||||
|
CSelector s = Selector::type()(type);
|
||||||
|
|
||||||
|
return valOfBonuses(s, cachingStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IBonusBearer::hasBonusOfType(BonusType type) const
|
||||||
|
{
|
||||||
|
//This part is performance-critical
|
||||||
|
std::string cachingStr = "type_" + std::to_string(static_cast<int>(type));
|
||||||
|
|
||||||
|
CSelector s = Selector::type()(type);
|
||||||
|
|
||||||
|
return hasBonus(s, cachingStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int IBonusBearer::valOfBonuses(BonusType type, int subtype) const
|
||||||
|
{
|
||||||
|
//This part is performance-critical
|
||||||
|
std::string cachingStr = "type_" + std::to_string(static_cast<int>(type)) + "_" + std::to_string(subtype);
|
||||||
|
|
||||||
|
CSelector s = Selector::typeSubtype(type, subtype);
|
||||||
|
|
||||||
|
return valOfBonuses(s, cachingStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IBonusBearer::hasBonusOfType(BonusType type, int subtype) const
|
||||||
|
{
|
||||||
|
//This part is performance-critical
|
||||||
|
std::string cachingStr = "type_" + std::to_string(static_cast<int>(type)) + "_" + std::to_string(subtype);
|
||||||
|
|
||||||
|
CSelector s = Selector::typeSubtype(type, subtype);
|
||||||
|
|
||||||
|
return hasBonus(s, cachingStr);
|
||||||
|
}
|
||||||
|
|
||||||
bool IBonusBearer::hasBonusFrom(BonusSource source, ui32 sourceID) const
|
bool IBonusBearer::hasBonusFrom(BonusSource source, ui32 sourceID) const
|
||||||
{
|
{
|
||||||
boost::format fmt("source_%did_%d");
|
boost::format fmt("source_%did_%d");
|
||||||
|
@ -33,8 +33,10 @@ public:
|
|||||||
std::shared_ptr<const Bonus> getBonus(const CSelector &selector) const; //returns any bonus visible on node that matches (or nullptr if none matches)
|
std::shared_ptr<const Bonus> getBonus(const CSelector &selector) const; //returns any bonus visible on node that matches (or nullptr if none matches)
|
||||||
|
|
||||||
//Optimized interface (with auto-caching)
|
//Optimized interface (with auto-caching)
|
||||||
int valOfBonuses(BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then anyt;
|
int valOfBonuses(BonusType type) const; //subtype -> subtype of bonus;
|
||||||
bool hasBonusOfType(BonusType type, int subtype = -1) const;//determines if hero has a bonus of given type (and optionally subtype)
|
bool hasBonusOfType(BonusType type) const;//determines if hero has a bonus of given type (and optionally subtype)
|
||||||
|
int valOfBonuses(BonusType type, int subtype) const; //subtype -> subtype of bonus;
|
||||||
|
bool hasBonusOfType(BonusType type, int subtype) const;//determines if hero has a bonus of given type (and optionally subtype)
|
||||||
bool hasBonusFrom(BonusSource source, ui32 sourceID) const;
|
bool hasBonusFrom(BonusSource source, ui32 sourceID) const;
|
||||||
|
|
||||||
virtual int64_t getTreeVersion() const = 0;
|
virtual int64_t getTreeVersion() const = 0;
|
||||||
|
@ -430,7 +430,7 @@ OppositeSideLimiter::OppositeSideLimiter(PlayerColor Owner):
|
|||||||
|
|
||||||
ILimiter::EDecision OppositeSideLimiter::limit(const BonusLimitationContext & context) const
|
ILimiter::EDecision OppositeSideLimiter::limit(const BonusLimitationContext & context) const
|
||||||
{
|
{
|
||||||
auto contextOwner = CBonusSystemNode::retrieveNodeOwner(& context.node);
|
auto contextOwner = context.node.getOwner();
|
||||||
auto decision = (owner == contextOwner || owner == PlayerColor::CANNOT_DETERMINE) ? ILimiter::EDecision::DISCARD : ILimiter::EDecision::ACCEPT;
|
auto decision = (owner == contextOwner || owner == PlayerColor::CANNOT_DETERMINE) ? ILimiter::EDecision::DISCARD : ILimiter::EDecision::ACCEPT;
|
||||||
return decision;
|
return decision;
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Bonus.h"
|
#include "Bonus.h"
|
||||||
#include "battle/BattleHex.h"
|
|
||||||
|
#include "../GameConstants.h"
|
||||||
|
#include "../battle/BattleHex.h"
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class CCreature;
|
||||||
|
|
||||||
extern DLL_LINKAGE const std::map<std::string, TLimiterPtr> bonusLimiterMap;
|
extern DLL_LINKAGE const std::map<std::string, TLimiterPtr> bonusLimiterMap;
|
||||||
|
|
||||||
struct BonusLimitationContext
|
struct BonusLimitationContext
|
||||||
|
@ -197,7 +197,7 @@ JsonNode OwnerUpdater::toJsonNode() const
|
|||||||
|
|
||||||
std::shared_ptr<Bonus> OwnerUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
|
std::shared_ptr<Bonus> OwnerUpdater::createUpdatedBonus(const std::shared_ptr<Bonus> & b, const CBonusSystemNode & context) const
|
||||||
{
|
{
|
||||||
auto owner = CBonusSystemNode::retrieveNodeOwner(&context);
|
auto owner = context.getOwner();
|
||||||
|
|
||||||
if(owner == PlayerColor::UNFLAGGABLE)
|
if(owner == PlayerColor::UNFLAGGABLE)
|
||||||
owner = PlayerColor::NEUTRAL;
|
owner = PlayerColor::NEUTRAL;
|
||||||
|
@ -589,7 +589,7 @@ int32_t CGHeroInstance::getSpellSchoolLevel(const spells::Spell * spell, int32_t
|
|||||||
|
|
||||||
spell->forEachSchool([&, this](const spells::SchoolInfo & cnf, bool & stop)
|
spell->forEachSchool([&, this](const spells::SchoolInfo & cnf, bool & stop)
|
||||||
{
|
{
|
||||||
int32_t thisSchool = valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, 1 << (static_cast<ui8>(cnf.id))); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
|
int32_t thisSchool = valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, cnf.id); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
|
||||||
if(thisSchool > skill)
|
if(thisSchool > skill)
|
||||||
{
|
{
|
||||||
skill = thisSchool;
|
skill = thisSchool;
|
||||||
@ -598,7 +598,7 @@ int32_t CGHeroInstance::getSpellSchoolLevel(const spells::Spell * spell, int32_t
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
vstd::amax(skill, valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, 0)); //any school bonus
|
vstd::amax(skill, valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, SpellSchool(ESpellSchool::ANY))); //any school bonus
|
||||||
vstd::amax(skill, valOfBonuses(BonusType::SPELL, spell->getIndex())); //given by artifact or other effect
|
vstd::amax(skill, valOfBonuses(BonusType::SPELL, spell->getIndex())); //given by artifact or other effect
|
||||||
|
|
||||||
vstd::amax(skill, 0); //in case we don't know any school
|
vstd::amax(skill, 0); //in case we don't know any school
|
||||||
@ -611,7 +611,7 @@ int64_t CGHeroInstance::getSpellBonus(const spells::Spell * spell, int64_t base,
|
|||||||
//applying sorcery secondary skill
|
//applying sorcery secondary skill
|
||||||
|
|
||||||
if(spell->isMagical())
|
if(spell->isMagical())
|
||||||
base = static_cast<int64_t>(base * (valOfBonuses(BonusType::SPELL_DAMAGE)) / 100.0);
|
base = static_cast<int64_t>(base * (valOfBonuses(BonusType::SPELL_DAMAGE, SpellSchool(ESpellSchool::ANY))) / 100.0);
|
||||||
|
|
||||||
base = static_cast<int64_t>(base * (100 + valOfBonuses(BonusType::SPECIFIC_SPELL_DAMAGE, spell->getIndex())) / 100.0);
|
base = static_cast<int64_t>(base * (100 + valOfBonuses(BonusType::SPECIFIC_SPELL_DAMAGE, spell->getIndex())) / 100.0);
|
||||||
|
|
||||||
@ -619,7 +619,7 @@ int64_t CGHeroInstance::getSpellBonus(const spells::Spell * spell, int64_t base,
|
|||||||
|
|
||||||
spell->forEachSchool([&maxSchoolBonus, this](const spells::SchoolInfo & cnf, bool & stop)
|
spell->forEachSchool([&maxSchoolBonus, this](const spells::SchoolInfo & cnf, bool & stop)
|
||||||
{
|
{
|
||||||
vstd::amax(maxSchoolBonus, valOfBonuses(cnf.damagePremyBonus));
|
vstd::amax(maxSchoolBonus, valOfBonuses(BonusType::SPELL_DAMAGE, cnf.id));
|
||||||
});
|
});
|
||||||
|
|
||||||
base = static_cast<int64_t>(base * (100 + maxSchoolBonus) / 100.0);
|
base = static_cast<int64_t>(base * (100 + maxSchoolBonus) / 100.0);
|
||||||
@ -708,7 +708,7 @@ bool CGHeroInstance::canCastThisSpell(const spells::Spell * spell) const
|
|||||||
|
|
||||||
spell->forEachSchool([this, &schoolBonus](const spells::SchoolInfo & cnf, bool & stop)
|
spell->forEachSchool([this, &schoolBonus](const spells::SchoolInfo & cnf, bool & stop)
|
||||||
{
|
{
|
||||||
if(hasBonusOfType(cnf.knoledgeBonus))
|
if(hasBonusOfType(BonusType::SPELLS_OF_SCHOOL, cnf.id))
|
||||||
{
|
{
|
||||||
schoolBonus = stop = true;
|
schoolBonus = stop = true;
|
||||||
}
|
}
|
||||||
|
@ -272,7 +272,7 @@ int3 CGObjectInstance::getVisitableOffset() const
|
|||||||
return appearance->getVisitableOffset();
|
return appearance->getVisitableOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGObjectInstance::giveDummyBonus(const ObjectInstanceID & heroID, BonusDuration duration) const
|
void CGObjectInstance::giveDummyBonus(const ObjectInstanceID & heroID, BonusDuration::Type duration) const
|
||||||
{
|
{
|
||||||
GiveBonus gbonus;
|
GiveBonus gbonus;
|
||||||
gbonus.bonus.type = BonusType::NONE;
|
gbonus.bonus.type = BonusType::NONE;
|
||||||
|
@ -232,7 +232,7 @@ protected:
|
|||||||
virtual void setPropertyDer(ui8 what, ui32 val);
|
virtual void setPropertyDer(ui8 what, ui32 val);
|
||||||
|
|
||||||
/// Gives dummy bonus from this object to hero. Can be used to track visited state
|
/// Gives dummy bonus from this object to hero. Can be used to track visited state
|
||||||
void giveDummyBonus(const ObjectInstanceID & heroID, BonusDuration duration = BonusDuration::ONE_DAY) const;
|
void giveDummyBonus(const ObjectInstanceID & heroID, BonusDuration::Type duration = BonusDuration::ONE_DAY) const;
|
||||||
|
|
||||||
///Serialize object-type specific options
|
///Serialize object-type specific options
|
||||||
virtual void serializeJsonOptions(JsonSerializeFormat & handler);
|
virtual void serializeJsonOptions(JsonSerializeFormat & handler);
|
||||||
|
@ -343,7 +343,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
|||||||
std::vector <CSpell *> spells;
|
std::vector <CSpell *> spells;
|
||||||
for(auto spell : VLC->spellh->objects)
|
for(auto spell : VLC->spellh->objects)
|
||||||
{
|
{
|
||||||
if(map.isAllowedSpell(spell->id) && spell->school[static_cast<ESpellSchool>(i)])
|
if(map.isAllowedSpell(spell->id) && spell->school[SpellSchool(i)])
|
||||||
spells.push_back(spell);
|
spells.push_back(spell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,6 +550,29 @@ public:
|
|||||||
for(ui32 i = 0; i < length; i++)
|
for(ui32 i = 0; i < length; i++)
|
||||||
load(data.data()[i]);
|
load(data.data()[i]);
|
||||||
}
|
}
|
||||||
|
template <std::size_t T>
|
||||||
|
void load(std::bitset<T> &data)
|
||||||
|
{
|
||||||
|
static_assert(T <= 64);
|
||||||
|
if constexpr (T <= 16)
|
||||||
|
{
|
||||||
|
uint16_t read;
|
||||||
|
load(read);
|
||||||
|
data = read;
|
||||||
|
}
|
||||||
|
else if constexpr (T <= 32)
|
||||||
|
{
|
||||||
|
uint32_t read;
|
||||||
|
load(read);
|
||||||
|
data = read;
|
||||||
|
}
|
||||||
|
else if constexpr (T <= 64)
|
||||||
|
{
|
||||||
|
uint64_t read;
|
||||||
|
load(read);
|
||||||
|
data = read;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CLoadFile : public IBinaryReader
|
class DLL_LINKAGE CLoadFile : public IBinaryReader
|
||||||
|
@ -364,6 +364,26 @@ public:
|
|||||||
for(ui32 i = 0; i < length; i++)
|
for(ui32 i = 0; i < length; i++)
|
||||||
save(data.data()[i]);
|
save(data.data()[i]);
|
||||||
}
|
}
|
||||||
|
template <std::size_t T>
|
||||||
|
void save(const std::bitset<T> &data)
|
||||||
|
{
|
||||||
|
static_assert(T <= 64);
|
||||||
|
if constexpr (T <= 16)
|
||||||
|
{
|
||||||
|
auto writ = static_cast<uint16_t>(data.to_ulong());
|
||||||
|
save(writ);
|
||||||
|
}
|
||||||
|
else if constexpr (T <= 32)
|
||||||
|
{
|
||||||
|
auto writ = static_cast<uint32_t>(data.to_ulong());
|
||||||
|
save(writ);
|
||||||
|
}
|
||||||
|
else if constexpr (T <= 64)
|
||||||
|
{
|
||||||
|
auto writ = static_cast<uint64_t>(data.to_ulong());
|
||||||
|
save(writ);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CSaveFile : public IBinaryWriter
|
class DLL_LINKAGE CSaveFile : public IBinaryWriter
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
const ui32 SERIALIZATION_VERSION = 822;
|
const ui32 SERIALIZATION_VERSION = 823;
|
||||||
const ui32 MINIMAL_SERIALIZATION_VERSION = 822;
|
const ui32 MINIMAL_SERIALIZATION_VERSION = 823;
|
||||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
||||||
|
|
||||||
class CHero;
|
class CHero;
|
||||||
@ -54,7 +54,7 @@ struct VectorizedObjectInfo
|
|||||||
class DLL_LINKAGE CSerializer
|
class DLL_LINKAGE CSerializer
|
||||||
{
|
{
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static si32 idToNumber(const T &t, typename boost::enable_if<boost::is_convertible<T,si32> >::type * dummy = 0)
|
static si32 idToNumber(const T &t, typename std::enable_if<std::is_convertible<T,si32>::value>::type * dummy = 0)
|
||||||
{
|
{
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ int32_t AbilityCaster::getSpellSchoolLevel(const Spell * spell, int32_t * outSel
|
|||||||
|
|
||||||
if(spell->getLevel() > 0)
|
if(spell->getLevel() > 0)
|
||||||
{
|
{
|
||||||
vstd::amax(skill, unit->valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, 0));
|
vstd::amax(skill, unit->valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, SpellSchool(ESpellSchool::ANY)));
|
||||||
}
|
}
|
||||||
|
|
||||||
vstd::amax(skill, 0);
|
vstd::amax(skill, 0);
|
||||||
|
@ -38,44 +38,32 @@ namespace SpellConfig
|
|||||||
{
|
{
|
||||||
static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"};
|
static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"};
|
||||||
|
|
||||||
static const spells::SchoolInfo SCHOOL[4] =
|
const spells::SchoolInfo SCHOOL[4] =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
ESpellSchool::AIR,
|
ESpellSchool::AIR,
|
||||||
BonusType::AIR_SPELL_DMG_PREMY,
|
|
||||||
BonusType::AIR_IMMUNITY,
|
BonusType::AIR_IMMUNITY,
|
||||||
"air",
|
"air"
|
||||||
SecondarySkill::AIR_MAGIC,
|
|
||||||
BonusType::AIR_SPELLS
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ESpellSchool::FIRE,
|
ESpellSchool::FIRE,
|
||||||
BonusType::FIRE_SPELL_DMG_PREMY,
|
|
||||||
BonusType::FIRE_IMMUNITY,
|
BonusType::FIRE_IMMUNITY,
|
||||||
"fire",
|
"fire"
|
||||||
SecondarySkill::FIRE_MAGIC,
|
|
||||||
BonusType::FIRE_SPELLS
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ESpellSchool::WATER,
|
ESpellSchool::WATER,
|
||||||
BonusType::WATER_SPELL_DMG_PREMY,
|
|
||||||
BonusType::WATER_IMMUNITY,
|
BonusType::WATER_IMMUNITY,
|
||||||
"water",
|
"water"
|
||||||
SecondarySkill::WATER_MAGIC,
|
|
||||||
BonusType::WATER_SPELLS
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ESpellSchool::EARTH,
|
ESpellSchool::EARTH,
|
||||||
BonusType::EARTH_SPELL_DMG_PREMY,
|
|
||||||
BonusType::EARTH_IMMUNITY,
|
BonusType::EARTH_IMMUNITY,
|
||||||
"earth",
|
"earth"
|
||||||
SecondarySkill::EARTH_MAGIC,
|
|
||||||
BonusType::EARTH_SPELLS
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//order as described in http://bugs.vcmi.eu/view.php?id=91
|
//order as described in http://bugs.vcmi.eu/view.php?id=91
|
||||||
static const ESpellSchool SCHOOL_ORDER[4] =
|
static const SpellSchool SCHOOL_ORDER[4] =
|
||||||
{
|
{
|
||||||
ESpellSchool::AIR, //=0
|
ESpellSchool::AIR, //=0
|
||||||
ESpellSchool::FIRE, //=1
|
ESpellSchool::FIRE, //=1
|
||||||
@ -162,9 +150,9 @@ spells::AimType CSpell::getTargetType() const
|
|||||||
void CSpell::forEachSchool(const std::function<void(const spells::SchoolInfo &, bool &)>& cb) const
|
void CSpell::forEachSchool(const std::function<void(const spells::SchoolInfo &, bool &)>& cb) const
|
||||||
{
|
{
|
||||||
bool stop = false;
|
bool stop = false;
|
||||||
for(ESpellSchool iter : SpellConfig::SCHOOL_ORDER)
|
for(auto iter : SpellConfig::SCHOOL_ORDER)
|
||||||
{
|
{
|
||||||
const spells::SchoolInfo & cnf = SpellConfig::SCHOOL[static_cast<ui8>(iter)];
|
const spells::SchoolInfo & cnf = SpellConfig::SCHOOL[iter];
|
||||||
if(school.at(cnf.id))
|
if(school.at(cnf.id))
|
||||||
{
|
{
|
||||||
cb(cnf, stop);
|
cb(cnf, stop);
|
||||||
@ -393,15 +381,15 @@ int64_t CSpell::adjustRawDamage(const spells::Caster * caster, const battle::Uni
|
|||||||
//applying protections - when spell has more then one elements, only one protection should be applied (I think)
|
//applying protections - when spell has more then one elements, only one protection should be applied (I think)
|
||||||
forEachSchool([&](const spells::SchoolInfo & cnf, bool & stop)
|
forEachSchool([&](const spells::SchoolInfo & cnf, bool & stop)
|
||||||
{
|
{
|
||||||
if(bearer->hasBonusOfType(BonusType::SPELL_DAMAGE_REDUCTION, static_cast<ui8>(cnf.id)))
|
if(bearer->hasBonusOfType(BonusType::SPELL_DAMAGE_REDUCTION, cnf.id))
|
||||||
{
|
{
|
||||||
ret *= 100 - bearer->valOfBonuses(BonusType::SPELL_DAMAGE_REDUCTION, static_cast<ui8>(cnf.id));
|
ret *= 100 - bearer->valOfBonuses(BonusType::SPELL_DAMAGE_REDUCTION, cnf.id);
|
||||||
ret /= 100;
|
ret /= 100;
|
||||||
stop = true; //only bonus from one school is used
|
stop = true; //only bonus from one school is used
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
CSelector selector = Selector::type()(BonusType::SPELL_DAMAGE_REDUCTION).And(Selector::subtype()(-1));
|
CSelector selector = Selector::typeSubtype(BonusType::SPELL_DAMAGE_REDUCTION, SpellSchool(ESpellSchool::ANY));
|
||||||
|
|
||||||
//general spell dmg reduction, works only on magical effects
|
//general spell dmg reduction, works only on magical effects
|
||||||
if(bearer->hasBonus(selector) && isMagical())
|
if(bearer->hasBonus(selector) && isMagical())
|
||||||
|
@ -43,16 +43,18 @@ class IBattleCast;
|
|||||||
|
|
||||||
struct SchoolInfo
|
struct SchoolInfo
|
||||||
{
|
{
|
||||||
ESpellSchool id; //backlink
|
SpellSchool id; //backlink
|
||||||
BonusType damagePremyBonus;
|
|
||||||
BonusType immunityBonus;
|
BonusType immunityBonus;
|
||||||
std::string jsonName;
|
std::string jsonName;
|
||||||
SecondarySkill::ESecondarySkill skill;
|
|
||||||
BonusType knoledgeBonus;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace SpellConfig
|
||||||
|
{
|
||||||
|
extern const spells::SchoolInfo SCHOOL[4];
|
||||||
|
}
|
||||||
|
|
||||||
enum class VerticalPosition : ui8{TOP, CENTER, BOTTOM};
|
enum class VerticalPosition : ui8{TOP, CENTER, BOTTOM};
|
||||||
|
|
||||||
class DLL_LINKAGE CSpell : public spells::Spell
|
class DLL_LINKAGE CSpell : public spells::Spell
|
||||||
@ -188,7 +190,7 @@ public:
|
|||||||
|
|
||||||
si32 level;
|
si32 level;
|
||||||
|
|
||||||
std::map<ESpellSchool, bool> school;
|
std::map<SpellSchool, bool> school;
|
||||||
|
|
||||||
si32 power; //spell's power
|
si32 power; //spell's power
|
||||||
|
|
||||||
|
@ -89,6 +89,18 @@ private:
|
|||||||
si32 maxVal = std::numeric_limits<si32>::max();
|
si32 maxVal = std::numeric_limits<si32>::max();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ResistanceCondition : public TargetConditionItemBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
bool check(const Mechanics * m, const battle::Unit * target) const override
|
||||||
|
{
|
||||||
|
if(m->isPositiveSpell()) //Always pass on positive
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return target->magicResistance() < 100;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class CreatureCondition : public TargetConditionItemBase
|
class CreatureCondition : public TargetConditionItemBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -279,6 +291,10 @@ protected:
|
|||||||
{
|
{
|
||||||
const bool battleWideNegation = target->hasBonusOfType(BonusType::NEGATE_ALL_NATURAL_IMMUNITIES, 0);
|
const bool battleWideNegation = target->hasBonusOfType(BonusType::NEGATE_ALL_NATURAL_IMMUNITIES, 0);
|
||||||
const bool heroNegation = target->hasBonusOfType(BonusType::NEGATE_ALL_NATURAL_IMMUNITIES, 1);
|
const bool heroNegation = target->hasBonusOfType(BonusType::NEGATE_ALL_NATURAL_IMMUNITIES, 1);
|
||||||
|
//Non-magical effects is not affected by orb of vulnerability
|
||||||
|
if(!m->isMagicalEffect())
|
||||||
|
return false;
|
||||||
|
|
||||||
//anyone can cast on artifact holder`s stacks
|
//anyone can cast on artifact holder`s stacks
|
||||||
if(heroNegation)
|
if(heroNegation)
|
||||||
{
|
{
|
||||||
@ -315,6 +331,12 @@ public:
|
|||||||
return elementalCondition;
|
return elementalCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object createResistance() const override
|
||||||
|
{
|
||||||
|
static auto elementalCondition = std::make_shared<ResistanceCondition>();
|
||||||
|
return elementalCondition;
|
||||||
|
}
|
||||||
|
|
||||||
Object createNormalLevel() const override
|
Object createNormalLevel() const override
|
||||||
{
|
{
|
||||||
static std::shared_ptr<TargetConditionItem> nlCondition = std::make_shared<NormalLevelCondition>();
|
static std::shared_ptr<TargetConditionItem> nlCondition = std::make_shared<NormalLevelCondition>();
|
||||||
@ -340,8 +362,8 @@ public:
|
|||||||
auto params = BonusParams(identifier, "", -1);
|
auto params = BonusParams(identifier, "", -1);
|
||||||
if(params.isConverted)
|
if(params.isConverted)
|
||||||
{
|
{
|
||||||
if(params.valRelevant)
|
if(params.val)
|
||||||
return std::make_shared<SelectorCondition>(params.toSelector(), params.val, params.val);
|
return std::make_shared<SelectorCondition>(params.toSelector(), *params.val, *params.val);
|
||||||
return std::make_shared<SelectorCondition>(params.toSelector());
|
return std::make_shared<SelectorCondition>(params.toSelector());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,6 +469,7 @@ void TargetCondition::serializeJson(JsonSerializeFormat & handler, const ItemFac
|
|||||||
absolute.push_back(itemFactory->createAbsoluteSpell());
|
absolute.push_back(itemFactory->createAbsoluteSpell());
|
||||||
absolute.push_back(itemFactory->createAbsoluteLevel());
|
absolute.push_back(itemFactory->createAbsoluteLevel());
|
||||||
normal.push_back(itemFactory->createElemental());
|
normal.push_back(itemFactory->createElemental());
|
||||||
|
normal.push_back(itemFactory->createResistance());
|
||||||
normal.push_back(itemFactory->createNormalLevel());
|
normal.push_back(itemFactory->createNormalLevel());
|
||||||
normal.push_back(itemFactory->createNormalSpell());
|
normal.push_back(itemFactory->createNormalSpell());
|
||||||
negation.push_back(itemFactory->createReceptiveFeature());
|
negation.push_back(itemFactory->createReceptiveFeature());
|
||||||
|
@ -49,6 +49,7 @@ public:
|
|||||||
virtual Object createElemental() const = 0;
|
virtual Object createElemental() const = 0;
|
||||||
virtual Object createNormalLevel() const = 0;
|
virtual Object createNormalLevel() const = 0;
|
||||||
virtual Object createNormalSpell() const = 0;
|
virtual Object createNormalSpell() const = 0;
|
||||||
|
virtual Object createResistance() const = 0;
|
||||||
|
|
||||||
virtual Object createConfigurable(std::string scope, std::string type, std::string identifier) const = 0;
|
virtual Object createConfigurable(std::string scope, std::string type, std::string identifier) const = 0;
|
||||||
virtual Object createFromJsonStruct(const JsonNode & jsonStruct) const = 0;
|
virtual Object createFromJsonStruct(const JsonNode & jsonStruct) const = 0;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "Damage.h"
|
#include "Damage.h"
|
||||||
#include "Registry.h"
|
#include "Registry.h"
|
||||||
|
#include "../CSpellHandler.h"
|
||||||
#include "../ISpellMechanics.h"
|
#include "../ISpellMechanics.h"
|
||||||
|
|
||||||
#include "../../NetPacks.h"
|
#include "../../NetPacks.h"
|
||||||
@ -20,6 +21,8 @@
|
|||||||
#include "../../CGeneralTextHandler.h"
|
#include "../../CGeneralTextHandler.h"
|
||||||
#include "../../serializer/JsonSerializeFormat.h"
|
#include "../../serializer/JsonSerializeFormat.h"
|
||||||
|
|
||||||
|
#include <vcmi/spells/Spell.h>
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
namespace spells
|
namespace spells
|
||||||
@ -82,16 +85,14 @@ bool Damage::isReceptive(const Mechanics * m, const battle::Unit * unit) const
|
|||||||
if(!UnitEffect::isReceptive(m, unit))
|
if(!UnitEffect::isReceptive(m, unit))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
bool isImmune = m->getSpell()->isMagical() && (unit->getBonusBearer()->valOfBonuses(BonusType::SPELL_DAMAGE_REDUCTION, SpellSchool(ESpellSchool::ANY)) >= 100); //General spell damage immunity
|
||||||
//elemental immunity for damage
|
//elemental immunity for damage
|
||||||
auto filter = m->getElementalImmunity();
|
m->getSpell()->forEachSchool([&](const SchoolInfo & cnf, bool & stop)
|
||||||
|
|
||||||
for(auto element : filter)
|
|
||||||
{
|
{
|
||||||
if(!m->isPositiveSpell() && unit->hasBonusOfType(element, 2))
|
isImmune |= (unit->getBonusBearer()->valOfBonuses(BonusType::SPELL_DAMAGE_REDUCTION, cnf.id) >= 100); //100% reduction is immunity
|
||||||
return false;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return !isImmune;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Damage::serializeJsonUnitEffect(JsonSerializeFormat & handler)
|
void Damage::serializeJsonUnitEffect(JsonSerializeFormat & handler)
|
||||||
|
@ -351,6 +351,14 @@ public:
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<std::size_t T>
|
||||||
|
static int quickRetInt(lua_State * L, const std::bitset<T> & value)
|
||||||
|
{
|
||||||
|
lua_settop(L, 0);
|
||||||
|
lua_pushinteger(L, static_cast<int32_t>(value.to_ulong()));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int quickRetStr(lua_State * L, const std::string & value)
|
static int quickRetStr(lua_State * L, const std::string & value)
|
||||||
{
|
{
|
||||||
lua_settop(L, 0);
|
lua_settop(L, 0);
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
#include "BonusSystem.h"
|
#include "BonusSystem.h"
|
||||||
|
|
||||||
|
#include "../../../lib/JsonNode.h"
|
||||||
|
|
||||||
#include "../../../lib/bonuses/BonusList.h"
|
#include "../../../lib/bonuses/BonusList.h"
|
||||||
#include "../../../lib/bonuses/Bonus.h"
|
#include "../../../lib/bonuses/Bonus.h"
|
||||||
#include "../../../lib/bonuses/IBonusBearer.h"
|
#include "../../../lib/bonuses/IBonusBearer.h"
|
||||||
@ -155,8 +157,8 @@ int BonusProxy::toJsonNode(lua_State * L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T, typename N>
|
||||||
static void publishMap(lua_State * L, const T & map)
|
static void publishMap(lua_State * L, const std::map<T , N> & map)
|
||||||
{
|
{
|
||||||
for(auto & p : map)
|
for(auto & p : map)
|
||||||
{
|
{
|
||||||
@ -169,6 +171,20 @@ static void publishMap(lua_State * L, const T & map)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
static void publishMap(lua_State * L, const std::map<T , std::bitset<N>> & map)
|
||||||
|
{
|
||||||
|
for(auto & p : map)
|
||||||
|
{
|
||||||
|
const std::string & name = p.first;
|
||||||
|
auto id = static_cast<int32_t>(p.second.to_ulong());
|
||||||
|
|
||||||
|
lua_pushstring(L, name.c_str());
|
||||||
|
lua_pushinteger(L, id);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BonusProxy::adjustStaticTable(lua_State * L) const
|
void BonusProxy::adjustStaticTable(lua_State * L) const
|
||||||
{
|
{
|
||||||
publishMap(L, bonusNameMap);
|
publishMap(L, bonusNameMap);
|
||||||
|
@ -67,6 +67,7 @@ set(test_SRCS
|
|||||||
spells/targetConditions/NormalLevelConditionTest.cpp
|
spells/targetConditions/NormalLevelConditionTest.cpp
|
||||||
spells/targetConditions/NormalSpellConditionTest.cpp
|
spells/targetConditions/NormalSpellConditionTest.cpp
|
||||||
spells/targetConditions/ReceptiveFeatureConditionTest.cpp
|
spells/targetConditions/ReceptiveFeatureConditionTest.cpp
|
||||||
|
spells/targetConditions/ResistanceConditionTest.cpp
|
||||||
spells/targetConditions/SpellEffectConditionTest.cpp
|
spells/targetConditions/SpellEffectConditionTest.cpp
|
||||||
spells/targetConditions/TargetConditionItemFixture.cpp
|
spells/targetConditions/TargetConditionItemFixture.cpp
|
||||||
|
|
||||||
|
@ -33,6 +33,9 @@ public:
|
|||||||
MOCK_CONST_METHOD0(manaLimit, int32_t());
|
MOCK_CONST_METHOD0(manaLimit, int32_t());
|
||||||
MOCK_CONST_METHOD0(getHeroCaster, CGHeroInstance*());
|
MOCK_CONST_METHOD0(getHeroCaster, CGHeroInstance*());
|
||||||
|
|
||||||
|
//ACreature
|
||||||
|
MOCK_CONST_METHOD0(magicResistance, int32_t());
|
||||||
|
|
||||||
MOCK_CONST_METHOD0(unitBaseAmount, int32_t());
|
MOCK_CONST_METHOD0(unitBaseAmount, int32_t());
|
||||||
MOCK_CONST_METHOD0(unitId, uint32_t());
|
MOCK_CONST_METHOD0(unitId, uint32_t());
|
||||||
MOCK_CONST_METHOD0(unitSide, ui8());
|
MOCK_CONST_METHOD0(unitSide, ui8());
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vcmi/spells/Spell.h>
|
#include <vcmi/spells/Spell.h>
|
||||||
|
#include "GameConstants.h"
|
||||||
|
|
||||||
namespace spells
|
namespace spells
|
||||||
{
|
{
|
||||||
|
@ -56,7 +56,7 @@ TEST_F(AbilityCasterTest, MagicAbilityAffectedByGenericBonus)
|
|||||||
{
|
{
|
||||||
EXPECT_CALL(spellMock, getLevel()).WillRepeatedly(Return(1));
|
EXPECT_CALL(spellMock, getLevel()).WillRepeatedly(Return(1));
|
||||||
|
|
||||||
casterBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::MAGIC_SCHOOL_SKILL, BonusSource::OTHER, 2, 0, 0));
|
casterBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::MAGIC_SCHOOL_SKILL, BonusSource::OTHER, 2, 0, SpellSchool(ESpellSchool::ANY)));
|
||||||
|
|
||||||
EXPECT_CALL(actualCaster, getAllBonuses(_, _, _, _)).Times(AtLeast(1));
|
EXPECT_CALL(actualCaster, getAllBonuses(_, _, _, _)).Times(AtLeast(1));
|
||||||
EXPECT_CALL(actualCaster, getTreeVersion()).Times(AtLeast(0));
|
EXPECT_CALL(actualCaster, getTreeVersion()).Times(AtLeast(0));
|
||||||
@ -70,7 +70,7 @@ TEST_F(AbilityCasterTest, MagicAbilityIngoresSchoolBonus)
|
|||||||
{
|
{
|
||||||
EXPECT_CALL(spellMock, getLevel()).WillRepeatedly(Return(1));
|
EXPECT_CALL(spellMock, getLevel()).WillRepeatedly(Return(1));
|
||||||
|
|
||||||
casterBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::MAGIC_SCHOOL_SKILL, BonusSource::OTHER, 2, 0, 1));
|
casterBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::MAGIC_SCHOOL_SKILL, BonusSource::OTHER, 2, 0, SpellSchool(ESpellSchool::AIR)));
|
||||||
|
|
||||||
EXPECT_CALL(actualCaster, getAllBonuses(_, _, _, _)).Times(AtLeast(1));
|
EXPECT_CALL(actualCaster, getAllBonuses(_, _, _, _)).Times(AtLeast(1));
|
||||||
EXPECT_CALL(actualCaster, getTreeVersion()).Times(AtLeast(0));
|
EXPECT_CALL(actualCaster, getTreeVersion()).Times(AtLeast(0));
|
||||||
|
@ -41,6 +41,7 @@ public:
|
|||||||
MOCK_CONST_METHOD0(createAbsoluteLevel, Object());
|
MOCK_CONST_METHOD0(createAbsoluteLevel, Object());
|
||||||
MOCK_CONST_METHOD0(createAbsoluteSpell, Object());
|
MOCK_CONST_METHOD0(createAbsoluteSpell, Object());
|
||||||
MOCK_CONST_METHOD0(createElemental, Object());
|
MOCK_CONST_METHOD0(createElemental, Object());
|
||||||
|
MOCK_CONST_METHOD0(createResistance, Object());
|
||||||
MOCK_CONST_METHOD0(createNormalLevel, Object());
|
MOCK_CONST_METHOD0(createNormalLevel, Object());
|
||||||
MOCK_CONST_METHOD0(createNormalSpell, Object());
|
MOCK_CONST_METHOD0(createNormalSpell, Object());
|
||||||
MOCK_CONST_METHOD1(createFromJsonStruct, Object(const JsonNode &));
|
MOCK_CONST_METHOD1(createFromJsonStruct, Object(const JsonNode &));
|
||||||
@ -74,6 +75,7 @@ public:
|
|||||||
ON_CALL(factoryMock, createAbsoluteLevel()).WillByDefault(Return(itemStub));
|
ON_CALL(factoryMock, createAbsoluteLevel()).WillByDefault(Return(itemStub));
|
||||||
ON_CALL(factoryMock, createAbsoluteSpell()).WillByDefault(Return(itemStub));
|
ON_CALL(factoryMock, createAbsoluteSpell()).WillByDefault(Return(itemStub));
|
||||||
ON_CALL(factoryMock, createElemental()).WillByDefault(Return(itemStub));
|
ON_CALL(factoryMock, createElemental()).WillByDefault(Return(itemStub));
|
||||||
|
ON_CALL(factoryMock, createResistance()).WillByDefault(Return(itemStub));
|
||||||
ON_CALL(factoryMock, createNormalLevel()).WillByDefault(Return(itemStub));
|
ON_CALL(factoryMock, createNormalLevel()).WillByDefault(Return(itemStub));
|
||||||
ON_CALL(factoryMock, createNormalSpell()).WillByDefault(Return(itemStub));
|
ON_CALL(factoryMock, createNormalSpell()).WillByDefault(Return(itemStub));
|
||||||
|
|
||||||
@ -139,6 +141,7 @@ TEST_F(TargetConditionTest, CreatesSpecialConditions)
|
|||||||
EXPECT_CALL(factoryMock, createAbsoluteLevel()).Times(1);
|
EXPECT_CALL(factoryMock, createAbsoluteLevel()).Times(1);
|
||||||
EXPECT_CALL(factoryMock, createAbsoluteSpell()).Times(1);
|
EXPECT_CALL(factoryMock, createAbsoluteSpell()).Times(1);
|
||||||
EXPECT_CALL(factoryMock, createElemental()).Times(1);
|
EXPECT_CALL(factoryMock, createElemental()).Times(1);
|
||||||
|
EXPECT_CALL(factoryMock, createResistance()).Times(1);
|
||||||
EXPECT_CALL(factoryMock, createNormalLevel()).Times(1);
|
EXPECT_CALL(factoryMock, createNormalLevel()).Times(1);
|
||||||
EXPECT_CALL(factoryMock, createNormalSpell()).Times(1);
|
EXPECT_CALL(factoryMock, createNormalSpell()).Times(1);
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ TEST_F(BonusConditionTest, ImmuneByDefault)
|
|||||||
TEST_F(BonusConditionTest, ReceptiveIfMatchesType)
|
TEST_F(BonusConditionTest, ReceptiveIfMatchesType)
|
||||||
{
|
{
|
||||||
setDefaultExpectations();
|
setDefaultExpectations();
|
||||||
unitBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::DIRECT_DAMAGE_IMMUNITY, BonusSource::OTHER, 0, 0));
|
unitBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::SPELL_DAMAGE_REDUCTION, BonusSource::OTHER, 100, 0));
|
||||||
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,16 +20,19 @@ namespace test
|
|||||||
using namespace ::spells;
|
using namespace ::spells;
|
||||||
using namespace ::testing;
|
using namespace ::testing;
|
||||||
|
|
||||||
class ImmunityNegationConditionTest : public TargetConditionItemTest, public WithParamInterface<bool>
|
class ImmunityNegationConditionTest : public TargetConditionItemTest, public WithParamInterface<std::tuple<bool, bool>>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool ownerMatches;
|
bool ownerMatches;
|
||||||
|
bool isMagicalEffect;
|
||||||
|
|
||||||
void setDefaultExpectations()
|
void setDefaultExpectations()
|
||||||
{
|
{
|
||||||
ownerMatches = GetParam();
|
ownerMatches = ::testing::get<0>(GetParam());
|
||||||
|
isMagicalEffect = ::testing::get<1>(GetParam());
|
||||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _, _)).Times(AtLeast(0));
|
EXPECT_CALL(unitMock, getAllBonuses(_, _, _, _)).Times(AtLeast(0));
|
||||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
||||||
|
EXPECT_CALL(mechanicsMock, isMagicalEffect()).Times(AtLeast(0)).WillRepeatedly(Return(isMagicalEffect));
|
||||||
EXPECT_CALL(mechanicsMock, ownerMatches(Eq(&unitMock), Field(&boost::logic::tribool::value, boost::logic::tribool::false_value))).WillRepeatedly(Return(ownerMatches));
|
EXPECT_CALL(mechanicsMock, ownerMatches(Eq(&unitMock), Field(&boost::logic::tribool::value, boost::logic::tribool::false_value))).WillRepeatedly(Return(ownerMatches));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +59,7 @@ TEST_P(ImmunityNegationConditionTest, WithHeroNegation)
|
|||||||
|
|
||||||
unitBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::NEGATE_ALL_NATURAL_IMMUNITIES, BonusSource::OTHER, 0, 0, 1));
|
unitBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::NEGATE_ALL_NATURAL_IMMUNITIES, BonusSource::OTHER, 0, 0, 1));
|
||||||
|
|
||||||
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
EXPECT_EQ(isMagicalEffect, subject->isReceptive(&mechanicsMock, &unitMock));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ImmunityNegationConditionTest, WithBattleWideNegation)
|
TEST_P(ImmunityNegationConditionTest, WithBattleWideNegation)
|
||||||
@ -66,14 +69,18 @@ TEST_P(ImmunityNegationConditionTest, WithBattleWideNegation)
|
|||||||
unitBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::NEGATE_ALL_NATURAL_IMMUNITIES, BonusSource::OTHER, 0, 0, 0));
|
unitBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::NEGATE_ALL_NATURAL_IMMUNITIES, BonusSource::OTHER, 0, 0, 0));
|
||||||
|
|
||||||
//This should return if ownerMatches, because anyone should cast onto owner's stacks, but not on enemyStacks
|
//This should return if ownerMatches, because anyone should cast onto owner's stacks, but not on enemyStacks
|
||||||
EXPECT_EQ(ownerMatches, subject->isReceptive(&mechanicsMock, &unitMock));
|
EXPECT_EQ(ownerMatches && isMagicalEffect, subject->isReceptive(&mechanicsMock, &unitMock));
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P
|
INSTANTIATE_TEST_SUITE_P
|
||||||
(
|
(
|
||||||
ByUnitOwner,
|
ByUnitOwner,
|
||||||
ImmunityNegationConditionTest,
|
ImmunityNegationConditionTest,
|
||||||
|
Combine
|
||||||
|
(
|
||||||
|
Values(false, true),
|
||||||
Values(false, true)
|
Values(false, true)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,16 @@ using namespace ::spells;
|
|||||||
using namespace ::testing;
|
using namespace ::testing;
|
||||||
|
|
||||||
|
|
||||||
class NormalLevelConditionTest : public TargetConditionItemTest
|
class NormalLevelConditionTest : public TargetConditionItemTest, public WithParamInterface<bool>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
bool isMagicalEffect;
|
||||||
|
|
||||||
void setDefaultExpectations()
|
void setDefaultExpectations()
|
||||||
{
|
{
|
||||||
EXPECT_CALL(mechanicsMock, isMagicalEffect()).WillRepeatedly(Return(true));
|
isMagicalEffect = GetParam();
|
||||||
|
EXPECT_CALL(mechanicsMock, isMagicalEffect()).WillRepeatedly(Return(isMagicalEffect));
|
||||||
|
if(isMagicalEffect)
|
||||||
EXPECT_CALL(unitMock, getAllBonuses(_, _, _, _)).Times(AtLeast(1));
|
EXPECT_CALL(unitMock, getAllBonuses(_, _, _, _)).Times(AtLeast(1));
|
||||||
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
||||||
}
|
}
|
||||||
@ -36,43 +39,53 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(NormalLevelConditionTest, DefaultForAbility)
|
TEST_P(NormalLevelConditionTest, DefaultForAbility)
|
||||||
{
|
{
|
||||||
setDefaultExpectations();
|
setDefaultExpectations();
|
||||||
EXPECT_CALL(mechanicsMock, getSpellLevel()).WillRepeatedly(Return(0));
|
EXPECT_CALL(mechanicsMock, getSpellLevel()).WillRepeatedly(Return(0));
|
||||||
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(NormalLevelConditionTest, DefaultForNormal)
|
TEST_P(NormalLevelConditionTest, DefaultForNormal)
|
||||||
{
|
{
|
||||||
setDefaultExpectations();
|
setDefaultExpectations();
|
||||||
EXPECT_CALL(mechanicsMock, getSpellLevel()).WillRepeatedly(Return(1));
|
EXPECT_CALL(mechanicsMock, getSpellLevel()).WillRepeatedly(Return(1));
|
||||||
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(NormalLevelConditionTest, ReceptiveNormal)
|
TEST_P(NormalLevelConditionTest, ReceptiveNormal)
|
||||||
{
|
{
|
||||||
setDefaultExpectations();
|
setDefaultExpectations();
|
||||||
unitBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::LEVEL_SPELL_IMMUNITY, BonusSource::OTHER, 3, 0));
|
unitBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::LEVEL_SPELL_IMMUNITY, BonusSource::OTHER, 3, 0));
|
||||||
|
if(isMagicalEffect)
|
||||||
EXPECT_CALL(mechanicsMock, getSpellLevel()).Times(AtLeast(1)).WillRepeatedly(Return(4));
|
EXPECT_CALL(mechanicsMock, getSpellLevel()).Times(AtLeast(1)).WillRepeatedly(Return(4));
|
||||||
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: this tests covers fact that creature abilities ignored (by spell level == 0), should this be done by ability flag or by cast mode?
|
//TODO: this tests covers fact that creature abilities ignored (by spell level == 0), should this be done by ability flag or by cast mode?
|
||||||
TEST_F(NormalLevelConditionTest, ReceptiveAbility)
|
TEST_P(NormalLevelConditionTest, ReceptiveAbility)
|
||||||
{
|
{
|
||||||
setDefaultExpectations();
|
setDefaultExpectations();
|
||||||
unitBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::LEVEL_SPELL_IMMUNITY, BonusSource::OTHER, 5, 0));
|
unitBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::LEVEL_SPELL_IMMUNITY, BonusSource::OTHER, 5, 0));
|
||||||
|
if(isMagicalEffect)
|
||||||
EXPECT_CALL(mechanicsMock, getSpellLevel()).Times(AtLeast(1)).WillRepeatedly(Return(0));
|
EXPECT_CALL(mechanicsMock, getSpellLevel()).Times(AtLeast(1)).WillRepeatedly(Return(0));
|
||||||
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(NormalLevelConditionTest, ImmuneNormal)
|
TEST_P(NormalLevelConditionTest, ImmuneNormal)
|
||||||
{
|
{
|
||||||
setDefaultExpectations();
|
setDefaultExpectations();
|
||||||
unitBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::LEVEL_SPELL_IMMUNITY, BonusSource::OTHER, 4, 0));
|
unitBonuses.addNewBonus(std::make_shared<Bonus>(BonusDuration::ONE_BATTLE, BonusType::LEVEL_SPELL_IMMUNITY, BonusSource::OTHER, 4, 0));
|
||||||
|
if(isMagicalEffect)
|
||||||
EXPECT_CALL(mechanicsMock, getSpellLevel()).Times(AtLeast(1)).WillRepeatedly(Return(2));
|
EXPECT_CALL(mechanicsMock, getSpellLevel()).Times(AtLeast(1)).WillRepeatedly(Return(2));
|
||||||
EXPECT_FALSE(subject->isReceptive(&mechanicsMock, &unitMock));
|
EXPECT_EQ(!isMagicalEffect, subject->isReceptive(&mechanicsMock, &unitMock));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P
|
||||||
|
(
|
||||||
|
ByMagicalEffect,
|
||||||
|
NormalLevelConditionTest,
|
||||||
|
Values(false, true)
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
72
test/spells/targetConditions/ResistanceConditionTest.cpp
Normal file
72
test/spells/targetConditions/ResistanceConditionTest.cpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* ElementalConditionTest.cpp, part of VCMI engine
|
||||||
|
*
|
||||||
|
* Authors: listed in file AUTHORS in main folder
|
||||||
|
*
|
||||||
|
* License: GNU General Public License v2.0 or later
|
||||||
|
* Full text of license available in license.txt file, in main folder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "StdInc.h"
|
||||||
|
|
||||||
|
#include "TargetConditionItemFixture.h"
|
||||||
|
|
||||||
|
namespace test
|
||||||
|
{
|
||||||
|
using namespace ::spells;
|
||||||
|
using namespace ::testing;
|
||||||
|
|
||||||
|
class ResistanceConditionTest : public TargetConditionItemTest, public WithParamInterface<bool>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool isPositive;
|
||||||
|
void setDefaultExpectations()
|
||||||
|
{
|
||||||
|
EXPECT_CALL(unitMock, getTreeVersion()).Times(AtLeast(0));
|
||||||
|
EXPECT_CALL(mechanicsMock, isPositiveSpell()).WillRepeatedly(Return(isPositive));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp() override
|
||||||
|
{
|
||||||
|
TargetConditionItemTest::SetUp();
|
||||||
|
subject = TargetConditionItemFactory::getDefault()->createResistance();
|
||||||
|
|
||||||
|
isPositive = GetParam();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_P(ResistanceConditionTest, ReceptiveIfNoBonus)
|
||||||
|
{
|
||||||
|
setDefaultExpectations();
|
||||||
|
|
||||||
|
EXPECT_CALL(unitMock, magicResistance()).Times(AtLeast(0)).WillRepeatedly(Return(0));
|
||||||
|
|
||||||
|
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ResistanceConditionTest, DependsOnPositivness)
|
||||||
|
{
|
||||||
|
setDefaultExpectations();
|
||||||
|
|
||||||
|
EXPECT_CALL(unitMock, magicResistance()).Times(AtLeast(0)).WillRepeatedly(Return(100));
|
||||||
|
|
||||||
|
EXPECT_EQ(isPositive, subject->isReceptive(&mechanicsMock, &unitMock));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ResistanceConditionTest, ReceptiveIfResistanceIsLessThanHundred)
|
||||||
|
{
|
||||||
|
setDefaultExpectations();
|
||||||
|
|
||||||
|
EXPECT_CALL(unitMock, magicResistance()).Times(AtLeast(0)).WillRepeatedly(Return(99));
|
||||||
|
|
||||||
|
EXPECT_TRUE(subject->isReceptive(&mechanicsMock, &unitMock));
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P
|
||||||
|
(
|
||||||
|
ByPositiveness,
|
||||||
|
ResistanceConditionTest,
|
||||||
|
Values(false, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user