diff --git a/CMakeLists.txt b/CMakeLists.txt index 48a61b3dc..af7719dda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/Mods/WoG/config/defaultMods.json b/Mods/WoG/config/defaultMods.json new file mode 100644 index 000000000..89e8f70f1 --- /dev/null +++ b/Mods/WoG/config/defaultMods.json @@ -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 + } +} diff --git a/Mods/WoG/config/wogFileOverrides.json b/Mods/WoG/config/wogFileOverrides.json new file mode 100644 index 000000000..ecffd762d --- /dev/null +++ b/Mods/WoG/config/wogFileOverrides.json @@ -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" +} diff --git a/Mods/WoG/mod.json b/Mods/WoG/mod.json index 17b4944fb..c760f62fc 100644 --- a/Mods/WoG/mod.json +++ b/Mods/WoG/mod.json @@ -1,6 +1,10 @@ { "filesystem": { + "" : + [ + { "type" : "map", "path" : "/Config/wogFileOverrides.json"} + ], "CONFIG/" : [ { "type" : "dir", "path" : "/Config"} diff --git a/Mods/vcmi/mod.json b/Mods/vcmi/mod.json index 9ddae299d..d9e284bae 100644 --- a/Mods/vcmi/mod.json +++ b/Mods/vcmi/mod.json @@ -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" } diff --git a/client/CCastleInterface.cpp b/client/CCastleInterface.cpp index 3fe772b15..fcd7deef1 100644 --- a/client/CCastleInterface.cpp +++ b/client/CCastleInterface.cpp @@ -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; diff --git a/client/CMT.cpp b/client/CMT.cpp index ec4fc6795..5bbaac03f 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -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 diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index d9e42aa1b..e7699b9ca 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -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); } diff --git a/client/CSoundBase.h b/client/CSoundBase.h index 395212ba4..c67ff1010 100644 --- a/client/CSoundBase.h +++ b/client/CSoundBase.h @@ -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) \ diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index 6dc427aca..8aec79735 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -556,8 +556,8 @@ void CGarrisonInt::createSlots() void CGarrisonInt::recreateSlots() { - setSplittingMode(false); selectSlot(nullptr); + setSplittingMode(false); for(size_t i = 0; iblock(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; diff --git a/client/battle/CBattleAnimations.cpp b/client/battle/CBattleAnimations.cpp index 190cff18d..9af720076 100644 --- a/client/battle/CBattleAnimations.cpp +++ b/client/battle/CBattleAnimations.cpp @@ -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(); } diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 5a3fb0648..832a3945b 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -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; } diff --git a/config/artifacts.json b/config/artifacts.json index 56a2904b6..a4d1848cd 100644 --- a/config/artifacts.json +++ b/config/artifacts.json @@ -2,7 +2,7 @@ "spellBook": { "index" : 0, - "type" : ["HERO"] + "type" : ["HERO"], }, "spellScroll": { diff --git a/config/creatures/inferno.json b/config/creatures/inferno.json index f4ce7bbff..e00fece33 100644 --- a/config/creatures/inferno.json +++ b/config/creatures/inferno.json @@ -398,7 +398,7 @@ "hateArchAngels" : { "type" : "HATE", - "subtype" : "creature.angel", + "subtype" : "creature.archangel", "val" : 50 }, "FLYING_ARMY" : diff --git a/config/defaultMods.json b/config/defaultMods.json index efa3c655b..c85e52cf3 100644 --- a/config/defaultMods.json +++ b/config/defaultMods.json @@ -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 } } diff --git a/config/mainmenu.json b/config/mainmenu.json index 9c3a6938c..2293f3db4 100644 --- a/config/mainmenu.json +++ b/config/mainmenu.json @@ -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": diff --git a/config/schemas/mod.json b/config/schemas/mod.json index 17a91a576..c97aecc2a 100644 --- a/config/schemas/mod.json +++ b/config/schemas/mod.json @@ -69,7 +69,7 @@ }, "type": { "type" : "string", - "enum" : [ "dir", "lod", "snd", "vid" ], + "enum" : [ "dir", "lod", "snd", "vid", "map" ], "description" : "Type of data source" } } diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index 1b17f2db0..d6efd0cc1 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -89,9 +89,9 @@ std::pair< std::vector, 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(-1))); + curB->addNewBonus(makeFeature(Bonus::LUCK, Bonus::ONE_BATTLE, 0, +2, Bonus::TERRAIN_OVERLAY)->addLimiter(make_shared(EAlignment::NEUTRAL))); break; } case BFieldType::EVIL_FOG: diff --git a/lib/BattleState.h b/lib/BattleState.h index 340aa6a27..cddd937e2 100644 --- a/lib/BattleState.h +++ b/lib/BattleState.h @@ -97,7 +97,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb shared_ptr getObstacleOnTile(BattleHex tile) const; std::set 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 *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 diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index e98777641..4710d9aec 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -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); diff --git a/lib/CBattleCallback.h b/lib/CBattleCallback.h index 87a9161c4..a899513ee 100644 --- a/lib/CBattleCallback.h +++ b/lib/CBattleCallback.h @@ -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 batteAdjacentCreatures (const CStack * stack) const; TDmgRange calculateDmgRange(const BattleAttackInfo &info) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair - 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 - 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 + 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 + 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 //hextowallpart //int battleGetWallUnderHex(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found std::pair battleEstimateDamage(const BattleAttackInfo &bai, std::pair * retaliationDmg = NULL) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 1471575cc..a37289e03 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -272,7 +272,7 @@ std::vector CCreatureHandler::loadLegacyData(size_t dataSize) std::vector 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); diff --git a/lib/CDefObjInfoHandler.cpp b/lib/CDefObjInfoHandler.cpp index 2835166b4..120a98f7c 100644 --- a/lib/CDefObjInfoHandler.cpp +++ b/lib/CDefObjInfoHandler.cpp @@ -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; diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index 81339d194..0a7dd0d0b 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -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(); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index da7bc4911..04a552506 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -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 diff --git a/lib/filesystem/CFilesystemLoader.h b/lib/filesystem/CFilesystemLoader.h index 7a54bb41f..0fe1ade70 100644 --- a/lib/filesystem/CFilesystemLoader.h +++ b/lib/filesystem/CFilesystemLoader.h @@ -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 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 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 load(const std::string & resourceName) const override; + bool existsEntry(const std::string & resourceName) const override; + boost::unordered_map getEntries() const override; + std::string getOrigin() const override; + bool createEntry(std::string filename) override; private: /** The base directory which is scanned and indexed. */ diff --git a/lib/filesystem/CLodArchiveLoader.h b/lib/filesystem/CLodArchiveLoader.h index 2c1c7c61c..80c01bd72 100644 --- a/lib/filesystem/CLodArchiveLoader.h +++ b/lib/filesystem/CLodArchiveLoader.h @@ -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 load(const std::string & resourceName) const; - - /** - * Gets all entries in the archive. - * - * @return a list of all entries in the archive. - */ - boost::unordered_map 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 load(const std::string & resourceName) const override; + boost::unordered_map 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. * diff --git a/lib/filesystem/CMappedFileLoader.cpp b/lib/filesystem/CMappedFileLoader.cpp new file mode 100644 index 000000000..2bf0f5bab --- /dev/null +++ b/lib/filesystem/CMappedFileLoader.cpp @@ -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 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 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)); +} diff --git a/lib/filesystem/CMappedFileLoader.h b/lib/filesystem/CMappedFileLoader.h new file mode 100644 index 000000000..39b273c3b --- /dev/null +++ b/lib/filesystem/CMappedFileLoader.h @@ -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 load(const std::string & resourceName) const override; + bool existsEntry(const std::string & resourceName) const override; + boost::unordered_map 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 fileList; +}; diff --git a/lib/filesystem/CResourceLoader.cpp b/lib/filesystem/CResourceLoader.cpp index d268f37ff..e4d4d3d2c 100644 --- a/lib/filesystem/CResourceLoader.cpp +++ b/lib/filesystem/CResourceLoader.cpp @@ -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(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(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 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; diff --git a/lib/filesystem/CResourceLoader.h b/lib/filesystem/CResourceLoader.h index c2458a4a0..9e9654c36 100644 --- a/lib/filesystem/CResourceLoader.h +++ b/lib/filesystem/CResourceLoader.h @@ -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 diff --git a/lib/filesystem/ISimpleResourceLoader.h b/lib/filesystem/ISimpleResourceLoader.h index 00fd5342c..d7eaf0b4b 100644 --- a/lib/filesystem/ISimpleResourceLoader.h +++ b/lib/filesystem/ISimpleResourceLoader.h @@ -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. * diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index d78efd68f..dc36e1207 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -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