1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

- generic string ID -> numeric ID resolution system

- - hero army and creature upgrade names are resolved using new system
- - faction names and creatures in towns are resolved using new system
- (linux) replaced build_data.sh with hopefully better vcmibuilder script
- minor fixes
This commit is contained in:
Ivan Savenko 2012-12-03 16:00:17 +00:00
parent c9dd80ea6d
commit 85a23e298c
17 changed files with 720 additions and 458 deletions

View File

@ -113,6 +113,8 @@ if (NOT APPLE)
install(DIRECTORY Mods/vcmi DESTINATION ${DATA_DIR}/Mods PATTERN ".svn" EXCLUDE)
# copy only fs.json for WoG
install(FILES Mods/WoG/filesystem.json DESTINATION ${DATA_DIR}/Mods/WoG)
install(FILES vcmibuilder DESTINATION ${BIN_DIR})
endif()
if(WIN32)

View File

@ -1,160 +0,0 @@
#!/bin/sh
# Copyright (c) 2009,2010,2011 Frank Zago
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# Extract game data from various archives and create a tree with the right files
# Tools needed:
# unshield: sudo apt-get install unshield
# rename (from perl)
# unrar: sudo apt-get install unrar
# Data files needed:
# data1.cab and data1.hdr from the original 1st CDROM
# Heroes3.snd from the original 2nd CDROM
# the WoG release v3.58f: allinone_358f.zip
# the VCMI distribution: vcmi_089.zip
# the menu graphic pack: vcmi32menu.zip
# Usage: put this script and the 4 data files into the same directory
# and run the script.
DESTDIR=`pwd`/vcmi
rm -rf $DESTDIR
mkdir $DESTDIR
# Extract Data from original CD-ROM
# 376917499 2002-06-14 14:32 _setup/data1.cab
# 27308 2002-06-14 14:32 _setup/data1.hdr
rm -rf temp
mkdir temp
cd temp
echo Extracting data1.cab
unshield x ../data1.cab || exit 1
echo Done extracting data1.cab
rm -rf CommonFiles
rm -rf GameUpdate
rm -rf Support
rm -rf Program_Files/mplayer
rm -rf Program_Files/ONLINE
find . -name "*.DLL" | xargs rm -f
find . -name "*.dll" | xargs rm -f
find . -name "*.pdf" -print0 | xargs -0 rm -f
find . -name "*.HLP" | xargs rm -f
find . -name "*.TXT" | xargs rm -f
find . -name "*.cnt" | xargs rm -f
find . -name "*.exe" | xargs rm -f
find . -name "*.EXE" | xargs rm -f
find . -name "*.ASI" | xargs rm -f
# Tree is clean. Move extracted files to their final destination
mv Program_Files/* $DESTDIR
cd ..
# Copy Heroes3.snd from 2nd CDROM and rename it to avoid a name conflict.
cp Heroes3.snd $DESTDIR/Data/Heroes3-cd2.snd
# Extract Data from WoG
# 39753248 allinone_358f.zip
rm -rf temp
mkdir temp
cd temp
unzip ../allinone_358f.zip
cd WoG_Install
mkdir temp
cd temp
unrar x -o+ ../main1.wog
unrar x -o+ ../main2.wog
unrar x -o+ ../main3.wog
unrar x -o+ ../main4.wog
#unrar x -o+ ../main5.wog
#unrar x -o+ ../main6_optional.wog
#unrar x -o+ ../main7_optional.wog
#unrar x -o+ ../main8_optional.wog
#unrar x -o+ ../main9_optional.wog
mkdir -p $DESTDIR/Data/zvs/
mv data/zvs/Lib1.res $DESTDIR/Data/zvs/
rm -rf picsall Documentation Data data update
rm -f action.txt h3bitmap.txt H3sprite.txt InstMult.txt
rm -f ACTION.TXT H3BITMAP.TXT H3SPRITE.TXT INSTMULT.TXT
rm -f *.DLL *.dll *.fnt readme.txt *.exe *.EXE *.lod *.vid h3ab_ahd.snd
rename 'y/a-z/A-Z/' *
# Tree is clean. Move extracted files to their final destination
mv MAPS/* $DESTDIR/Maps
rmdir MAPS
mkdir $DESTDIR/Sprites
mv *.MSK *.DEF $DESTDIR/Sprites
mv * $DESTDIR/Data/
cd ../../..
# Extract Data from VCMI release
rm -rf temp
mkdir temp
cd temp
#7zr x ../vcmi_088b.7z
unzip ../vcmi_089.zip
find . -name "*.dll" | xargs rm -f
find . -name "*.DLL" | xargs rm -f
find . -name "*.exe" | xargs rm -f
rm -rf AI
rm -f AUTHORS ChangeLog license.txt Microsoft.VC90.CRT.manifest
rm -rf MP3
rm -rf Games
# Tree is clean. Move extracted files to their final destination
cp -a . $DESTDIR
cd ..
# Extract graphics package
rm -rf temp
mkdir temp
cd temp
unzip ../vcmi32menu.zip
# Tree is already clean. Move extracted files to their final destination
cp -a . $DESTDIR
cd ..
# Done
echo
echo The complete game data is at $DESTDIR
echo You could move it to /usr/local/share/games/
echo and configure vcmi with:
echo ./configure --datadir=/usr/local/share/games/ --bindir=\`pwd\` --libdir=\`pwd\`
echo
echo If you want to run from your VCMI tree, create those links
echo at the root of your VCMI tree:
echo ln -s client/vcmiclient .
echo ln -s server/vcmiserver .
echo ln -s AI/GeniusAI/.libs/GeniusAI.so .

View File

@ -294,7 +294,7 @@ void CTownList::CTownItem::update()
{
size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->builded >= CGI->modh->settings.MAX_BUILDING_PER_TURN];
picture->setFrame(iconIndex);
picture->setFrame(iconIndex + 2);
redraw();
}

View File

@ -1072,7 +1072,7 @@ void CCreaInfo::clickRight(tribool down, bool previousState)
CInfoPopup *mess = new CInfoPopup();//creating popup
mess->free = true;
mess->bitmap = CMessage::drawBoxTextBitmapSub
(LOCPLINT->playerID, descr,graphics->bigImgs[creature->idNumber],"");
(LOCPLINT->playerID, descr,graphics->bigImgs[creature->iconIndex],"");
mess->pos.x = screen->w/2 - mess->bitmap->w/2;
mess->pos.y = screen->h/2 - mess->bitmap->h/2;
GH.pushInt(mess);

View File

@ -837,7 +837,7 @@ bool CVideoPlayer::open(std::string fname, bool loop, bool useOverlay)
{
PixelFormat screenFormat = PIX_FMT_NONE;
if (screen->format->Bshift < screen->format->Rshift)
if (screen->format->Bshift > screen->format->Rshift)
{
// this a BGR surface
switch (screen->format->BytesPerPixel)

View File

@ -134,7 +134,16 @@
[ [ 30, 37 ], [ 31, 38 ], [ 32, 39 ], [ 33, 40 ] ],
[ [ 34, 41 ], [ 35, 42 ], [ 36, 43 ] ]
],
"creatures" : [ [0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13] ],
"creatures" :
[
["Pikeman", "Halberdier"],
["Archer", "HeavyCrossbowman"],
["Griffin", "RoyalGriffin"],
["Swordsman", "Crusader"],
["Monk", "Zealot"],
["Cavalier", "Champion"],
["Angel", "Archangel"]
],
"horde" : [ 2, -1 ],
"primaryResource" : 127,
"mageGuild" : 4,
@ -375,7 +384,16 @@
[ [ 30, 37 ], [ 31, 38 ], [ 32, 39 ], [ 33, 40 ] ],
[ [ 34, 41 ], [ 35, 42 ], [ 36, 43 ] ]
],
"creatures" : [ [14, 15], [16, 17], [18, 19], [20, 21], [22, 23], [24, 25], [26, 27] ],
"creatures" :
[
["Centaur", "EliteCentaur"],
["Dwarf", "BattleDwarf"],
["WoodElf", "GrandElf"],
["Pegasus", "SilverPegasus"],
["Treefolk", "BriarTreefolk"],
["Unicorn", "WarUnicorn"],
["GreenDragon", "GoldDragon"]
],
"horde" : [ 1, 4 ],
"mageGuild" : 5,
"primaryResource" : 4,
@ -612,7 +630,16 @@
[ [ 30, 37 ], [ 31, 38 ], [ 32, 39 ], [ 33, 40 ] ],
[ [ 34, 41 ], [ 35, 42 ], [ 36, 43 ] ]
],
"creatures" : [ [28, 29], [30, 31], [32, 33], [34, 35], [36, 37], [38, 39], [40, 41] ],
"creatures" :
[
["ApprenticeGremlin", "MasterGremlin"],
["StoneGargoyle", "ObsidianGargoyle"],
["IronGolem", "StoneGolem"],
["Mage", "ArchMage"],
["Genie", "Caliph"],
["NagaSentinel", "NagaGuardian"],
["LesserTitan", "GreaterTitan"]
],
"horde" : [ 1, -1 ],
"primaryResource" : 5,
"mageGuild" : 5,
@ -847,7 +874,16 @@
[ [ 30, 37 ], [ 31, 38 ], [ 32, 39 ], [ 33, 40 ] ],
[ [ 34, 41 ], [ 35, 42 ], [ 36, 43 ] ]
],
"creatures" : [ [42, 43], [44, 45], [46, 47], [48, 49], [50, 51], [52, 53], [54, 55] ],
"creatures" :
[
["Imp", "Familiar"],
["Gog", "Magog"],
["HellHound", "Cerberus"],
["Single-HornedDemon", "Dual-HornedDemon"],
["PitFiend", "PitFoe"],
["Efreet", "EfreetSultan"],
["Devil", "ArchDevil"]
],
"horde" : [ 0, 2 ],
"mageGuild" : 5,
"primaryResource" : 1,
@ -1088,7 +1124,16 @@
[ [ 30, 37 ], [ 31, 38 ], [ 32, 39 ], [ 33, 40 ] ],
[ [ 34, 41 ], [ 35, 42 ], [ 36, 43 ] ]
],
"creatures" : [ [56, 57], [58, 59], [60, 61], [62, 63], [64, 65], [66, 67], [68, 69] ],
"creatures" :
[
["Skeleton", "SkeletonWarrior"],
["Zombie", "ZombieLord"],
["Wight", "Wraith"],
["Vampire", "Nosferatu"],
["Lich", "PowerLich"],
["BlackKnight", "BlackLord"],
["BoneDragon", "GhostDragon"]
],
"horde" : [ 0, -1 ],
"mageGuild" : 5,
"primaryResource" : 127,
@ -1323,7 +1368,16 @@
[ [ 30, 37 ], [ 31, 38 ], [ 32, 39 ], [ 33, 40 ] ],
[ [ 34, 41 ], [ 35, 42 ], [ 36, 43 ] ]
],
"creatures" : [ [70, 71], [72, 73], [74, 75], [76, 77], [78, 79], [80, 81], [82, 83] ],
"creatures" :
[
["Troglodyte", "InfernalTroglodyte"],
["Harpy", "HarpyHag"],
["Beholder", "EvilEye"],
["Medusa", "MedusaQueen"],
["Minotaur", "MinotaurKing"],
["Manticore", "Scorpicore"],
["RedDragon", "BlackDragon"]
],
"horde" : [ 0, -1 ],
"mageGuild" : 5,
"primaryResource" : 3,
@ -1556,7 +1610,16 @@
[ [ 30, 37 ], [ 31, 38 ], [ 32, 39 ], [ 33, 40 ] ],
[ [ 34, 41 ], [ 35, 42 ], [ 36, 43 ] ]
],
"creatures" : [ [84, 85], [86, 87], [88, 89], [90, 91], [92, 93], [94, 95], [96, 97] ],
"creatures" :
[
["Goblin", "Hobgoblin"],
["GoblinWolfRider", "HobgoblinWolfRider"],
["Orc", "OrcChieftain"],
["Ogre", "OgreMage"],
["Roc", "Thunderbird"],
["Cyclops", "CyclopsLord"],
["YoungBehemoth", "AncientBehemoth"]
],
"horde" : [ 0, -1 ],
"mageGuild" : 3,
"primaryResource" : 127,
@ -1791,7 +1854,16 @@
[ [ 30, 37 ], [ 31, 38 ], [ 32, 39 ], [ 33, 40 ] ],
[ [ 34, 41 ], [ 35, 42 ], [ 36, 43 ] ]
],
"creatures" : [ [98, 99], [100, 101], [104, 105], [106, 107], [102, 103], [108, 109], [110, 111] ],
"creatures" :
[
["Gnoll", "GnollMarauder"],
["PrimitiveLizardman", "AdvancedLizardman"],
["Dragonflies", "FireDragonFly"],
["Basilisk", "GreaterBasilisk"],
["CopperGorgon", "BronzeGorgon"],
["Wyvern", "WyvernMonarch"],
["Hydra", "ChaosHydra"]
],
"horde" : [ 0, -1 ],
"mageGuild" : 3,
"primaryResource" : 127,
@ -2031,7 +2103,16 @@
[ [ 30, 37 ], [ 31, 38 ], [ 32, 39 ], [ 33, 40 ] ],
[ [ 34, 41 ], [ 35, 42 ], [ 36, 43 ] ]
],
"creatures" : [ [118, 119], [112, 127], [115, 123], [114, 129], [113, 125], [120, 121], [130, 131] ],
"creatures" :
[
["Pixie", "Sprite"],
["AirElemental", "StormElemental"],
["WaterElemental", "IceElemental"],
["FireElemental", "ElectricityElemental"],
["EarthElemental", "StoneElemental"],
["PsiElemental", "MagicElemental"],
["Firebird", "Pheonix"]
],
"horde" : [ 0, -1 ],
"mageGuild" : 5,
"primaryResource" : 1,

File diff suppressed because it is too large Load Diff

View File

@ -318,21 +318,23 @@ void CCreatureHandler::loadCreatures()
int creatureID = creature["id"].Float();
const JsonNode *value;
/* A creature can have several names. */
BOOST_FOREACH(const JsonNode &name, creature["name"].Vector())
{
boost::assign::insert(nameToID)(name.String(), creatureID);
}
// Set various creature properties
CCreature *c = creatures[creatureID];
c->level = creature["level"].Float();
c->faction = creature["faction"].Float();
c->animDefName = creature["defname"].String();
VLC->modh->identifiers.requestIdentifier(std::string("faction.") + creature["faction"].String(), [=](si32 faction)
{
c->faction = faction;
});
BOOST_FOREACH(const JsonNode &value, creature["upgrades"].Vector())
{
c->upgradeNames.insert(value.String());
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + value.String(), [=](si32 identifier)
{
c->upgrades.insert(identifier);
});
}
value = &creature["projectile_defname"];
@ -360,6 +362,12 @@ void CCreatureHandler::loadCreatures()
AddAbility(c, ability.Vector());
}
}
/* A creature can have several names. */
BOOST_FOREACH(const JsonNode &name, creature["name"].Vector())
{
VLC->modh->identifiers.registerObject(std::string("creature.") + name.String(), c->idNumber);
}
}
BOOST_FOREACH(const JsonNode &creature, config["unused_creatures"].Vector())
@ -568,34 +576,12 @@ void CCreatureHandler::loadUnitAnimInfo(CCreature & unit, CLegacyConfigParser &
parser.endLine();
}
void CCreatureHandler::loadSoundsInfo()
void CCreatureHandler::loadCreatureSounds(JsonNode node, si32 creaID) // passing node by value to get clearer binding code
{
tlog5 << "\t\tReading config/cr_sounds.json" << std::endl;
const JsonNode config(ResourceID("config/cr_sounds.json"));
if (!config["creature_sounds"].isNull())
{
BOOST_FOREACH(const JsonNode &node, config["creature_sounds"].Vector())
{
const JsonNode *value;
int id;
value = &node["name"];
bmap<std::string,int>::const_iterator i = nameToID.find(value->String());
if (i != nameToID.end())
id = i->second;
else
{
tlog1 << "Sound info for an unknown creature: " << value->String() << std::endl;
continue;
}
/* This is a bit ugly. Maybe we should use an array for
* sound ids instead of separate variables and define
* attack/defend/killed/... as indexes. */
#define GET_SOUND_VALUE(value_name) do { value = &node[#value_name]; if (!value->isNull()) creatures[id]->sounds.value_name = value->String(); } while(0)
/* This is a bit ugly. Maybe we should use an array for
* sound ids instead of separate variables and define
* attack/defend/killed/... as indexes. */
#define GET_SOUND_VALUE(value_name) do { creatures[creaID]->sounds.value_name = node[#value_name].String(); } while(0)
GET_SOUND_VALUE(attack);
GET_SOUND_VALUE(defend);
GET_SOUND_VALUE(killed);
@ -607,6 +593,20 @@ void CCreatureHandler::loadSoundsInfo()
GET_SOUND_VALUE(startMoving);
GET_SOUND_VALUE(endMoving);
#undef GET_SOUND_VALUE
}
void CCreatureHandler::loadSoundsInfo()
{
tlog5 << "\t\tReading config/cr_sounds.json" << std::endl;
const JsonNode config(ResourceID("config/cr_sounds.json"));
if (!config["creature_sounds"].isNull())
{
BOOST_FOREACH(const JsonNode &node, config["creature_sounds"].Vector())
{
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + node["name"].String(),
boost::bind(&CCreatureHandler::loadCreatureSounds, this, node, _1));
}
}
}
@ -620,11 +620,10 @@ void CCreatureHandler::load(const JsonNode & node)
CCreature * creature = loadCreature(entry.second);
creature->nameRef = entry.first;
creature->idNumber = creatures.size();
nameToID[entry.first] = creatures.size();
creatures.push_back(creature);
tlog3 << "Added creature: " << entry.first << "\n";
//TODO: notify modHandler that this refName can be resolved to ID
VLC->modh->identifiers.registerObject(std::string("creature.") + creature->nameRef, creature->idNumber);
}
}
}
@ -640,7 +639,6 @@ CCreature * CCreatureHandler::loadCreature(const JsonNode & node)
cre->cost = Res::ResourceSet(node["cost"]);
cre->level = node["level"].Float();
cre->faction = node["faction"].Float(); //TODO: replaced by string -> id conversion
cre->fightValue = node["fightValue"].Float();
cre->AIValue = node["aiValue"].Float();
cre->growth = node["growth"].Float();
@ -658,10 +656,22 @@ CCreature * CCreatureHandler::loadCreature(const JsonNode & node)
cre->ammMin = amounts["min"].Float();
cre->ammMax = amounts["max"].Float();
std::string factionStr = node["faction"].String();
if (factionStr.empty())
factionStr = "neutral"; //TODO: should be done in schema
VLC->modh->identifiers.requestIdentifier(std::string("faction.") + factionStr, [=](si32 faction)
{
cre->faction = faction;
});
//optional
BOOST_FOREACH (auto & str, node["upgrades"].Vector())
{
cre->upgradeNames.insert (str.String());
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + str.String(), [=](si32 identifier)
{
cre->upgrades.insert(identifier);
});
}
if (!node["shots"].isNull())

