1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00

- CMappedFileLoader class to remap WoG files to h3 names

- WoG should be optional, all remapped files are listed in WoG/config/wogFileOverrides.json
- fixed several cases of incorrect positioning of creatures in battles
- some missing sounds for battle effects
- negative luck support, disabled by default
- a bit hackish detection of WoG presence, VCMI should work on SoD-only installs
This commit is contained in:
Ivan Savenko 2013-05-04 13:14:23 +00:00
parent 298f862d86
commit 8be801a6dc
33 changed files with 338 additions and 176 deletions

View File

@ -140,12 +140,13 @@ endif()
# For apple this files will be already inside vcmiclient bundle
if (NOT APPLE)
# copy whole directory but .svn control files and user-specific settings.json
# copy whole directory but .svn control files
install(DIRECTORY config DESTINATION ${DATA_DIR} PATTERN ".svn" EXCLUDE)
# copy vcmi mod along with all its content
install(DIRECTORY Mods/vcmi DESTINATION ${DATA_DIR}/Mods PATTERN ".svn" EXCLUDE)
# copy only mod.json for WoG
# copy only files added by vcmi for WoG
install(FILES Mods/WoG/mod.json DESTINATION ${DATA_DIR}/Mods/WoG)
install(DIRECTORY Mods/WoG/config DESTINATION ${DATA_DIR}/Mods/WoG PATTERN ".svn" EXCLUDE)
install(FILES vcmibuilder DESTINATION ${BIN_DIR} PERMISSIONS
OWNER_WRITE OWNER_READ OWNER_EXECUTE

View File

@ -0,0 +1,20 @@
// default configuration for mod system loaded at launch
{
"textData" :
{
"heroClass" : 18,
"artifact" : 171,
"creature" : 197,
"faction" : 9,
"hero" : 156
},
"modules":
{
"STACK_EXPERIENCE": true,
"STACK_ARTIFACTS": true,
"COMMANDERS": true,
"MITHRIL": false //so far unused
}
}

View File

@ -0,0 +1,45 @@
{
// Text configs
"data/crgen1.txt" : "data/zcrgn1.txt",
"data/crtraits.txt" : "data/zcrtrait.txt",
"data/help.txt" : "data/zelp.txt",
"data/objects.txt" : "data/zobjcts.txt",
// main menu images
"data/gamselb0.bmp" : "data/ZPIC1000.bmp", // map selection screen
"data/gamselb1.bmp" : "data/ZPIC1001.bmp", // map selection screen
"data/loadbar.bmp" : "data/ZPIC106.bmp", // loading screen
"data/gamselbk.bmp" : "data/ZPIC1005.bmp", // background
"data/newgame.bmp" : "data/ZNEWGAM.bmp", // "new game" text
"data/loadgame.bmp" : "data/ZLOADGAM.bmp", // "load game" text
// main menu buttons
"sprites/mmenung.def" : "sprites/zmenung.def",
"sprites/mmenulg.def" : "sprites/zmenulg.def",
"sprites/mmenuhs.def" : "sprites/zmenuhs.def",
"sprites/mmenucr.def" : "sprites/zmenucr.def",
"sprites/mmenuqt.def" : "sprites/zmenuqt.def",
// game type select (single/multi/campaign)
"sprites/gtsingl.def" : "sprites/ztsingl.def",
"sprites/gtmulti.def" : "sprites/ztmulti.def",
"sprites/gtcampn.def" : "sprites/ztcampn.def",
"sprites/gttutor.def" : "sprites/zttutor.def",
"sprites/gtback.def" : "sprites/ztback.def",
// campaigns
"sprites/csssod.def" : "sprites/zsssod.def",
"sprites/cssroe.def" : "sprites/zssroe.def",
"sprites/cssarm.def" : "sprites/zssarm.def",
"sprites/csscus.def" : "sprites/zsscus.def",
// resource bars
"data/tresbar.bmp" : "data/zresbar.bmp",
"data/kresbar.bmp" : "data/z2esbar.bmp",
"data/aresbar.bmp" : "data/zresbar.bmp",
// misc
"data/tpcainfo.bmp" : "data/zpcainfo.bmp", // stats images for town fort
"music/mainmenu.mp3" : "music/mainmenuwog.mp3",
"video/credits.bik" : "video/acredit.bik"
}

View File

@ -1,6 +1,10 @@
{
"filesystem":
{
"" :
[
{ "type" : "map", "path" : "/Config/wogFileOverrides.json"}
],
"CONFIG/" :
[
{ "type" : "dir", "path" : "/Config"}

View File

@ -16,10 +16,5 @@
},
"name" : "VCMI essential files",
"description" : "Essential files required for VCMI to run correctly",
"depends" :
[
"wog"
]
"description" : "Essential files required for VCMI to run correctly"
}

View File

@ -874,7 +874,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
Rect barRect(9, 182, 732, 18);
statusbar = new CGStatusBar(new CPicture(*panel, barRect, 9, 555, false));
resdatabar = new CResDataBar("ZRESBAR", 3, 575, 32, 2, 85, 85);
resdatabar = new CResDataBar("ARESBAR", 3, 575, 32, 2, 85, 85);
townlist = new CTownList(3, Point(744, 414), "IAM014", "IAM015");
if (from)
@ -1518,7 +1518,7 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *
if (!town->creatures[level].second.empty())
addUsedEvents(LCLICK | RCLICK | HOVER);//Activate only if dwelling is present
icons = new CPicture("ZPCAINFO", 261, 3);
icons = new CPicture("TPCAINFO", 261, 3);
buildingPic = new CAnimImage(town->town->clientInfo.buildingsIcons, buildingID, 0, 4, 21);
const CCreature* creature = NULL;

View File

@ -282,7 +282,6 @@ int main(int argc, char** argv)
};
if (!testFile("DATA/HELP.TXT", "Heroes III data") &&
!testFile("DATA/ZELP.TXT", "In the Wake of Gods data") &&
!testFile("MODS/VCMI/MOD.JSON", "VCMI mod") &&
!testFile("DATA/StackQueueBgBig.PCX", "VCMI data"))
exit(1); // These are unrecoverable errors

View File

@ -879,8 +879,17 @@ void CPlayerInterface::battleAttack(const BattleAttack *ba)
boost::algorithm::replace_first(hlp,"%s", (stack->count != 1) ? stack->getCreature()->namePl.c_str() : stack->getCreature()->nameSing.c_str());
battleInt->console->addText(hlp);
battleInt->displayEffect(18, stack->position);
CCS->soundh->playSound(soundBase::GOODLUCK);
}
if(ba->unlucky()) //unlucky hit
{
const CStack *stack = cb->battleGetStackByID(ba->stackAttacking);
std::string hlp = CGI->generaltexth->allTexts[44];
boost::algorithm::replace_first(hlp,"%s", (stack->count != 1) ? stack->getCreature()->namePl.c_str() : stack->getCreature()->nameSing.c_str());
battleInt->console->addText(hlp);
battleInt->displayEffect(48, stack->position);
CCS->soundh->playSound(soundBase::BADLUCK);
}
//TODO: bad luck?
if (ba->deathBlow())
{
const CStack *stack = cb->battleGetStackByID(ba->stackAttacking);
@ -892,6 +901,7 @@ void CPlayerInterface::battleAttack(const BattleAttack *ba)
const CStack * attacked = cb->battleGetStackByID(i->stackAttacked);
battleInt->displayEffect(73, attacked->position);
}
CCS->soundh->playSound(soundBase::deathBlow);
}

