1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-23 22:37:55 +02:00

some changes towards editing H3 objects via mods. Should be stable, report if not.

- removed duplicated json loading code in handlers
- simpler and mod-friendly handling of combined artifacts
- reorganized CCreature to avoid huge number of fields in one structure
This commit is contained in:
Ivan Savenko
2013-03-02 16:55:51 +00:00
parent cc2391d641
commit f306d7bb70
17 changed files with 565 additions and 729 deletions

View File

@@ -208,6 +208,50 @@ static void RemoveAbility(CCreature *cre, const JsonNode &ability)
cre->removeBonus(b);
}
void CCreatureHandler::loadBonuses(CCreature & ncre, std::string bonuses)
{
static const std::map<std::string,Bonus::BonusType> abilityMap =
boost::assign::map_list_of
("FLYING_ARMY", Bonus::FLYING)
("SHOOTING_ARMY", Bonus::SHOOTER)
("SIEGE_WEAPON", Bonus::SIEGE_WEAPON)
("const_free_attack", Bonus::BLOCKS_RETALIATION)
("IS_UNDEAD", Bonus::UNDEAD)
("const_no_melee_penalty",Bonus::NO_MELEE_PENALTY)
("const_jousting",Bonus::JOUSTING)
("KING_1",Bonus::KING1)
("KING_2",Bonus::KING2)
("KING_3",Bonus::KING3)
("const_no_wall_penalty",Bonus::NO_WALL_PENALTY)
("CATAPULT",Bonus::CATAPULT)
("MULTI_HEADED",Bonus::ATTACKS_ALL_ADJACENT)
("IMMUNE_TO_MIND_SPELLS",Bonus::MIND_IMMUNITY)
("IMMUNE_TO_FIRE_SPELLS",Bonus::FIRE_IMMUNITY)
("HAS_EXTENDED_ATTACK",Bonus::TWO_HEX_ATTACK_BREATH);
auto hasAbility = [&](const std::string name) -> bool
{
return boost::algorithm::find_first(bonuses, name);
};
BOOST_FOREACH(auto a, abilityMap)
{
if(hasAbility(a.first))
ncre.addBonus(0, a.second);
}
if(hasAbility("DOUBLE_WIDE"))
ncre.doubleWide = true;
if(hasAbility("const_raises_morale"))
{
ncre.addBonus(+1, Bonus::MORALE);;
ncre.getBonusList().back()->addPropagator(make_shared<CPropagatorNodeType>(CBonusSystemNode::HERO));
}
if(hasAbility("const_lowers_morale"))
{
ncre.addBonus(-1, Bonus::MORALE);;
ncre.getBonusList().back()->effectRange = Bonus::ONLY_ENEMY_ARMY;
}
}
void CCreatureHandler::loadCreatures()
{
tlog5 << "\t\tReading ZCRTRAIT.TXT" << std::endl;
@@ -255,8 +299,8 @@ void CCreatureHandler::loadCreatures()
ncre.ammMin = parser.readNumber();
ncre.ammMax = parser.readNumber();
ncre.abilityText = parser.readString();
ncre.abilityRefs = parser.readString();
std::string abilities = parser.readString();
loadBonuses(ncre, parser.readString());
{ //adding abilities from ZCRTRAIT.TXT
static const std::map < std::string,Bonus::BonusType> abilityMap = boost::assign::map_list_of
@@ -277,11 +321,11 @@ void CCreatureHandler::loadCreatures()
("IMMUNE_TO_FIRE_SPELLS",Bonus::FIRE_IMMUNITY)
("IMMUNE_TO_FIRE_SPELLS",Bonus::FIRE_IMMUNITY)
("HAS_EXTENDED_ATTACK",Bonus::TWO_HEX_ATTACK_BREATH);
auto hasAbility = [&ncre](const std::string name) -> bool
auto hasAbility = [&](const std::string name) -> bool
{
return boost::algorithm::find_first(ncre.abilityRefs,name);
};
return boost::algorithm::find_first(abilities, name);
};
BOOST_FOREACH(auto a, abilityMap)
{
if(hasAbility(a.first))
@@ -315,18 +359,6 @@ void CCreatureHandler::loadCreatures()
int creatureID = node.second["id"].Float();
CCreature *c = creatures[creatureID];
BOOST_FOREACH(const JsonNode &ability, node.second["ability_remove"].Vector())
{
RemoveAbility(c, ability);
}
BOOST_FOREACH(const JsonNode &ability, node.second["abilities"].Vector())
{
if (ability.getType() == JsonNode::DATA_VECTOR)
AddAbility(c, ability.Vector());
else
c->addNewBonus(JsonUtils::parseBonus(ability));
}
loadCreatureJson(c, node.second);
// Main reference name, e.g. royalGriffin
@@ -508,47 +540,41 @@ void CCreatureHandler::loadAnimationInfo()
void CCreatureHandler::loadUnitAnimInfo(CCreature & unit, CLegacyConfigParser & parser)
{
unit.timeBetweenFidgets = parser.readNumber();
unit.walkAnimationTime = parser.readNumber();
unit.attackAnimationTime = parser.readNumber();
unit.flightAnimationDistance = parser.readNumber();
unit.animation.timeBetweenFidgets = parser.readNumber();
unit.animation.walkAnimationTime = parser.readNumber();
unit.animation.attackAnimationTime = parser.readNumber();
unit.animation.flightAnimationDistance = parser.readNumber();
///////////////////////
unit.upperRightMissleOffsetX = parser.readNumber();
unit.upperRightMissleOffsetY = parser.readNumber();
unit.rightMissleOffsetX = parser.readNumber();
unit.rightMissleOffsetY = parser.readNumber();
unit.lowerRightMissleOffsetX = parser.readNumber();
unit.lowerRightMissleOffsetY = parser.readNumber();
unit.animation.upperRightMissleOffsetX = parser.readNumber();
unit.animation.upperRightMissleOffsetY = parser.readNumber();
unit.animation.rightMissleOffsetX = parser.readNumber();
unit.animation.rightMissleOffsetY = parser.readNumber();
unit.animation.lowerRightMissleOffsetX = parser.readNumber();
unit.animation.lowerRightMissleOffsetY = parser.readNumber();
///////////////////////
for(int jjj=0; jjj<12; ++jjj)
{
unit.missleFrameAngles[jjj] = parser.readNumber();
unit.animation.missleFrameAngles[jjj] = parser.readNumber();
}
unit.troopCountLocationOffset= parser.readNumber();
unit.attackClimaxFrame = parser.readNumber();
unit.animation.troopCountLocationOffset= parser.readNumber();
unit.animation.attackClimaxFrame = parser.readNumber();
parser.endLine();
}
void CCreatureHandler::load(const JsonNode & node)
void CCreatureHandler::load(std::string creatureID, const JsonNode & node)
{
BOOST_FOREACH(auto & entry, node.Struct())
{
if (!entry.second.isNull()) // may happens if mod removed creature by setting json entry to null
{
CCreature * creature = loadCreature(entry.second);
creature->nameRef = entry.first;
creature->idNumber = CreatureID(creatures.size());
CCreature * creature = loadCreature(node);
creature->nameRef = creatureID;
creature->idNumber = CreatureID(creatures.size());
creatures.push_back(creature);
tlog5 << "Added creature: " << entry.first << "\n";
registerCreature(creature->nameRef,creature->idNumber);
}
}
creatures.push_back(creature);
tlog5 << "Added creature: " << creatureID << "\n";
registerCreature(creature->nameRef, creature->idNumber);
}
CCreature * CCreatureHandler::loadCreature(const JsonNode & node)
@@ -586,74 +612,31 @@ CCreature * CCreatureHandler::loadCreature(const JsonNode & node)
cre->doubleWide = node["doubleWide"].Bool();
BOOST_FOREACH (const JsonNode &bonus, node["abilities"].Vector())
{
auto b = JsonUtils::parseBonus(bonus);
b->source = Bonus::CREATURE_ABILITY;
b->duration = Bonus::PERMANENT;
cre->addNewBonus(b);
}
BOOST_FOREACH (const JsonNode &exp, node["stackExperience"].Vector())
{
auto bonus = JsonUtils::parseBonus (exp["bonus"]);
bonus->source = Bonus::STACK_EXPERIENCE;
bonus->duration = Bonus::PERMANENT;
const JsonVector &values = exp["values"].Vector();
int lowerLimit = 1;//, upperLimit = 255;
if (values[0].getType() == JsonNode::JsonType::DATA_BOOL)
{
BOOST_FOREACH (const JsonNode &val, values)
{
if (val.Bool() == true)
{
bonus->limiter = make_shared<RankRangeLimiter>(RankRangeLimiter(lowerLimit));
cre->addNewBonus (new Bonus(*bonus)); //bonuses must be unique objects
break; //TODO: allow bonuses to turn off?
}
++lowerLimit;
}
}
else
{
int lastVal = 0;
BOOST_FOREACH (const JsonNode &val, values)
{
if (val.Float() != lastVal)
{
bonus->val = val.Float() - lastVal;
bonus->limiter.reset (new RankRangeLimiter(lowerLimit));
cre->addNewBonus (new Bonus(*bonus));
}
lastVal = val.Float();
++lowerLimit;
}
}
}
//graphics
loadStackExperience(cre, node["stackExperience"]);
const JsonNode & graphics = node["graphics"];
cre->timeBetweenFidgets = graphics["timeBetweenFidgets"].Float();
cre->troopCountLocationOffset = graphics["troopCountLocationOffset"].Float();
cre->attackClimaxFrame = graphics["attackClimaxFrame"].Float();
cre->animation.timeBetweenFidgets = graphics["timeBetweenFidgets"].Float();
cre->animation.troopCountLocationOffset = graphics["troopCountLocationOffset"].Float();
cre->animation.attackClimaxFrame = graphics["attackClimaxFrame"].Float();
const JsonNode & animationTime = graphics["animationTime"];
cre->walkAnimationTime = animationTime["walk"].Float();
cre->attackAnimationTime = animationTime["attack"].Float();
cre->flightAnimationDistance = animationTime["flight"].Float(); //?
cre->animation.walkAnimationTime = animationTime["walk"].Float();
cre->animation.attackAnimationTime = animationTime["attack"].Float();
cre->animation.flightAnimationDistance = animationTime["flight"].Float(); //?
const JsonNode & missile = graphics["missile"];
const JsonNode & offsets = missile["offset"];
cre->upperRightMissleOffsetX = offsets["upperX"].Float();
cre->upperRightMissleOffsetY = offsets["upperY"].Float();
cre->rightMissleOffsetX = offsets["middleX"].Float();
cre->rightMissleOffsetY = offsets["middleY"].Float();
cre->lowerRightMissleOffsetX = offsets["lowerX"].Float();
cre->lowerRightMissleOffsetY = offsets["lowerY"].Float();
cre->animation.upperRightMissleOffsetX = offsets["upperX"].Float();
cre->animation.upperRightMissleOffsetY = offsets["upperY"].Float();
cre->animation.rightMissleOffsetX = offsets["middleX"].Float();
cre->animation.rightMissleOffsetY = offsets["middleY"].Float();
cre->animation.lowerRightMissleOffsetX = offsets["lowerX"].Float();
cre->animation.lowerRightMissleOffsetY = offsets["lowerY"].Float();
int i = 0;
BOOST_FOREACH (auto & angle, missile["frameAngles"].Vector())
{
cre->missleFrameAngles[i++] = angle.Float();
cre->animation.missleFrameAngles[i++] = angle.Float();
}
cre->advMapDef = graphics["map"].String();
cre->iconIndex = graphics["iconIndex"].Float();
@@ -667,6 +650,24 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
creature->level = config["level"].Float();
creature->animDefName = config["graphics"]["animation"].String();
BOOST_FOREACH(const JsonNode &ability, config["ability_remove"].Vector())
{
RemoveAbility(creature, ability);
}
BOOST_FOREACH(const JsonNode &ability, config["abilities"].Vector())
{
if (ability.getType() == JsonNode::DATA_VECTOR)
AddAbility(creature, ability.Vector()); // used only for H3 creatures
else
{
auto b = JsonUtils::parseBonus(ability);
b->source = Bonus::CREATURE_ABILITY;
b->duration = Bonus::PERMANENT;
creature->addNewBonus(b);
}
}
VLC->modh->identifiers.requestIdentifier(std::string("faction.") + config["faction"].String(), [=](si32 faction)
{
creature->faction = faction;
@@ -683,12 +684,10 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
if(config["hasDoubleWeek"].Bool())
doubledCreatures.insert(creature->idNumber);
creature->projectile = config["graphics"]["missile"]["projectile"].String();
creature->projectileSpin = config["graphics"]["missile"]["spinning"].Bool();
creature->animation.projectileImageName = config["graphics"]["missile"]["projectile"].String();
creature->animation.projectileSpin = config["graphics"]["missile"]["spinning"].Bool();
creature->special = config["special"].Bool();
if ( creature->special )
notUsedMonsters.insert(creature->idNumber);
const JsonNode & sounds = config["sound"];
@@ -706,6 +705,46 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
#undef GET_SOUND_VALUE
}
void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode & input)
{
BOOST_FOREACH (const JsonNode &exp, input.Vector())
{
auto bonus = JsonUtils::parseBonus (exp["bonus"]);
bonus->source = Bonus::STACK_EXPERIENCE;
bonus->duration = Bonus::PERMANENT;
const JsonVector &values = exp["values"].Vector();
int lowerLimit = 1;//, upperLimit = 255;
if (values[0].getType() == JsonNode::JsonType::DATA_BOOL)
{
BOOST_FOREACH (const JsonNode &val, values)
{
if (val.Bool() == true)
{
bonus->limiter = make_shared<RankRangeLimiter>(RankRangeLimiter(lowerLimit));
creature->addNewBonus (new Bonus(*bonus)); //bonuses must be unique objects
break; //TODO: allow bonuses to turn off?
}
++lowerLimit;
}
}
else
{
int lastVal = 0;
BOOST_FOREACH (const JsonNode &val, values)
{
if (val.Float() != lastVal)
{
bonus->val = val.Float() - lastVal;
bonus->limiter.reset (new RankRangeLimiter(lowerLimit));
creature->addNewBonus (new Bonus(*bonus));
}
lastVal = val.Float();
++lowerLimit;
}
}
}
}
void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigParser & parser) //help function for parsing CREXPBON.txt
{
bool enable = false; //some bonuses are activated with values 2 or 1
@@ -1022,7 +1061,7 @@ CreatureID CCreatureHandler::pickRandomMonster(const boost::function<int()> &ran
do
{
r = vstd::pickRandomElementOf(creatures, randGen)->idNumber;
} while (vstd::contains(VLC->creh->notUsedMonsters,r));
} while (VLC->creh->creatures[r] && VLC->creh->creatures[r]->special); // find first "not special" creature
}
else
{
@@ -1031,9 +1070,9 @@ CreatureID CCreatureHandler::pickRandomMonster(const boost::function<int()> &ran
BOOST_FOREACH(const CBonusSystemNode *b, creaturesOfLevel[tier].getChildrenNodes())
{
assert(b->getNodeType() == CBonusSystemNode::CREATURE);
CreatureID creid = static_cast<const CCreature*>(b)->idNumber;
if(!vstd::contains(notUsedMonsters, creid))
allowed.push_back(creid);
const CCreature * crea = dynamic_cast<const CCreature*>(b);
if(crea && !crea->special)
allowed.push_back(crea->idNumber);
}
if(!allowed.size())