View File

@ -86,7 +86,7 @@ public:
bool valid() const;
void addBonus(int val, int type, int subtype = -1);
std::string nodeName() const OVERRIDE;
std::string nodeName() const override;
//void getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const;
template<typename RanGen>
@ -102,7 +102,7 @@ public:
{
h & static_cast<CBonusSystemNode&>(*this);
h & namePl & nameSing & nameRef
& cost & upgradeNames & upgrades
& cost & upgrades
& fightValue & AIValue & growth & hordeGrowth
& ammMin & ammMax & level
& abilityText & abilityRefs & animDefName & advMapDef;
@ -131,7 +131,6 @@ public:
std::set<int> notUsedMonsters;
std::set<TCreature> doubledCreatures; //they get double week
std::vector<ConstTransitivePtr<CCreature> > creatures; //creature ID -> creature info
bmap<std::string,int> nameToID;
//stack exp
std::map<TBonusType, std::pair<std::string, std::string> > stackBonuses; // bonus => name, description
@ -160,6 +159,7 @@ public:
/// read one line from cranim.txt
void loadUnitAnimInfo(CCreature & unit, CLegacyConfigParser &parser);
/// load cr_sounds.json config
void loadCreatureSounds(JsonNode node, si32 creaID);
void loadSoundsInfo();
/// parse crexpbon.txt file from H3
void loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigParser &parser);
@ -177,7 +177,7 @@ public:
template <typename Handler> void serialize(Handler &h, const int version)
{
//TODO: should be optimized, not all these informations needs to be serialized (same for ccreature)
h & notUsedMonsters & creatures & nameToID;
h & notUsedMonsters & creatures;
h & stackBonuses & expRanks & maxExpPerBattle & expAfterUpgrade;
h & factionCommanders & skillLevels & skillRequirements & commanderLevelPremy;
h & allCreatures;

View File

@ -7,6 +7,7 @@
#include "JsonNode.h"
#include "GameConstants.h"
#include "BattleHex.h"
#include "CModHandler.h"
/*
* CHeroHandler.cpp, part of VCMI engine
@ -134,10 +135,15 @@ void CHeroHandler::loadHeroes()
for(int x=0;x<3;x++)
{
hero->lowStack[x] = parser.readNumber();
hero->highStack[x] = parser.readNumber();
hero->refTypeStack[x] = parser.readString();
boost::algorithm::replace_all(hero->refTypeStack[x], " ", ""); //remove spaces
hero->initialArmy[x].minAmount = parser.readNumber();
hero->initialArmy[x].maxAmount = parser.readNumber();
std::string refName = parser.readString();
boost::algorithm::replace_all(refName, " ", ""); //remove spaces
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + refName, [=](si32 creature)
{
hero->initialArmy[x].creature = creature;
});
}
parser.endLine();

View File

@ -33,14 +33,27 @@ struct SSpecialtyInfo
class DLL_LINKAGE CHero
{
public:
struct InitialArmyStack
{
ui32 minAmount;
ui32 maxAmount;
TCreature creature;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & minAmount & maxAmount & creature;
}
};
enum EHeroClasses {KNIGHT, CLERIC, RANGER, DRUID, ALCHEMIST, WIZARD,
DEMONIAC, HERETIC, DEATHKNIGHT, NECROMANCER, WARLOCK, OVERLORD,
BARBARIAN, BATTLEMAGE, BEASTMASTER, WITCH, PLANESWALKER, ELEMENTALIST};
std::string name; //name of hero
si32 ID;
ui32 lowStack[3], highStack[3]; //amount of units; described below
std::string refTypeStack[3]; //reference names of units appearing in hero's army if he is recruited in tavern
InitialArmyStack initialArmy[3];
CHeroClass * heroClass;
EHeroClasses heroType; //hero class
std::vector<std::pair<ui8,ui8> > secSkillsInit; //initial secondary skills; first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert)
@ -54,7 +67,7 @@ public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & name & ID & lowStack & highStack & refTypeStack & heroClass & heroType & secSkillsInit & spec & startingSpell & sex;
h & name & ID & initialArmy & heroClass & heroType & secSkillsInit & spec & startingSpell & sex;
}
};

View File

@ -4,6 +4,7 @@
#include "JsonNode.h"
#include "Filesystem/CResourceLoader.h"
#include "Filesystem/ISimpleResourceLoader.h"
/*
* CModHandler.h, part of VCMI engine
*
@ -25,6 +26,46 @@ class CTownHandler;
class CGeneralTextHandler;
class ResourceLocator;
void CIdentifierStorage::requestIdentifier(std::string name, const boost::function<void(si32)> & callback)
{
auto iter = registeredObjects.find(name);
if (iter != registeredObjects.end())
callback(iter->second); //already registered - trigger callback immediately
else
missingObjects[name].push_back(callback); // queue callback
}
void CIdentifierStorage::registerObject(std::string name, si32 identifier)
{
// do not allow to register same object twice
assert(registeredObjects.find(name) == registeredObjects.end());
registeredObjects[name] = identifier;
auto iter = missingObjects.find(name);
if (iter != missingObjects.end())
{
//call all awaiting callbacks
BOOST_FOREACH(auto function, iter->second)
{
function(identifier);
}
missingObjects.erase(iter);
}
}
void CIdentifierStorage::finalize() const
{
// print list of missing objects and crash
// in future should try to do some cleanup (like returning all id's as 0)
BOOST_FOREACH(auto object, missingObjects)
{
tlog1 << "Error: object " << object.first << " was not found!\n";
}
assert(missingObjects.empty());
}
CModHandler::CModHandler()
{
VLC->modh = this;
@ -99,6 +140,7 @@ void CModHandler::loadActiveMods()
VLC->creh->load(config["creatures"]);
}
VLC->creh->buildBonusTreeForTiers(); //do that after all new creatures are loaded
identifiers.finalize();
}
void CModHandler::reload()
@ -118,16 +160,6 @@ void CModHandler::reload()
VLC->dobjinfo->gobjs[Obj::MONSTER][crea->idNumber] = info;
}
BOOST_FOREACH(auto up, crea->upgradeNames)
{
auto it = VLC->creh->nameToID.find(up);
if (it != VLC->creh->nameToID.end())
{
crea->upgrades.insert (it->second);
}
else
tlog2 << "Not found upgrade with name " << up << "\n";
}
}
}

View File

@ -22,6 +22,24 @@ class CModIndentifier;
class CModInfo;
class JsonNode;
/// class that stores all object identifiers strings and maps them to numeric ID's
/// if possible, objects ID's should be in format <type>.<name>, camelCase e.g. "creature.grandElf"
class CIdentifierStorage
{
std::map<std::string, si32 > registeredObjects;
std::map<std::string, std::vector<boost::function<void(si32)> > > missingObjects;
public:
/// request identifier for specific object name. If ID is not yet resolved callback will be queued
/// and will be called later
void requestIdentifier(std::string name, const boost::function<void(si32)> & callback);
/// registers new object, calls all associated callbacks
void registerObject(std::string name, si32 identifier);
/// called at the very end of loading to check for any missing ID's
void finalize() const;
};
typedef si32 TModID;
class DLL_LINKAGE CModInfo
@ -41,12 +59,14 @@ public:
class DLL_LINKAGE CModHandler
{
public:
//std::string currentConfig; //save settings in this file
std::map <TModID, CModInfo> allMods;
std::set <TModID> activeMods;//TODO: use me
public:
CIdentifierStorage identifiers;
/// management of game settings config
void loadConfigFromFile (std::string name);
void saveConfigToFile (std::string name);

View File

@ -783,38 +783,27 @@ void CGHeroInstance::initArmy(IArmyDescriptor *dst /*= NULL*/)
for(int stackNo=0; stackNo < howManyStacks; stackNo++)
{
int creID = 0;
auto creItr = VLC->creh->nameToID.find(type->refTypeStack[stackNo]);
if(creItr == VLC->creh->nameToID.end())
{
tlog1 << "Cannot find a creature named " << type->refTypeStack[stackNo] << std::endl;
tlog1 << "Available creatures: \n";
BOOST_FOREACH(auto i, VLC->creh->nameToID)
{
tlog1 << boost::format("\t%s => %d\n") % i.first % i.second;
}
}
else
creID = creItr->second;
auto & stack = type->initialArmy[stackNo];
int range = type->highStack[stackNo] - type->lowStack[stackNo];
int count = ran()%(range+1) + type->lowStack[stackNo];
int range = stack.maxAmount - stack.minAmount;
int count = ran()%(range+1) + stack.minAmount;
if(creID>=145 && creID<=149) //war machine
if(stack.creature >= 145 &&
stack.creature <= 149) //war machine
{
warMachinesGiven++;
if(dst != this)
continue;
int slot = -1, aid = -1;
switch (creID)
switch (stack.creature)
{
case 145: //catapult
slot = ArtifactPosition::MACH4;
aid = 3;
break;
default:
aid = CArtHandler::convertMachineID(creID,true);
aid = CArtHandler::convertMachineID(stack.creature, true);
slot = 9 + aid;
break;
}
@ -825,7 +814,7 @@ void CGHeroInstance::initArmy(IArmyDescriptor *dst /*= NULL*/)
tlog3 << "Hero " << name << " already has artifact at " << slot << ", omitting giving " << aid << std::endl;
}
else
dst->setCreature(stackNo-warMachinesGiven, creID, count);
dst->setCreature(stackNo-warMachinesGiven, stack.creature, count);
}
}
void CGHeroInstance::initHeroDefInfo()