View File

@ -213,7 +213,7 @@ VCMI_SOUND_NAME(cyclopMove) VCMI_SOUND_FILE(CYCLMOVE.wav) \
VCMI_SOUND_NAME(cyclopShot) VCMI_SOUND_FILE(CYCLSHOT.wav) \
VCMI_SOUND_NAME(cyclopWNCE) VCMI_SOUND_FILE(CYCLWNCE.wav) \
VCMI_SOUND_NAME(DANGER) VCMI_SOUND_FILE(DANGER.wav) \
VCMI_SOUND_NAME(deathBlood) VCMI_SOUND_FILE(DEATHBLO.wav) \
VCMI_SOUND_NAME(deathBlow) VCMI_SOUND_FILE(DEATHBLO.wav) \
VCMI_SOUND_NAME(deathCloud) VCMI_SOUND_FILE(DEATHCLD.wav) \
VCMI_SOUND_NAME(deathRIP) VCMI_SOUND_FILE(DEATHRIP.wav) \
VCMI_SOUND_NAME(deathSTR) VCMI_SOUND_FILE(DEATHSTR.wav) \

View File

@ -556,8 +556,8 @@ void CGarrisonInt::createSlots()
void CGarrisonInt::recreateSlots()
{
setSplittingMode(false);
selectSlot(nullptr);
setSplittingMode(false);
for(size_t i = 0; i<splitButtons.size(); i++)
splitButtons[i]->block(true);
@ -629,10 +629,10 @@ void CGarrisonInt::setSplittingMode(bool on)
if (inSplittingMode || on)
{
BOOST_FOREACH(CGarrisonSlot * slot, slotsUp)
slot->setHighlight(slot->creature == nullptr || slot->creature == getSelection()->creature);
slot->setHighlight( ( on && (slot->creature == nullptr || slot->creature == getSelection()->creature)));
BOOST_FOREACH(CGarrisonSlot * slot, slotsDown)
slot->setHighlight(slot->creature == nullptr || slot->creature == getSelection()->creature);
slot->setHighlight( ( on && (slot->creature == nullptr || slot->creature == getSelection()->creature)));
inSplittingMode = on;
}
}
@ -1773,7 +1773,7 @@ void CMinorResDataBar::showAll(SDL_Surface * to)
CMinorResDataBar::CMinorResDataBar()
{
bg = BitmapHandler::loadBitmap("Z2ESBAR.bmp");
bg = BitmapHandler::loadBitmap("KRESBAR.bmp");
SDL_SetColorKey(bg,SDL_SRCCOLORKEY,SDL_MapRGB(bg->format,0,255,255));
graphics->blueToPlayersAdv(bg,LOCPLINT->playerID);
pos.x = 7;
@ -5241,7 +5241,7 @@ CPuzzleWindow::CPuzzleWindow(const int3 &GrailPos, double discoveredRatio):
new CPicture("PUZZLOGO", 607, 3);
new CLabel(700, 95, FONT_BIG, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[463]);
new CResDataBar("ZRESBAR.bmp", 3, 575, 32, 2, 85, 85);
new CResDataBar("ARESBAR.bmp", 3, 575, 32, 2, 85, 85);
int faction = LOCPLINT->cb->getStartInfo()->playerInfos.find(LOCPLINT->playerID)->second.castle;

View File

@ -492,7 +492,7 @@ void CMovementAnimation::endAnim()
{
const CStack * movedStack = stack;
myAnim()->pos = CClickableHex::getXYUnitAnim(nextHex, movedStack->attackerOwned, movedStack, owner);
myAnim()->pos = CClickableHex::getXYUnitAnim(nextHex, owner->creDir[stack->ID], movedStack, owner);
CBattleAnimation::endAnim();
if(movedStack)
@ -604,6 +604,8 @@ CReverseAnimation::CReverseAnimation(CBattleInterface * _owner, const CStack * s
bool CReverseAnimation::init()
{
logGlobal->errorStream() << "Pos at " << myAnim()->pos.x << "x" << myAnim()->pos.y;
if(myAnim() == NULL || myAnim()->getType() == CCreatureAnim::DEATH)
{
endAnim();
@ -614,8 +616,10 @@ bool CReverseAnimation::init()
if(!priority && !isEarliest(false))
return false;
if(myAnim()->framesInGroup(CCreatureAnim::TURN_R))
myAnim()->setType(CCreatureAnim::TURN_R);
//myAnim()->pos = CClickableHex::getXYUnitAnim(hex, owner->creDir[stack->ID], stack, owner);
if(myAnim()->framesInGroup(CCreatureAnim::TURN_L))
myAnim()->setType(CCreatureAnim::TURN_L);
else
setupSecondPart();
@ -647,6 +651,8 @@ void CReverseAnimation::nextFrame()
void CReverseAnimation::endAnim()
{
logGlobal->errorStream() << "Pos on end " << myAnim()->pos.x << "x" << myAnim()->pos.y;
CBattleAnimation::endAnim();
if( stack->alive() )//don't do that if stack is dead
myAnim()->setType(CCreatureAnim::HOLDING);
@ -656,6 +662,7 @@ void CReverseAnimation::endAnim()
void CReverseAnimation::setupSecondPart()
{
logGlobal->errorStream() << "Pos before 2nd " << myAnim()->pos.x << "x" << myAnim()->pos.y;
if(!stack)
{
endAnim();
@ -664,9 +671,7 @@ void CReverseAnimation::setupSecondPart()
owner->creDir[stack->ID] = !owner->creDir[stack->ID];
Point coords = CClickableHex::getXYUnitAnim(hex, owner->creDir[stack->ID], stack, owner);
myAnim()->pos.x = coords.x;
//creAnims[stackID]->pos.y = coords.second;
myAnim()->pos = CClickableHex::getXYUnitAnim(hex, owner->creDir[stack->ID], stack, owner);
if(stack->doubleWide())
{
@ -682,10 +687,12 @@ void CReverseAnimation::setupSecondPart()
}
}
logGlobal->errorStream() << "Pos after 2nd " << myAnim()->pos.x << "x" << myAnim()->pos.y;
secondPartSetup = true;
if(myAnim()->framesInGroup(CCreatureAnim::TURN_L))
myAnim()->setType(CCreatureAnim::TURN_L);
if(myAnim()->framesInGroup(CCreatureAnim::TURN_R))
myAnim()->setType(CCreatureAnim::TURN_R);
else
endAnim();
}

View File

@ -1986,6 +1986,7 @@ void CBattleInterface::battleTriggerEffect(const BattleTriggerEffect & bte)
std::string hlp = CGI->generaltexth->allTexts[33];
boost::algorithm::replace_first(hlp,"%s",(stack->getName()));
displayEffect(20,stack->position);
CCS->soundh->playSound(soundBase::GOODMRLE);
console->addText(hlp);
break;
}
@ -2640,6 +2641,7 @@ void CBattleInterface::startAction(const BattleAction* action)
case Battle::BAD_MORALE:
txtid = -34; //negative -> no separate singular/plural form
displayEffect(30,stack->position);
CCS->soundh->playSound(soundBase::BADMRLE);
break;
}

View File

@ -2,7 +2,7 @@
"spellBook":
{
"index" : 0,
"type" : ["HERO"]
"type" : ["HERO"],
},
"spellScroll":
{

View File

@ -398,7 +398,7 @@
"hateArchAngels" :
{
"type" : "HATE",
"subtype" : "creature.angel",
"subtype" : "creature.archangel",
"val" : 50
},
"FLYING_ARMY" :

View File

@ -4,8 +4,8 @@
"textData" :
{
"heroClass" : 18,
"artifact" : 171,
"creature" : 197,
"artifact" : 144,
"creature" : 150,
"faction" : 9,
"hero" : 156
},
@ -17,13 +17,14 @@
"NEUTRAL_STACK_EXP_DAILY" : 500,
"MAX_BUILDING_PER_TURN" : 1,
"DWELLINGS_ACCUMULATE_CREATURES" : true,
"ALL_CREATURES_GET_DOUBLE_MONTHS" : false
"ALL_CREATURES_GET_DOUBLE_MONTHS" : false,
"NEGATIVE_LUCK" : false
},
"modules":
{
"STACK_EXPERIENCE": true,
"STACK_ARTIFACTS": true,
"COMMANDERS": true,
"STACK_EXPERIENCE": false,
"STACK_ARTIFACTS": false,
"COMMANDERS": false,
"MITHRIL": false //so far unused
}
}

View File

@ -1,15 +1,15 @@
{
//images used in game selection screen
"game-select" : ["ZPIC1000", "ZPIC1001"],
"game-select" : ["gamselb0", "gamselb1"],
"loading" : ["ZPIC106", "LoadBar"],
"loading" : ["loadbar"],
//Main menu window, consists of several sub-menus aka items
"window":
{
"background" : "ZPIC1005",
"background" : "gamselbk",
//"scalable" : true, //background will be scaled to screen size
"video" : {"x": 8, "y": 105, "name":"ACREDIT.SMK" },//Floating WoG logo
"video" : {"x": 8, "y": 105, "name":"CREDITS.SMK" },//Floating WoG logo
//"images" : [],//Optioal, contains any additional images in the same format as video
"items" :
[
@ -17,46 +17,46 @@
"name" : "main",
"buttons":
[
{"x": 540, "y": 10, "name":"ZMENUNG", "hotkey" : 110, "help": 3, "command": "to new"},
{"x": 532, "y": 132, "name":"ZMENULG", "hotkey" : 108, "help": 4, "command": "to load"},
{"x": 524, "y": 251, "name":"ZMENUHS", "hotkey" : 104, "help": 5, "command": "highscores"},
{"x": 557, "y": 359, "name":"ZMENUCR", "hotkey" : 99, "help": 6, "command": "to credits"},
{"x": 586, "y": 468, "name":"ZMENUQT", "hotkey" : 27, "help": 7, "command": "exit"}
{"x": 540, "y": 10, "name":"MMENUNG", "hotkey" : 110, "help": 3, "command": "to new"},
{"x": 532, "y": 132, "name":"MMENULG", "hotkey" : 108, "help": 4, "command": "to load"},
{"x": 524, "y": 251, "name":"MMENUHS", "hotkey" : 104, "help": 5, "command": "highscores"},
{"x": 557, "y": 359, "name":"MMENUCR", "hotkey" : 99, "help": 6, "command": "to credits"},
{"x": 586, "y": 468, "name":"MMENUQT", "hotkey" : 27, "help": 7, "command": "exit"}
]
},
{
"name" : "new",
"buttons":
[
{"x": 545, "y": 4, "name":"ZTSINGL", "hotkey" : 115, "help": 10, "command": "start single"},
{"x": 568, "y": 120, "name":"ZTMULTI", "hotkey" : 109, "help": 12, "command": "start multi"},
{"x": 541, "y": 233, "name":"ZTCAMPN", "hotkey" : 99, "help": 11, "command": "to campaign"},
{"x": 545, "y": 358, "name":"ZTTUTOR", "hotkey" : 116, "help": 13, "command": "start tutorial"},
{"x": 582, "y": 464, "name":"ZTBACK", "hotkey" : 27, "help": 14, "command": "to main"}
{"x": 545, "y": 4, "name":"GTSINGL", "hotkey" : 115, "help": 10, "command": "start single"},
{"x": 568, "y": 120, "name":"GTMULTI", "hotkey" : 109, "help": 12, "command": "start multi"},
{"x": 541, "y": 233, "name":"GTCAMPN", "hotkey" : 99, "help": 11, "command": "to campaign"},
{"x": 545, "y": 358, "name":"GTTUTOR", "hotkey" : 116, "help": 13, "command": "start tutorial"},
{"x": 582, "y": 464, "name":"GTBACK", "hotkey" : 27, "help": 14, "command": "to main"}
],
"images": [ {"x": 114, "y": 312, "name":"ZNEWGAM"} ]
"images": [ {"x": 114, "y": 312, "name":"NEWGAME"} ]
},
{
"name" : "load",
"buttons":
[
{"x": 545, "y": 8, "name":"ZTSINGL", "hotkey" : 115, "help": 10, "command": "load single"},
{"x": 568, "y": 120, "name":"ZTMULTI", "hotkey" : 109, "help": 12, "command": "load multi"},
{"x": 541, "y": 233, "name":"ZTCAMPN", "hotkey" : 99, "help": 11, "command": "load campaign"},
{"x": 545, "y": 358, "name":"ZTTUTOR", "hotkey" : 116, "help": 13, "command": "load tutorial"},
{"x": 582, "y": 464, "name":"ZTBACK", "hotkey" : 27, "help": 14, "command": "to main"}
{"x": 545, "y": 8, "name":"GTSINGL", "hotkey" : 115, "help": 10, "command": "load single"},
{"x": 568, "y": 120, "name":"GTMULTI", "hotkey" : 109, "help": 12, "command": "load multi"},
{"x": 541, "y": 233, "name":"GTCAMPN", "hotkey" : 99, "help": 11, "command": "load campaign"},
{"x": 545, "y": 358, "name":"GTTUTOR", "hotkey" : 116, "help": 13, "command": "load tutorial"},
{"x": 582, "y": 464, "name":"GTBACK", "hotkey" : 27, "help": 14, "command": "to main"}
],
"images": [ {"x": 114, "y": 312, "name":"ZLOADGAM"} ]
"images": [ {"x": 114, "y": 312, "name":"LOADGAME"} ]
},
{
"name" : "campaign",
"buttons":
[
{"x": 535, "y": 4, "name":"ZSSSOD", "hotkey" : 119, "command": "campaigns sod"},
{"x": 494, "y": 117, "name":"ZSSROE", "hotkey" : 114, "command": "campaigns roe"},
{"x": 486, "y": 241, "name":"ZSSARM", "hotkey" : 97, "command": "campaigns ab"},
{"x": 550, "y": 358, "name":"ZSSCUS", "hotkey" : 99, "command": "start campaign"},
{"x": 582, "y": 464, "name":"ZTBACK", "hotkey" : 27, "command": "to new"}
{"x": 535, "y": 4, "name":"CSSSOD", "hotkey" : 119, "command": "campaigns sod"},
{"x": 494, "y": 117, "name":"CSSROE", "hotkey" : 114, "command": "campaigns roe"},
{"x": 486, "y": 241, "name":"CSSARM", "hotkey" : 97, "command": "campaigns ab"},
{"x": 550, "y": 358, "name":"CSSCUS", "hotkey" : 99, "command": "start campaign"},
{"x": 582, "y": 464, "name":"GTBACK", "hotkey" : 27, "command": "to new"}
],
}
]
@ -116,7 +116,7 @@
},
{
"name":"wog",
"name":"wog", /// wog campaigns, currently has no assigned button in campaign screen and thus unused
"images" : [ {"x": 0, "y": 0, "name":"CAMPZALL"} ],
"exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN", "hotkey" : 27},
"items":

View File

@ -69,7 +69,7 @@
},
"type": {
"type" : "string",
"enum" : [ "dir", "lod", "snd", "vid" ],
"enum" : [ "dir", "lod", "snd", "vid", "map" ],
"description" : "Type of data source"
}
}

View File

@ -89,9 +89,9 @@ std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, Ba
}
ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero,
bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg )
bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg )
{
TDmgRange range = calculateDmgRange(attacker, defender, shooting, charge, lucky, deathBlow, ballistaDoubleDmg);
TDmgRange range = calculateDmgRange(attacker, defender, shooting, charge, lucky, unlucky, deathBlow, ballistaDoubleDmg);
if(range.first != range.second)
{
@ -636,7 +636,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp
}
case BFieldType::CLOVER_FIELD:
{ //+2 luck bonus for neutral creatures
curB->addNewBonus(makeFeature(Bonus::LUCK, Bonus::ONE_BATTLE, 0, +2, Bonus::TERRAIN_OVERLAY)->addLimiter(make_shared<CreatureFactionLimiter>(-1)));
curB->addNewBonus(makeFeature(Bonus::LUCK, Bonus::ONE_BATTLE, 0, +2, Bonus::TERRAIN_OVERLAY)->addLimiter(make_shared<CreatureAlignmentLimiter>(EAlignment::NEUTRAL)));
break;
}
case BFieldType::EVIL_FOG:

View File

@ -97,7 +97,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
shared_ptr<CObstacleInstance> getObstacleOnTile(BattleHex tile) const;
std::set<BattleHex> getStoppers(bool whichSidePerspective) const;
ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg); //charge - number of hexes travelled before attack (for champion's jousting)
ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg); //charge - number of hexes travelled before attack (for champion's jousting)
void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
//void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee

View File

@ -768,9 +768,9 @@ bool CBattleInfoCallback::battleCanShoot(const CStack * stack, BattleHex dest) c
}
TDmgRange CBattleInfoCallback::calculateDmgRange(const CStack* attacker, const CStack* defender, bool shooting,
ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg) const
ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg) const
{
return calculateDmgRange(attacker, defender, attacker->count, shooting, charge, lucky, deathBlow, ballistaDoubleDmg);
return calculateDmgRange(attacker, defender, attacker->count, shooting, charge, lucky, unlucky, deathBlow, ballistaDoubleDmg);
}
TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) const
@ -878,6 +878,11 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
{
additiveBonus += 1.0;
}
//unlucky hit, used only if negative luck is enabled
if (info.unluckyHit)
{
additiveBonus -= 0.5; // FIXME: how bad (and luck in general) should work with following bonuses?
}
//ballista double dmg
if(info.ballistaDoubleDamage)
@ -965,12 +970,13 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
}
TDmgRange CBattleInfoCallback::calculateDmgRange( const CStack* attacker, const CStack* defender, TQuantity attackerCount,
bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg ) const
bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg ) const
{
BattleAttackInfo bai(attacker, defender, shooting);
bai.attackerCount = attackerCount;
bai.chargedFields = charge;
bai.luckyHit = lucky;
bai.unluckyHit = unlucky;
bai.deathBlow = deathBlow;
bai.ballistaDoubleDamage = ballistaDoubleDmg;
return calculateDmgRange(bai);

View File

@ -203,6 +203,7 @@ struct DLL_LINKAGE BattleAttackInfo
int chargedFields;
bool luckyHit;
bool unluckyHit;
bool deathBlow;
bool ballistaDoubleDamage;
@ -235,8 +236,8 @@ public:
std::set<const CStack*> batteAdjacentCreatures (const CStack * stack) const;
TDmgRange calculateDmgRange(const BattleAttackInfo &info) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, TQuantity attackerCount, bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, TQuantity attackerCount, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
//hextowallpart //int battleGetWallUnderHex(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
std::pair<ui32, ui32> battleEstimateDamage(const BattleAttackInfo &bai, std::pair<ui32, ui32> * retaliationDmg = NULL) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>

View File

@ -272,7 +272,7 @@ std::vector<JsonNode> CCreatureHandler::loadLegacyData(size_t dataSize)
std::vector<JsonNode> h3Data;
h3Data.reserve(dataSize);
CLegacyConfigParser parser("DATA/ZCRTRAIT.TXT");
CLegacyConfigParser parser("DATA/CRTRAITS.TXT");
parser.endLine(); // header
parser.endLine();
@ -619,7 +619,10 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
BOOST_FOREACH(const JsonNode &ability, config["abilities"].Vector())
{
if (ability.getType() == JsonNode::DATA_VECTOR)
{
assert(0); // should be unused now
AddAbility(creature, ability.Vector()); // used only for H3 creatures
}
else
{
auto b = JsonUtils::parseBonus(ability);

View File

@ -50,7 +50,7 @@ CDefObjInfoHandler::CDefObjInfoHandler()
{
VLC->dobjinfo = this;
auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/ZOBJCTS.TXT"));
auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/OBJECTS.TXT"));
std::istringstream inp(std::string((char*)textFile.first.get(), textFile.second));
int objNumber;

View File

@ -163,7 +163,7 @@ CGeneralTextHandler::CGeneralTextHandler()
readToVector("DATA/RESTYPES.TXT", restypes);
readToVector("DATA/TERRNAME.TXT", terrainNames);
readToVector("DATA/RANDSIGN.TXT", randsign);
readToVector("DATA/ZCRGN1.TXT", creGens);
readToVector("DATA/CRGEN1.TXT", creGens);
readToVector("DATA/CRGEN4.TXT", creGens4);
readToVector("DATA/OVERVIEW.TXT", overview);
readToVector("DATA/ARRAYTXT.TXT", arraytxt);
@ -186,7 +186,7 @@ CGeneralTextHandler::CGeneralTextHandler()
while (parser.endLine());
}
{
CLegacyConfigParser parser("DATA/ZELP.TXT");
CLegacyConfigParser parser("DATA/HELP.TXT");
do
{
std::string first = parser.readString();

View File

@ -13,6 +13,7 @@ set(lib_SRCS
filesystem/CResourceLoader.cpp
filesystem/CFileInputStream.cpp
filesystem/CCompressedStream.cpp
filesystem/CMappedFileLoader.cpp
logging/CBasicLogConfigurator.cpp
logging/CLogger.cpp
mapping/CCampaignHandler.cpp

View File

@ -33,36 +33,13 @@ public:
*/
explicit CFilesystemLoader(const std::string & baseDirectory, size_t depth = 16, bool initial = false);
/**
* Loads a resource with the given resource name.
*
* @param resourceName The unqiue resource name in space of the filesystem.
* @return a input stream object, not null
*/
std::unique_ptr<CInputStream> load(const std::string & resourceName) const;
/**
* Checks if the file entry exists.
*
* @return true if the entry exists, false if not.
*/
bool existsEntry(const std::string & resourceName) const;
/**
* Gets all entries in the filesystem.
*
* @return a list of all entries in the filesystem.
*/
boost::unordered_map<ResourceID, std::string> getEntries() const;
/**
* Gets the origin of the archive loader.
*
* @return the file path to directory with archive (e.g. path/to/h3/mp3)
*/
std::string getOrigin() const;
bool createEntry(std::string filename);
/// Interface implementation
/// @see ISimpleResourceLoader
std::unique_ptr<CInputStream> load(const std::string & resourceName) const override;
bool existsEntry(const std::string & resourceName) const override;
boost::unordered_map<ResourceID, std::string> getEntries() const override;
std::string getOrigin() const override;
bool createEntry(std::string filename) override;
private:
/** The base directory which is scanned and indexed. */

View File

@ -57,46 +57,16 @@ public:
*/
explicit CLodArchiveLoader(const std::string & archive);
/**
* Loads a resource with the given resource name.
*
* @param resourceName The unqiue resource name in space of the archive.
* @return a input stream object, not null.
*
* @throws std::runtime_error if the archive entry wasn't found
*/
std::unique_ptr<CInputStream> load(const std::string & resourceName) const;
/**
* Gets all entries in the archive.
*
* @return a list of all entries in the archive.
*/
boost::unordered_map<ResourceID, std::string> getEntries() const;
/**
* Gets the archive entry for the requested resource
*
* @param resourceName The unqiue resource name in space of the archive.
* @return the archive entry for the requested resource or a null ptr if the archive wasn't found
*/
const ArchiveEntry * getArchiveEntry(const std::string & resourceName) const;
/**
* Checks if the archive entry exists.
*
* @return true if the entry exists, false if not.
*/
bool existsEntry(const std::string & resourceName) const;
/**
* Gets the origin of the archive loader.
*
* @return the file path to the archive which is scanned and indexed.
*/
std::string getOrigin() const;
/// Interface implementation
/// @see ISimpleResourceLoader
std::unique_ptr<CInputStream> load(const std::string & resourceName) const override;
boost::unordered_map<ResourceID, std::string> getEntries() const override;
bool existsEntry(const std::string & resourceName) const override;
std::string getOrigin() const override;
private:
const ArchiveEntry * getArchiveEntry(const std::string & resourceName) const;
/**
* Initializes a LOD archive.
*

View File

@ -0,0 +1,45 @@
#include "StdInc.h"
#include "CMappedFileLoader.h"
#include "CResourceLoader.h"
#include "../JsonNode.h"
CMappedFileLoader::CMappedFileLoader(const JsonNode &config)
{
BOOST_FOREACH(auto entry, config.Struct())
{
fileList[ResourceID(entry.first)] = entry.second.String();
}
}
std::unique_ptr<CInputStream> CMappedFileLoader::load(const std::string & resourceName) const
{
return CResourceHandler::get()->load(ResourceID(resourceName));
}
bool CMappedFileLoader::existsEntry(const std::string & resourceName) const
{
for(auto it = fileList.begin(); it != fileList.end(); ++it)
{
if(it->second == resourceName)
{
return true;
}
}
return false;
}
boost::unordered_map<ResourceID, std::string> CMappedFileLoader::getEntries() const
{
return fileList;
}
std::string CMappedFileLoader::getOrigin() const
{
return ""; // does not have any meaning with this type of data source
}
std::string CMappedFileLoader::getFullName(const std::string & resourceName) const
{
return CResourceHandler::get()->getResourceName(ResourceID(resourceName));
}

View File

@ -0,0 +1,53 @@
/*
* CMappedFileLoader.h, 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
*
*/
#pragma once
#include "ISimpleResourceLoader.h"
#include "CResourceLoader.h"
class CFileInfo;
class CInputStream;
/**
* Class that implements file mapping (aka *nix symbolic links)
* Uses json file as input, content is map:
* "fileA.txt" : "fileB.txt"
* Note that extension is necessary, but used only to determine type
*
* fileA - file which will be replaced
* fileB - file which will be used as replacement
*/
class DLL_LINKAGE CMappedFileLoader : public ISimpleResourceLoader
{
public:
/**
* Ctor.
*
* @param config Specifies filesystem configuration
*/
explicit CMappedFileLoader(const JsonNode & config);
/// Interface implementation
/// @see ISimpleResourceLoader
std::unique_ptr<CInputStream> load(const std::string & resourceName) const override;
bool existsEntry(const std::string & resourceName) const override;
boost::unordered_map<ResourceID, std::string> getEntries() const override;
std::string getOrigin() const override;
std::string getFullName(const std::string & resourceName) const override;
private:
/** A list of files in this map
* key = ResourceID for resource loader
* value = ResourceID to which file this request will be redirected
*/
boost::unordered_map<ResourceID, std::string> fileList;
};

View File

@ -3,6 +3,7 @@
#include "CFileInfo.h"
#include "CLodArchiveLoader.h"
#include "CFilesystemLoader.h"
#include "CMappedFileLoader.h"
//For filesystem initialization
#include "../JsonNode.h"
@ -127,7 +128,7 @@ std::string CResourceLoader::getResourceName(const ResourceID & resourceIdent) c
{
auto locator = getResource(resourceIdent);
if (locator.getLoader())
return locator.getLoader()->getOrigin() + '/' + locator.getResourceName();
return locator.getLoader()->getFullName(locator.getResourceName());
return "";
}
@ -372,6 +373,22 @@ void CResourceHandler::loadArchive(const std::string &prefix, const std::string
shared_ptr<ISimpleResourceLoader>(new CLodArchiveLoader(filename)), false);
}
void CResourceHandler::loadJsonMap(const std::string &prefix, const std::string &mountPoint, const JsonNode & config)
{
std::string URI = prefix + config["path"].String();
std::string filename = initialLoader->getResourceName(ResourceID(URI, EResType::TEXT));
if (!filename.empty())
{
auto configData = initialLoader->loadData(ResourceID(URI, EResType::TEXT));
const JsonNode config((char*)configData.first.get(), configData.second);
resourceLoader->addLoader(mountPoint,
shared_ptr<ISimpleResourceLoader>(new CMappedFileLoader(config)), false);
}
}
void CResourceHandler::loadFileSystem(const std::string & prefix, const std::string &fsConfigURI)
{
auto fsConfigData = initialLoader->loadData(ResourceID(fsConfigURI, EResType::TEXT));
@ -390,6 +407,8 @@ void CResourceHandler::loadFileSystem(const std::string & prefix, const JsonNode
CStopWatch timer;
logGlobal->debugStream() << "\t\tLoading resource at " << prefix + entry["path"].String();
if (entry["type"].String() == "map")
loadJsonMap(prefix, mountPoint.first, entry);
if (entry["type"].String() == "dir")
loadDirectory(prefix, mountPoint.first, entry);
if (entry["type"].String() == "lod")
@ -423,8 +442,19 @@ std::vector<std::string> CResourceHandler::getAvailableMods()
name.erase(0, name.find_last_of('/') + 1); //Remove path prefix
if (name == "WOG") // check if wog is actually present. Hack-ish but better than crash
{
if (!initialLoader->existsResource(ResourceID("ALL/DATA/ZVS", EResType::DIRECTORY)) &&
!initialLoader->existsResource(ResourceID("ALL/MODS/WOG/DATA/ZVS", EResType::DIRECTORY)))
{
++iterator;
continue;
}
}
if (!name.empty()) // this is also triggered for "ALL/MODS/" entry
foundMods.push_back(name);
++iterator;
}
return foundMods;

View File

@ -130,32 +130,9 @@ public:
return *this;
}
/**
* Gets the name of the identifier.
*
* @return the name of the identifier
*/
std::string getName() const;
/**
* Gets the type of the identifier.
*
* @return the type of the identifier
*/
EResType::Type getType() const;
/**
* Sets the name of the identifier.
*
* @param name the name of the identifier. No extension, will be converted to uppercase.
*/
void setName(std::string name);
/**
* Sets the type of the identifier.
*
* @param type the type of the identifier.
*/
void setType(EResType::Type type);
protected:
@ -390,6 +367,7 @@ public:
static void loadFileSystem(const std::string &prefix, const JsonNode & fsConfig);
static void loadDirectory(const std::string &prefix, const std::string & mountPoint, const JsonNode & config);
static void loadArchive(const std::string &prefix, const std::string & mountPoint, const JsonNode & config, EResType::Type archiveType);
static void loadJsonMap(const std::string &prefix, const std::string & mountPoint, const JsonNode & config);
/**
* Checks all subfolders of MODS directory for presence of mods

View File

@ -54,6 +54,14 @@ public:
*/
virtual std::string getOrigin() const =0;
/**
* Gets full name of resource, e.g. name of file in filesystem.
*/
virtual std::string getFullName(const std::string & resourceName) const
{
return getOrigin() + '/' + resourceName;
}
/**
* Creates new resource with specified filename.
*

View File

@ -697,17 +697,23 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
int attackerLuck = att->LuckVal();
const CGHeroInstance * h0 = gs->curB->heroes[0],
* h1 = gs->curB->heroes[1];
bool noLuck = false;
if((h0 && NBonus::hasOfType(h0, Bonus::BLOCK_LUCK)) ||
(h1 && NBonus::hasOfType(h1, Bonus::BLOCK_LUCK)))
if(!(h0 && NBonus::hasOfType(h0, Bonus::BLOCK_LUCK)) &&
!(h1 && NBonus::hasOfType(h1, Bonus::BLOCK_LUCK)))
{
noLuck = true;
if(attackerLuck > 0 && rand()%24 < attackerLuck)
{
bat.flags |= BattleAttack::LUCKY;
}
if (VLC->modh->settings.data["hardcodedFeatures"]["NEGATIVE_LUCK"].Bool()) // negative luck enabled
{
if (attackerLuck < 0 && rand()%24 < abs(attackerLuck))
{
bat.flags |= BattleAttack::UNLUCKY;
}
}
}
if(!noLuck && attackerLuck > 0 && rand()%24 < attackerLuck) //TODO?: negative luck option?
{
bat.flags |= BattleAttack::LUCKY;
}
if (rand()%100 < att->valOfBonuses(Bonus::DOUBLE_DAMAGE_CHANCE))
{
bat.flags |= BattleAttack::DEATH_BLOW;
@ -764,7 +770,7 @@ void CGameHandler::applyBattleEffects(BattleAttack &bat, const CStack *att, cons
bsa.flags |= BattleStackAttacked::SECONDARY; //all other targets do not suffer from spells & spell-like abilities
bsa.attackerID = att->ID;
bsa.stackAttacked = def->ID;
bsa.damageAmount = gs->curB->calculateDmg(att, def, gs->curB->battleGetOwner(att), gs->curB->battleGetOwner(def), bat.shot(), distance, bat.lucky(), bat.deathBlow(), bat.ballistaDoubleDmg());
bsa.damageAmount = gs->curB->calculateDmg(att, def, gs->curB->battleGetOwner(att), gs->curB->battleGetOwner(def), bat.shot(), distance, bat.lucky(), bat.unlucky(), bat.deathBlow(), bat.ballistaDoubleDmg());
def->prepareAttacked(bsa); //calculate casualties
//life drain handling