View File

@ -5,6 +5,7 @@
#include "CGeneralTextHandler.h"
#include "JsonNode.h"
#include "GameConstants.h"
#include "CModHandler.h"
#include "Filesystem/CResourceLoader.h"
/*
@ -402,14 +403,24 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
town.hordeLvl[town.hordeLvl.size()] = node.Float();
}
BOOST_FOREACH(const JsonNode &list, source["creatures"].Vector())
const JsonVector & creatures = source["creatures"].Vector();
town.creatures.resize(creatures.size());
for (size_t i=0; i< creatures.size(); i++)
{
std::vector<TCreature> level;
BOOST_FOREACH(const JsonNode &node, list.Vector())
const JsonVector & level = creatures[i].Vector();
town.creatures[i].resize(level.size());
for (size_t j=0; j<level.size(); j++)
{
level.push_back(node.Float());
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + level[j].String(), [=, &town](si32 creature)
{
town.creatures[i][j] = creature;
});
}
town.creatures.push_back(level);
}
loadBuildings(town, source["buildings"]);
@ -471,6 +482,7 @@ void CTownHandler::load(const JsonNode &source)
loadPuzzle(faction, node.second["puzzleMap"]);
tlog3 << "Added faction: " << node.first << "\n";
VLC->modh->identifiers.registerObject(std::string("faction.") + node.first, faction.factionID);
}
}

View File

@ -716,7 +716,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
if(result == 1) //retreat
{
sah.army[0].clear();
sah.army[0].setCreature(0, VLC->creh->nameToID[loserHero->type->refTypeStack[0]],1);
sah.army[0].setCreature(0, loserHero->type->initialArmy[0].creature, 1);
}
if(const CGHeroInstance *another = getPlayer(loser)->availableHeroes[1])
@ -3195,7 +3195,7 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, ui8 player)
{
sah.hid[hid] = newHero->subID;
sah.army[hid].clear();
sah.army[hid].setCreature(0, VLC->creh->nameToID[newHero->type->refTypeStack[0]],1);
sah.army[hid].setCreature(0, newHero->type->initialArmy[0].creature, 1);
}
else
sah.hid[hid] = -1;

257
vcmibuilder Executable file
View File

@ -0,0 +1,257 @@
#!/bin/bash
#
# VCMI data builder script
# Extracts game data from various sources and creates a tree with the right files
#
# Authors: listed in file AUTHORS in main folder
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# no console arguments - print help
if [ $# -eq 0 ] ; then
print_help=true
fi
# command line parsing
# can't use system getopt which is not cross-platform (BSD/Mac)
# can't use built-in getopts which can't parse long options (too difficult to avoid - e.g. CD1/CD2)
while [ $# -gt 0 ]
do
case $1 in
--cd1) cd1_dir=$2 ; shift 2 ;;
--cd2) cd2_dir=$2 ; shift 2 ;;
--gog) gog_file=$2 ; shift 2 ;;
--data) data_dir=$2 ; shift 2 ;;
--wog) wog_archive=$2 ; shift 2 ;;
--vcmi) vcmi_archive=$2 ; shift 2 ;;
--download) download=true ; shift 1 ;;
--validate) validate=true ; shift 1 ;;
*) print_help=true ; shift 1 ;;
esac
done
if [[ -n "$print_help" ]]
then
echo "VCMI data builder utility"
echo "Usage: vcmibuilder <options>"
echo "Options:"
echo " --cd1 DIRECTORY " "Path to mounted first CD with Heroes 3 install files"
echo " " "Requires unshield"
echo
echo " --cd2 DIRECTORY " "Path to second CD with Heroes 3 data files."
echo
echo " --gog EXECUTABLE " "Path to gog.com executable"
echo " " "Requires innoextract ( http://constexpr.org/innoextract/ )"
echo
echo " --data DIRECTORY " "Path to installed Heroes 3 data"
echo
echo " --wog ARCHIVE " "Path to manually downloaded WoG archive"
echo " " "Requires unzip"
echo
echo " --vcmi ARCHIVE " "Path to manually downloaded VCMI data package"
echo " " "Requires unzip"
echo
echo " --download " "If specified vcmibuilder will download packages using wget"
echo " " "Requires wget and Internet connection"
echo
echo " --validate " "If specified vcmibuilder will run basic validness checks"
exit 1
fi
# test presence of program $1, $2 will be passed as parameters to test presence
test_utility ()
{
$1 $2 > /dev/null 2>&1 || { echo "$1 was not found. Please install it" 1>&2 ; exit 1; }
}
#print error message and exit
fail ()
{
$2
echo "$1" 1>&2
exit 1
}
# print warning to stderr.
warning ()
{
echo "$1" 1>&2
warn_user=true
}
# check if selected options are correct.
if [[ -n "$data_dir" ]]
then
if [[ -n "$gog_file" ]] || [[ -n "$cd1_dir" ]] || [[ -n "$cd2_dir" ]]
then
warning "Warning: Installed data dir was specified. Both gog and cd options will be ignored"
unset gog_file cd1_dir cd2_dir
fi
fi
if [[ -n "$gog_file" ]]
then
test_utility "innoextract"
if [[ -n "$cd1_dir" ]] || [[ -n "$cd2_dir" ]]
then
warning "Warning: Both gog and cd options were specified. cd options will be ignored"
unset cd1_dir cd2_dir
fi
fi
if [[ -n "$cd1_dir" ]]
then
test_utility "unshield" "-V"
fi
if [[ -n "$download" ]]
then
if [[ -n "$wog_archive" ]] && [[ -n "$vcmi_archive" ]]
then
warning "Warning: Both wog and vcmi options were specified. Download option will not be used"
unset download
else
test_utility "wget" "-V"
fi
fi
if [[ -n "$download" ]] || [[ -n "$wog_archive" ]] || [[ -n "$vcmi_archive" ]]
then
test_utility "unzip"
fi
if [[ -z "$gog_file" ]] && [[ -z "$data_dir" ]] && ( [[ -z "$cd1_dir" ]] || [[ -z "$cd2_dir" ]] )
then
warning "Warning: Selected options will not create complete Heroes 3 data!"
fi
if [[ -z "$download" ]] && ( [[ -z "$wog_archive" ]] || [[ -z "$vcmi_archive" ]])
then
warning "Warning: Selected options will not create complete VCMI data!"
fi
# if at least one warning has been printed - ask for confirmation
if [[ -n "$warn_user" ]]
then
read -p "Do you wish to continue? (y/n) " -n 1
echo #print eol
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
exit 1
fi
fi
# start installation
dest_dir="./vcmi"
mkdir -p $dest_dir
if [[ -n "$gog_file" ]]
then
data_dir="./app"
# innoextract always reports error (iconv 84 error). Just test file for presence
test -f $gog_file || fail "Error: gog.com executable was not found!"
innoextract -s -p 1 $gog_file
fi
if [[ -n "$cd1_dir" ]]
then
data_dir="./cddir"
mkdir -p $data_dir
unshield -d $data_dir x $cd1_dir/_setup/data1.cab || fail "Error: failed to extract from Install Shield installer!" "rm -rf ./cddir"
# a bit tricky - different releases have different root directory. Move extracted files to data_dir
if [ -d $data_dir/"Heroes3" ]
then
mv $data_dir/Heroes3/* $data_dir
elif [ -d $data_dir"/Program_Files" ]
then
mv $data_dir/Program_Files/* $data_dir
else
echo "Error: failed to find extracted game files!"
echo "Extracted directories are: "
ls -la $data_dir
echo "Please report this on vcmi.eu"
exit 1;
fi
fi
if [[ -n "$cd2_dir" ]]
then
mkdir -p $dest_dir/Data
if [ -d $cd2_dir/heroes3 ]
then
cp $cd2_dir/heroes3/Data/Heroes3.vid $dest_dir/Data/VIDEO.VID
cp $cd2_dir/heroes3/Data/Heroes3.snd $dest_dir/Data/Heroes3-cd2.snd
else
cp $cd2_dir/Heroes3/Data/Heroes3.vid $dest_dir/Data/VIDEO.VID
cp $cd2_dir/Heroes3/Data/Heroes3.snd $dest_dir/Data/Heroes3-cd2.snd
fi
fi
if [[ -n "$data_dir" ]]
then
cp -r $data_dir/Data $dest_dir
cp -r $data_dir/Maps $dest_dir
# this folder is named differently from time to time
# vcmi can handle any case but script can't
if [ -d $data_dir/MP3 ]
then
cp -r $data_dir/MP3 $dest_dir
else
cp -r $data_dir/Mp3 $dest_dir
fi
fi
if [[ -n "$download" ]]
then
if [[ -z "$wog_archive" ]]
then
wget "http://download.vcmi.eu/WoG/wog.zip" -O wog.zip || fail "Error: failed to download WoG archive!" "rm -f wog.zip"
wog_archive="./wog.zip"
fi
if [[ -z "$vcmi_archive" ]]
then
wget "http://download.vcmi.eu/core.zip" -O core.zip || fail "Error: failed to download WoG archive!" "rm -f core.zip"
vcmi_archive="./core.zip"
fi
fi
if [[ -n "$wog_archive" ]]
then
echo "decompressing $wog_archive"
unzip -qo $wog_archive -d $dest_dir || fail "Error: failed to extract WoG archive!"
fi
if [[ -n "$vcmi_archive" ]]
then
echo "decompressing $vcmi_archive"
unzip -qo $vcmi_archive -d $dest_dir || fail "Error: failed to extract WoG archive!"
fi
if [[ -n "$validate" ]]
then
test -f $dest_dir/Data/H3bitmap.lod || fail "Error: Heroes 3 data files are missing!"
test -f $dest_dir/Data/H3sprite.lod || fail "Error: Heroes 3 data files are missing!"
test -f $dest_dir/Data/VIDEO.VID || fail "Error: Heroes 3 data files (CD2) are missing!"
test -d $dest_dir/Mods/WoG/Data || fail "Error: WoG data files are missing!"
test -d $dest_dir/Mods/vcmi/Data || fail "Error: VCMI data files are missing!"
fi
#TODO: Cleanup? How?
echo
echo "vcmibuilder finished succesfully"
echo "resulting data was placed into $PWD/vcmi"
echo "any other files in current directory can be removed"
echo