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

Merge branch 'develop' into feature/moatHexesConfig

This commit is contained in:
Arseniy Shestakov 2016-01-28 01:15:08 +03:00
commit 48aedaef47
75 changed files with 1548 additions and 739 deletions

1
.gitignore vendored
View File

@ -14,6 +14,7 @@
*.pro.user
*.pro.user.*
*.swp
*.h.gch
*~
/CMakeLists.txt.user
CMakeCache.txt

View File

@ -51,6 +51,7 @@
<Add option="-pedantic" />
<Add option="-Wextra" />
<Add option="-Wall" />
<Add option="-std=gnu++11" />
<Add option="-fexceptions" />
<Add option="-Wpointer-arith" />
<Add option="-Wno-switch" />

View File

@ -49,6 +49,7 @@
<Compiler>
<Add option="-Wextra" />
<Add option="-Wall" />
<Add option="-std=gnu++11" />
<Add option="-fexceptions" />
<Add option="-Wpointer-arith" />
<Add option="-Wno-switch" />

View File

@ -52,6 +52,7 @@
<Compiler>
<Add option="-Wextra" />
<Add option="-Wall" />
<Add option="-std=gnu++11" />
<Add option="-fexceptions" />
<Add option="-Wpointer-arith" />
<Add option="-Wno-switch" />

View File

@ -35,6 +35,7 @@
<Add option="-pedantic" />
<Add option="-Wextra" />
<Add option="-Wall" />
<Add option="-std=gnu++11" />
<Add option="-fexceptions" />
<Add option="-Wpointer-arith" />
<Add option="-Wno-switch" />

View File

@ -154,8 +154,8 @@ bool Goals::AbstractGoal::operator== (AbstractGoal &g)
//TODO: find out why the following are not generated automatically on MVS?
namespace Goals
{
namespace Goals
{
template <>
void CGoal<Win>::accept (VCAI * ai)
{
@ -275,7 +275,7 @@ TSubgoal Win::whatToDoToAchieve()
// 0.85 -> radius now 2 tiles
// 0.95 -> 1 tile radius, position is fully known
// AFAIK H3 AI does something like this
int3 grailPos = cb->getGrailPos(ratio);
int3 grailPos = cb->getGrailPos(&ratio);
if(ratio > 0.99)
{
return sptr (Goals::DigAtTile(grailPos));
@ -367,7 +367,7 @@ TSubgoal FindObj::whatToDoToAchieve()
std::string GetObj::completeMessage() const
{
return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
}
TSubgoal GetObj::whatToDoToAchieve()
@ -409,7 +409,7 @@ bool GetObj::fulfillsMe (TSubgoal goal)
std::string VisitHero::completeMessage() const
{
return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast<std::string>(objid);
return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast<std::string>(objid);
}
TSubgoal VisitHero::whatToDoToAchieve()
@ -435,10 +435,18 @@ TSubgoal VisitHero::whatToDoToAchieve()
bool VisitHero::fulfillsMe (TSubgoal goal)
{
if (goal->goalType == Goals::VISIT_TILE && cb->getObj(ObjectInstanceID(objid))->visitablePos() == goal->tile)
return true;
else
if (goal->goalType != Goals::VISIT_TILE)
{
return false;
}
auto obj = cb->getObj(ObjectInstanceID(objid));
if (!obj)
{
logAi->errorStream() << boost::format("Hero %s: VisitHero::fulfillsMe at %s: object %d not found")
% hero.name % goal->tile % objid;
return false;
}
return obj->visitablePos() == goal->tile;
}
TSubgoal GetArtOfType::whatToDoToAchieve()
@ -458,7 +466,7 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
return sptr (Goals::Explore());
}
return (fh->chooseSolution(getAllPossibleSubgoals()));
return (fh->chooseSolution(getAllPossibleSubgoals()));
}
TGoalVec ClearWayTo::getAllPossibleSubgoals()
@ -862,7 +870,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
{
auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1);
if(!creatures)
continue;
continue;
int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber);
if(upgradeNumber < 0)
@ -957,7 +965,7 @@ TGoalVec Conquer::getAllPossibleSubgoals()
std::vector<const CGObjectInstance *> objs;
for (auto obj : ai->visitableObjs)
{
if (conquerable(obj))
if (conquerable(obj))
objs.push_back (obj);
}
@ -1035,7 +1043,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
{
//get all possible towns, heroes and dwellings we may use
TGoalVec ret;
//TODO: include evaluation of monsters gather in calculation
for (auto t : cb->getTownsInfo())
{

View File

@ -3,7 +3,7 @@
<FileVersion major="1" minor="6" />
<Project>
<Option title="VCAI" />
<Option pch_mode="2" />
<Option pch_mode="0" />
<Option compiler="gcc" />
<Build>
<Target title="Debug-win32">
@ -57,6 +57,7 @@
<Add option="-pedantic" />
<Add option="-Wextra" />
<Add option="-Wall" />
<Add option="-std=gnu++11" />
<Add option="-fexceptions" />
<Add option="-Wpointer-arith" />
<Add option="-Wno-switch" />

View File

@ -1987,7 +1987,6 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
if(teleportChannelProbingList.size())
doChannelProbing();
}
ret = !i;
}
if (h)
{
@ -2002,6 +2001,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
completeGoal (sptr(Goals::VisitTile(dst).sethero(h))); //we stepped on some tile, anyway
completeGoal (sptr(Goals::ClearWayTo(dst).sethero(h)));
ret = (dst == h->visitablePos());
if(!ret) //reserve object we are heading towards
{
auto obj = vstd::frontOrNull(cb->getVisitableObjs(dst));
@ -2020,7 +2021,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
}
void VCAI::tryRealize(Goals::Explore & g)
{
throw cannotFulfillGoalException("EXPLORE is not a elementar goal!");
throw cannotFulfillGoalException("EXPLORE is not an elementar goal!");
}
void VCAI::tryRealize(Goals::RecruitHero & g)
@ -2853,7 +2854,7 @@ BattleState AIStatus::getBattle()
}
void AIStatus::addQuery(QueryID ID, std::string description)
{
{
if(ID == QueryID(-1))
{
logAi->debugStream() << boost::format("The \"query\" has an id %d, it'll be ignored as non-query. Description: %s") % ID % description;
@ -2878,7 +2879,7 @@ void AIStatus::removeQuery(QueryID ID)
std::string description = remainingQueries[ID];
remainingQueries.erase(ID);
cv.notify_all();
logAi->debugStream() << boost::format("Removing query %d - %s. Total queries count: %d") % ID % description % remainingQueries.size();
}

View File

@ -8,6 +8,8 @@ GENERAL:
- Angel Wings
- Boots of Levitation
* Implemented rumors in tavern window
* New cheat code:
- vcmiglaurung - gives 5000 crystal dragons into each slot
ADVETURE AI:
* Fixed AI trying to go through underground rock
@ -19,6 +21,8 @@ RANDOM MAP GENERATOR:
* Changed fractalization algorithm so it can create cycles
* Zones will not have straight paths anymore, they are totally random
* Added Thieves Guild random object (1 per zone)
* Added Seer Huts with quests that match OH3
* RMG will guarantee at least 100 pairs of Monoliths are available even if there are not enough different defs
0.97 -> 0.98
GENERAL:

View File

@ -7,6 +7,7 @@
#include "mapHandler.h"
#include "../lib/filesystem/Filesystem.h"
#include "../lib/filesystem/FileStream.h"
#include "CPreGame.h"
#include "windows/CCastleInterface.h"
#include "../lib/CConsoleHandler.h"
@ -79,7 +80,7 @@ extern boost::thread_specific_ptr<bool> inGuiThread;
SDL_Surface *screen = nullptr, //main screen surface
*screen2 = nullptr, //and hlp surface (used to store not-active interfaces layer)
*screenBuf = screen; //points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
std::queue<SDL_Event> events;
boost::mutex eventsM;
@ -187,7 +188,7 @@ static void SDLLogCallback(void* userdata,
{
//todo: convert SDL log priority to vcmi log priority
//todo: make separate log domain for SDL
logGlobal->debugStream() << "SDL(category " << category << "; priority " <<priority <<") "<<message;
}
@ -363,21 +364,21 @@ int main(int argc, char** argv)
}
GH.mainFPSmng->init(); //(!)init here AFTER SDL_Init() while using SDL for FPS management
atexit(SDL_Quit);
SDL_LogSetOutputFunction(&SDLLogCallback, nullptr);
int driversCount = SDL_GetNumRenderDrivers();
std::string preferredDriverName = video["driver"].String();
logGlobal->infoStream() << "Found " << driversCount << " render drivers";
for(int it = 0; it < driversCount; it++)
{
SDL_RendererInfo info;
SDL_GetRenderDriverInfo(it,&info);
std::string driverName(info.name);
if(!preferredDriverName.empty() && driverName == preferredDriverName)
{
preferredDriverIndex = it;
@ -385,8 +386,8 @@ int main(int argc, char** argv)
}
else
logGlobal->infoStream() << "\t" << driverName;
}
}
config::CConfigHandler::GuiOptionsMap::key_type resPair(res["width"].Float(), res["height"].Float());
if (conf.guiOptions.count(resPair) == 0)
{
@ -440,10 +441,15 @@ int main(int argc, char** argv)
CCS->musich->setVolume(settings["general"]["music"].Float());
logGlobal->infoStream()<<"Initializing screen and sound handling: "<<pomtime.getDiff();
#ifdef __APPLE__
// Ctrl+click should be treated as a right click on Mac OS X
SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1");
#endif
#ifndef VCMI_NO_THREADED_LOAD
//we can properly play intro only in the main thread, so we have to move loading to the separate thread
boost::thread loading(init);
#else
#else
init();
#endif
@ -632,7 +638,7 @@ void processCommand(const std::string &message)
for (auto & filename : list)
{
const bfs::path filePath = outPath / (filename.getName() + ".TXT");
bfs::create_directories(filePath.parent_path());
bfs::ofstream file(filePath);
@ -845,25 +851,25 @@ static bool checkVideoMode(int monitorIndex, int w, int h, int& bpp, bool fullsc
return true;
}
}
return false;
return false;
}
static bool recreateWindow(int w, int h, int bpp, bool fullscreen)
{
// VCMI will only work with 2 or 4 bytes per pixel
// VCMI will only work with 2 or 4 bytes per pixel
vstd::amax(bpp, 16);
vstd::amin(bpp, 32);
if(bpp>16)
bpp = 32;
int suggestedBpp = bpp;
if(!checkVideoMode(0,w,h,suggestedBpp,fullscreen))
{
logGlobal->errorStream() << "Error: SDL says that " << w << "x" << h << " resolution is not available!";
return false;
}
}
bool bufOnScreen = (screenBuf == screen);
screenBuf = nullptr; //it`s a link - just nullify
@ -873,34 +879,34 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen)
SDL_FreeSurface(screen2);
screen2 = nullptr;
}
if(nullptr != screen)
{
SDL_FreeSurface(screen);
screen = nullptr;
}
}
if(nullptr != screenTexture)
{
SDL_DestroyTexture(screenTexture);
screenTexture = nullptr;
}
if(nullptr != mainRenderer)
if(nullptr != mainRenderer)
{
SDL_DestroyRenderer(mainRenderer);
mainRenderer = nullptr;
}
if(nullptr != mainWindow)
{
SDL_DestroyWindow(mainWindow);
mainWindow = nullptr;
}
}
if(fullscreen)
{
//in full-screen mode always use desktop resolution
@ -911,33 +917,33 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen)
{
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED, w, h, 0);
}
if(nullptr == mainWindow)
{
throw std::runtime_error("Unable to create window\n");
}
//create first available renderer if preferred not set. Use no flags, so HW accelerated will be preferred but SW renderer also will possible
mainRenderer = SDL_CreateRenderer(mainWindow,preferredDriverIndex,0);
if(nullptr == mainRenderer)
{
throw std::runtime_error("Unable to create renderer\n");
}
}
SDL_RendererInfo info;
SDL_GetRendererInfo(mainRenderer,&info);
logGlobal->infoStream() << "Created renderer " << info.name;
logGlobal->infoStream() << "Created renderer " << info.name;
SDL_RenderSetLogicalSize(mainRenderer, w, h);
SDL_RenderSetViewport(mainRenderer, nullptr);
#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
int bmask = 0xff000000;
int gmask = 0x00ff0000;
@ -955,13 +961,13 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen)
{
logGlobal->errorStream() << "Unable to create surface";
logGlobal->errorStream() << w << " "<< h << " "<< bpp;
logGlobal->errorStream() << SDL_GetError();
throw std::runtime_error("Unable to create surface");
}
}
//No blending for screen itself. Required for proper cursor rendering.
SDL_SetSurfaceBlendMode(screen, SDL_BLENDMODE_NONE);
screenTexture = SDL_CreateTexture(mainRenderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
@ -972,23 +978,23 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen)
logGlobal->errorStream() << "Unable to create screen texture";
logGlobal->errorStream() << SDL_GetError();
throw std::runtime_error("Unable to create screen texture");
}
}
screen2 = CSDL_Ext::copySurface(screen);
if(nullptr == screen2)
{
throw std::runtime_error("Unable to copy surface\n");
}
}
screenBuf = bufOnScreen ? screen : screen2;
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 0);
SDL_RenderClear(mainRenderer);
SDL_RenderPresent(mainRenderer);
return true;
return true;
}
//used only once during initialization
@ -997,7 +1003,7 @@ static void setScreenRes(int w, int h, int bpp, bool fullscreen, bool resetVideo
if(!recreateWindow(w,h,bpp,fullscreen))
{
throw std::runtime_error("Requested screen resolution is not available\n");
}
}
}
static void fullScreenChanged()
@ -1008,16 +1014,16 @@ static void fullScreenChanged()
const bool toFullscreen = full->Bool();
auto bitsPerPixel = screen->format->BitsPerPixel;
auto w = screen->w;
auto h = screen->h;
if(!recreateWindow(w,h,bitsPerPixel,toFullscreen))
{
//will return false and report error if video mode is not supported
return;
}
return;
}
GH.totalRedraw();
}
@ -1025,7 +1031,7 @@ static void handleEvent(SDL_Event & ev)
{
if((ev.type==SDL_QUIT) ||(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT)))
{
handleQuit();
handleQuit();
return;
}
else if(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4)
@ -1040,8 +1046,8 @@ static void handleEvent(SDL_Event & ev)
{
case FORCE_QUIT:
{
handleQuit(false);
return;
handleQuit(false);
return;
}
break;
case RETURN_TO_MAIN_MENU:
@ -1080,8 +1086,8 @@ static void handleEvent(SDL_Event & ev)
fullScreenChanged();
break;
default:
logGlobal->errorStream() << "Unknown user event. Code " << ev.user.code;
break;
logGlobal->errorStream() << "Unknown user event. Code " << ev.user.code;
break;
}
return;
@ -1098,8 +1104,8 @@ static void handleEvent(SDL_Event & ev)
{
boost::unique_lock<boost::mutex> lock(eventsM);
events.push(ev);
}
}
}
@ -1114,12 +1120,12 @@ static void mainLoop()
while(1) //main SDL events loop
{
SDL_Event ev;
while(1 == SDL_PollEvent(&ev))
{
handleEvent(ev);
}
GH.renderFrame();
}

View File

@ -119,7 +119,7 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player)
isAutoFightOn = false;
duringMovement = false;
ignoreEvents = false;
ignoreEvents = false;
}
CPlayerInterface::~CPlayerInterface()
@ -647,6 +647,8 @@ void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet
autofightingAI->battleStart(army1, army2, int3(0,0,0), hero1, hero2, side);
isAutoFightOn = true;
cb->registerBattleInterface(autofightingAI);
// Player shouldn't be able to move on adventure map if quick combat is going
adventureInt->quickCombatLock();
}
//Don't wait for dialogs when we are non-active hot-seat player
@ -822,16 +824,16 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i
//tidy up
BattleAction ret = *(b->givenCommand->data);
vstd::clear_pointer(b->givenCommand->data);
if(ret.actionType == Battle::CANCEL)
{
if(stackId != ret.stackNumber)
logGlobal->error("Not current active stack action canceled");
logGlobal->traceStream() << "Canceled command for " << stackName;
logGlobal->traceStream() << "Canceled command for " << stackName;
}
else
logGlobal->traceStream() << "Giving command for " << stackName;
return ret;
}
@ -843,6 +845,7 @@ void CPlayerInterface::battleEnd(const BattleResult *br)
isAutoFightOn = false;
cb->unregisterBattleInterface(autofightingAI);
autofightingAI.reset();
adventureInt->quickCombatUnlock();
if(!battleInt)
{
@ -1332,7 +1335,7 @@ void CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path )
if(showingDialog->get() || !dialogs.empty())
return;
duringMovement = true;
setMovementStatus(true);
if (adventureInt && adventureInt->isHeroSleeping(h))
{
@ -1344,8 +1347,6 @@ void CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path )
}
boost::thread moveHeroTask(std::bind(&CPlayerInterface::doMoveHero,this,h,path));
}
bool CPlayerInterface::shiftPressed() const
@ -1556,6 +1557,7 @@ void CPlayerInterface::centerView (int3 pos, int focusTime)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
waitWhileDialog();
CCS->curh->hide();
adventureInt->centerOn (pos);
if(focusTime)
{
@ -1566,6 +1568,7 @@ void CPlayerInterface::centerView (int3 pos, int focusTime)
SDL_Delay(focusTime);
}
}
CCS->curh->show();
}
void CPlayerInterface::objectRemoved( const CGObjectInstance *obj )
@ -1607,11 +1610,11 @@ void CPlayerInterface::update()
{
// Make sure that gamestate won't change when GUI objects may obtain its parts on event processing or drawing request
boost::shared_lock<boost::shared_mutex> gsLock(cb->getGsMutex());
// While mutexes were locked away we may be have stopped being the active interface
// While mutexes were locked away we may be have stopped being the active interface
if(LOCPLINT != this)
return;
//if there are any waiting dialogs, show them
if((howManyPeople <= 1 || makingTurn) && !dialogs.empty() && !showingDialog->get())
{
@ -2167,7 +2170,7 @@ void CPlayerInterface::showPuzzleMap()
//TODO: interface should not know the real position of Grail...
double ratio = 0;
int3 grailPos = cb->getGrailPos(ratio);
int3 grailPos = cb->getGrailPos(&ratio);
GH.pushInt(new CPuzzleWindow(grailPos, ratio));
}
@ -2192,7 +2195,7 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI
int level = caster->getSpellSchoolLevel(spell);
adventureInt->worldViewOptions.showAllTerrain = (level>2);
}
auto castSoundPath = spell->getCastSound();
if (!castSoundPath.empty())
CCS->soundh->playSound(castSoundPath);
@ -2507,9 +2510,20 @@ void CPlayerInterface::stacksRebalanced(const StackLocation &src, const StackLoc
garrisonsChanged(objects);
}
void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al)
{
auto hero = dynamic_cast<const CGHeroInstance*>(al.relatedObj());
if(hero)
{
CArtPlace::askToAssemble(hero->getArt(al.slot), al.slot, hero);
}
}
void CPlayerInterface::artifactPut(const ArtifactLocation &al)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
adventureInt->infoBar.showSelection();
askToAssembleArtifact(al);
}
void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
@ -2534,6 +2548,7 @@ void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const Artifact
if(artWin)
artWin->artifactMoved(src, dst);
}
askToAssembleArtifact(dst);
}
void CPlayerInterface::artifactAssembled(const ArtifactLocation &al)
@ -2629,6 +2644,19 @@ bool CPlayerInterface::capturedAllEvents()
return false;
}
void CPlayerInterface::setMovementStatus(bool value)
{
duringMovement = value;
if(value)
{
CCS->curh->hide();
}
else
{
CCS->curh->show();
}
}
void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
{
int i = 1;
@ -2773,15 +2801,15 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
adventureInt->updateNextHero(h);
}
duringMovement = false;
setMovementStatus(false);
}
void CPlayerInterface::showWorldViewEx(const std::vector<ObjectPosInfo>& objectPositions)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
//TODO: showWorldViewEx
std::copy(objectPositions.begin(), objectPositions.end(), std::back_inserter(adventureInt->worldViewOptions.iconPositions));
viewWorldMap();
}

View File

@ -197,7 +197,7 @@ public:
void showComp(const Component &comp, std::string message) override; //display component in the advmapint infobox
void saveGame(COSer & h, const int version) override; //saving
void loadGame(CISer & h, const int version) override; //loading
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions) override;
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions) override;
//for battles
void actionFinished(const BattleAction& action) override;//occurs AFTER action taken by active stack or by the hero
@ -295,6 +295,8 @@ private:
bool ignoreEvents;
void doMoveHero(const CGHeroInstance *h, CGPath path);
void setMovementStatus(bool value);
void askToAssembleArtifact(const ArtifactLocation &al);
};
extern CPlayerInterface * LOCPLINT;

View File

@ -307,10 +307,10 @@ void CMenuScreen::switchToTab(size_t index)
//funciton for std::string -> std::function conversion for main menu
static std::function<void()> genCommand(CMenuScreen* menu, std::vector<std::string> menuType, const std::string &string)
{
static const std::vector<std::string> commandType =
static const std::vector<std::string> commandType =
{"to", "campaigns", "start", "load", "exit", "highscores"};
static const std::vector<std::string> gameType =
static const std::vector<std::string> gameType =
{"single", "multi", "campaign", "tutorial"};
std::list<std::string> commands;
@ -554,8 +554,8 @@ CGPreGame *CGPreGame::create()
{
if(!CGP)
CGP = new CGPreGame();
GH.terminate_cond.set(false);
GH.terminate_cond.set(false);
return CGP;
}
@ -1151,7 +1151,7 @@ void SelectionTab::parseGames(const std::unordered_set<ResourceID> &files, bool
lf >> *(mapInfo.mapHeader.get()) >> mapInfo.scenarioOpts;
mapInfo.fileURI = file.getName();
mapInfo.countPlayers();
std::time_t time = CFileInfo(*CResourceHandler::get()->getResourceName(file)).getDate();
std::time_t time = boost::filesystem::last_write_time(*CResourceHandler::get()->getResourceName(file));
mapInfo.date = std::asctime(std::localtime(&time));
// If multi mode then only multi games, otherwise single
@ -1362,9 +1362,9 @@ void SelectionTab::select( int position )
if(txt)
{
std::string filename = *CResourceHandler::get("local")->getResourceName(
auto filename = *CResourceHandler::get("local")->getResourceName(
ResourceID(curItems[py]->fileURI, EResType::CLIENT_SAVEGAME));
txt->setText(CFileInfo(filename).getBaseName());
txt->setText(filename.stem().string());
}
onSelect(curItems[py]);
@ -1487,8 +1487,8 @@ void SelectionTab::printMaps(SDL_Surface *to)
}
else
{
name = CFileInfo(*CResourceHandler::get("local")->getResourceName(
ResourceID(currentItem->fileURI, EResType::CLIENT_SAVEGAME))).getBaseName();
name = CResourceHandler::get("local")->getResourceName(
ResourceID(currentItem->fileURI, EResType::CLIENT_SAVEGAME))->stem().string();
}
//print name

View File

@ -59,8 +59,8 @@ template <typename T> class CApplyOnCL;
class CBaseForCLApply
{
public:
virtual void applyOnClAfter(CClient *cl, void *pack) const =0;
virtual void applyOnClBefore(CClient *cl, void *pack) const =0;
virtual void applyOnClAfter(CClient *cl, void *pack) const =0;
virtual void applyOnClBefore(CClient *cl, void *pack) const =0;
virtual ~CBaseForCLApply(){}
template<typename U> static CBaseForCLApply *getApplier(const U * t=nullptr)
@ -144,7 +144,7 @@ void CClient::waitForMoveAndSend(PlayerColor color)
{
logNetwork->traceStream() << "Send battle action to server: " << ba;
MakeAction temp_action(ba);
sendRequest(&temp_action, color);
sendRequest(&temp_action, color);
}
return;
}
@ -169,8 +169,8 @@ void CClient::run()
while(!terminate)
{
CPack *pack = serv->retreivePack(); //get the package from the server
if (terminate)
if (terminate)
{
vstd::clear_pointer(pack);
break;
@ -178,10 +178,10 @@ void CClient::run()
handlePack(pack);
}
}
}
//catch only asio exceptions
catch (const boost::system::system_error& e)
{
{
logNetwork->errorStream() << "Lost connection to server, ending listening thread!";
logNetwork->errorStream() << e.what();
if(!terminate) //rethrow (-> boom!) only if closing connection was unexpected
@ -219,18 +219,20 @@ void CClient::endGame( bool closeConnection /*= true*/ )
GH.curInt = nullptr;
{
boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
logNetwork->infoStream() << "Ending current game!";
logNetwork->infoStream() << "Ending current game!";
if(GH.topInt())
{
GH.topInt()->deactivate();
}
GH.listInt.clear();
GH.objsToBlit.clear();
GH.statusbar = nullptr;
logNetwork->infoStream() << "Removed GUI.";
logNetwork->infoStream() << "Removed GUI.";
vstd::clear_pointer(const_cast<CGameInfo*>(CGI)->mh);
vstd::clear_pointer(gs);
logNetwork->infoStream() << "Deleted mapHandler and gameState.";
logNetwork->infoStream() << "Deleted mapHandler and gameState.";
LOCPLINT = nullptr;
}
@ -238,9 +240,9 @@ void CClient::endGame( bool closeConnection /*= true*/ )
battleints.clear();
callbacks.clear();
battleCallbacks.clear();
logNetwork->infoStream() << "Deleted playerInts.";
logNetwork->infoStream() << "Client stopped.";
CGObelisk::reset();
logNetwork->infoStream() << "Deleted playerInts.";
logNetwork->infoStream() << "Client stopped.";
}
#if 1
@ -267,8 +269,8 @@ void CClient::loadGame(const std::string & fname, const bool server, const std::
std::unique_ptr<CLoadFile> loader;
try
{
std::string clientSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME));
std::string controlServerSaveName;
boost::filesystem::path clientSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME));
boost::filesystem::path controlServerSaveName;
if (CResourceHandler::get("local")->existsResource(ResourceID(fname, EResType::SERVER_SAVEGAME)))
{
@ -276,8 +278,8 @@ void CClient::loadGame(const std::string & fname, const bool server, const std::
}
else// create entry for server savegame. Triggered if save was made after launch and not yet present in res handler
{
controlServerSaveName = clientSaveName.substr(0, clientSaveName.find_last_of(".")) + ".vsgm1";
CResourceHandler::get("local")->createResource(controlServerSaveName, true);
controlServerSaveName = boost::filesystem::path(clientSaveName).replace_extension(".vsgm1");
CResourceHandler::get("local")->createResource(controlServerSaveName.string(), true);
}
if(clientSaveName.empty())
@ -320,7 +322,7 @@ void CClient::loadGame(const std::string & fname, const bool server, const std::
*serv << ui8(3) << ui8(loadNumPlayers); //load game; one client if single-player
*serv << fname;
*serv >> pom8;
if(pom8)
if(pom8)
throw std::runtime_error("Server cannot open the savegame!");
else
logNetwork->infoStream() << "Server opened savegame properly.";
@ -376,7 +378,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
{
enum {SINGLE, HOST, GUEST} networkMode = SINGLE;
if (con == nullptr)
if (con == nullptr)
{
CServerHandler sh;
serv = sh.connectToServer();
@ -459,7 +461,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
logNetwork->infoStream() << boost::format("Player %s will be lead by %s") % color % AiToGive;
installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), color);
}
else
else
{
installNewPlayerInterface(std::make_shared<CPlayerInterface>(color), color);
humanPlayers++;
@ -502,7 +504,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
// nm->giveActionCB(this);
// nm->giveInfoCB(this);
// nm->init();
//
//
// erm = nm; //something tells me that there'll at most one module and it'll be ERM
// }
}
@ -510,7 +512,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
void CClient::serialize(COSer & h, const int version)
{
assert(h.saving);
h & hotSeat;
h & hotSeat;
{
ui8 players = playerint.size();
h & players;
@ -520,7 +522,7 @@ void CClient::serialize(COSer & h, const int version)
LOG_TRACE_PARAMS(logGlobal, "Saving player %s interface", i->first);
assert(i->first == i->second->playerID);
h & i->first & i->second->dllName & i->second->human;
i->second->saveGame(h, version);
i->second->saveGame(h, version);
}
}
}
@ -536,7 +538,7 @@ void CClient::serialize(CISer & h, const int version)
for(int i=0; i < players; i++)
{
std::string dllname;
PlayerColor pid;
PlayerColor pid;
bool isHuman = false;
h & pid & dllname & isHuman;
@ -548,7 +550,7 @@ void CClient::serialize(CISer & h, const int version)
if(pid == PlayerColor::NEUTRAL)
{
installNewBattleInterface(CDynLibHandler::getNewBattleAI(dllname), pid);
//TODO? consider serialization
//TODO? consider serialization
continue;
}
else
@ -589,7 +591,7 @@ void CClient::serialize(COSer & h, const int version, const std::set<PlayerColor
LOG_TRACE_PARAMS(logGlobal, "Saving player %s interface", i->first);
assert(i->first == i->second->playerID);
h & i->first & i->second->dllName & i->second->human;
i->second->saveGame(h, version);
i->second->saveGame(h, version);
}
}
}
@ -605,7 +607,7 @@ void CClient::serialize(CISer & h, const int version, const std::set<PlayerColor
for(int i=0; i < players; i++)
{
std::string dllname;
PlayerColor pid;
PlayerColor pid;
bool isHuman = false;
h & pid & dllname & isHuman;
@ -618,7 +620,7 @@ void CClient::serialize(CISer & h, const int version, const std::set<PlayerColor
{
if(playerIDs.count(pid))
installNewBattleInterface(CDynLibHandler::getNewBattleAI(dllname), pid);
//TODO? consider serialization
//TODO? consider serialization
continue;
}
else
@ -640,7 +642,7 @@ void CClient::serialize(CISer & h, const int version, const std::set<PlayerColor
if(playerIDs.count(pid))
installNewPlayerInterface(nInt, pid);
nInt->loadGame(h, version);
nInt->loadGame(h, version);
}
if(playerIDs.count(PlayerColor::NEUTRAL))
@ -714,7 +716,7 @@ void CClient::battleStarted(const BattleInfo * info)
{
for(auto &battleCb : battleCallbacks)
{
if(vstd::contains_if(info->sides, [&](const SideInBattle& side) {return side.color == battleCb.first; })
if(vstd::contains_if(info->sides, [&](const SideInBattle& side) {return side.color == battleCb.first; })
|| battleCb.first >= PlayerColor::PLAYER_LIMIT)
{
battleCb.second->setBattle(info);
@ -742,7 +744,7 @@ void CClient::battleStarted(const BattleInfo * info)
{
boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
Rect((screen->w - 800)/2,
Rect((screen->w - 800)/2,
(screen->h - 600)/2, 800, 600), att, def);
GH.pushInt(bi);
@ -805,7 +807,7 @@ void CClient::commenceTacticPhaseForInt(std::shared_ptr<CBattleGameInterface> ba
catch(...)
{
handleException();
}
}
}
void CClient::invalidatePaths()
@ -889,7 +891,7 @@ void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> ba
boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
if(!color)
if(!color)
privilagedBattleEventReceivers.push_back(battleInterface);
battleints[colorUsed] = battleInterface;
@ -961,7 +963,7 @@ CConnection * CServerHandler::connectToServer()
#endif
th.update(); //put breakpoint here to attach to server before it does something stupid
CConnection *ret = justConnectToServer(settings["server"]["server"].String(), port);
if(verbose)
@ -1033,7 +1035,7 @@ CConnection * CServerHandler::justConnectToServer(const std::string &host, const
try
{
logNetwork->infoStream() << "Establishing connection...";
ret = new CConnection( host.size() ? host : settings["server"]["server"].String(),
ret = new CConnection( host.size() ? host : settings["server"]["server"].String(),
realPort,
NAME);
}

View File

@ -3,7 +3,7 @@
<FileVersion major="1" minor="6" />
<Project>
<Option title="VCMI_client" />
<Option pch_mode="2" />
<Option pch_mode="0" />
<Option compiler="gcc" />
<Build>
<Target title="Debug-win32">
@ -62,6 +62,7 @@
<Compiler>
<Add option="-Wextra" />
<Add option="-Wall" />
<Add option="-std=gnu++11" />
<Add option="-fexceptions" />
<Add option="-Wpointer-arith" />
<Add option="-Wno-switch" />
@ -70,7 +71,9 @@
<Add option="-Wno-overloaded-virtual" />
<Add option="-fpermissive" />
<Add option="-DBOOST_THREAD_USE_LIB" />
<Add option="-DBOOST_SYSTEM_NO_DEPRECATED" />
<Add option="-D_WIN32_WINNT=0x0501" />
<Add option="-D_WIN32" />
<Add directory="$(#boost.include)" />
<Add directory="../include" />
<Add directory="../client" />

View File

@ -1017,7 +1017,7 @@ void CBattleInterface::stackRemoved(int stackID)
setActiveStack(nullptr);
}
}
delete creAnims[stackID];
creAnims.erase(stackID);
creDir.erase(stackID);
@ -1201,18 +1201,18 @@ void CBattleInterface::stackIsCatapulting(const CatapultAttack & ca)
for(auto attackInfo : ca.attackedParts)
{
addNewAnim(new CShootingAnimation(this, stack, attackInfo.destinationTile, nullptr, true, attackInfo.damageDealt));
}
}
}
else
{
//no attacker stack, assume spell-related (earthquake) - only hit animation
//no attacker stack, assume spell-related (earthquake) - only hit animation
for(auto attackInfo : ca.attackedParts)
{
Point destPos = CClickableHex::getXYUnitAnim(attackInfo.destinationTile, nullptr, this) + Point(99, 120);
addNewAnim(new CSpellEffectAnimation(this, "SGEXPL.DEF", destPos.x, destPos.y));
}
}
}
waitForAnims();
@ -1323,9 +1323,9 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
//displaying message in console
std::vector<std::string> logLines;
spell.prepareBattleLog(curInt->cb.get(), sc, logLines);
for(auto line : logLines)
console->addText(line);
@ -1427,15 +1427,15 @@ void CBattleInterface::displayEffect(ui32 effect, int destTile, bool areaEffect)
}
void CBattleInterface::displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile, bool areaEffect)
{
{
if(animation.pause > 0)
{
addNewAnim(new CDummyAnimation(this, animation.pause));
addNewAnim(new CDummyAnimation(this, animation.pause));
}
else
{
addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM));
}
addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM));
}
}
void CBattleInterface::displaySpellCast(SpellID spellID, BattleHex destinationTile, bool areaEffect)
@ -1444,11 +1444,11 @@ void CBattleInterface::displaySpellCast(SpellID spellID, BattleHex destinationTi
if(spell == nullptr)
return;
for(const CSpell::TAnimation & animation : spell->animationInfo.cast)
{
displaySpellAnimation(animation, destinationTile, areaEffect);
}
}
}
void CBattleInterface::displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect)
@ -1933,7 +1933,7 @@ void CBattleInterface::bTacticNextStack(const CStack *current /*= nullptr*/)
waitForAnims();
TStacks stacksOfMine = tacticianInterface->cb->battleGetStacks(CBattleCallback::ONLY_MINE);
stacksOfMine.erase(std::remove_if(stacksOfMine.begin(), stacksOfMine.end(), &immobile), stacksOfMine.end());
vstd::erase_if(stacksOfMine, &immobile);
auto it = vstd::find(stacksOfMine, current);
if(it != stacksOfMine.end() && ++it != stacksOfMine.end())
stackActivated(*it);
@ -2072,7 +2072,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
case ANY_CREATURE:
if (shere && shere->alive() && isCastingPossibleHere (sactive, shere, myNumber))
legalAction = true;
break;
break;
case HOSTILE_CREATURE_SPELL:
if (shere && shere->alive() && !ourStack && isCastingPossibleHere (sactive, shere, myNumber))
legalAction = true;

View File

@ -32,7 +32,7 @@
*/
CArtPlace::CArtPlace(Point position, const CArtifactInstance * Art):
locked(false), picked(false), marked(false), ourArt(Art)
locked(false), picked(false), marked(false), ourArt(Art)
{
pos += position;
pos.w = pos.h = 44;
@ -180,7 +180,7 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
if(srcInBackpack && srcInSameHero)
{
if(!ourArt //cannot move from backpack to AFTER backpack -> combined with vstd::amin above it will guarantee that dest is at most the last artifact
|| ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted
|| ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted
vstd::advance(ourOwner->commonInfo->dst.slotID, -1);
}
if(srcInSameHero && ourOwner->commonInfo->dst.slotID == ourOwner->commonInfo->src.slotID) //we came to src == dst
@ -214,6 +214,32 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
}
}
bool CArtPlace::askToAssemble(const CArtifactInstance *art, ArtifactPosition slot,
const CGHeroInstance *hero)
{
std::vector<const CArtifact *> assemblyPossibilities = art->assemblyPossibilities(hero);
// If the artifact can be assembled, display dialog.
for(const CArtifact *combination : assemblyPossibilities)
{
LOCPLINT->showArtifactAssemblyDialog(
art->artType->id,
combination->id,
true,
std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combination->id),
0);
if(assemblyPossibilities.size() > 2)
{
logGlobal->warnStream() << boost::format(
"More than one possibility of assembling on %s... taking only first")
% art->artType->Name();
}
return true;
}
return false;
}
void CArtPlace::clickRight(tribool down, bool previousState)
{
if(down && ourArt && !locked && text.size() && !picked) //if there is no description or it's a lock, do nothing ;]
@ -225,20 +251,8 @@ void CArtPlace::clickRight(tribool down, bool previousState)
std::vector<const CArtifact *> assemblyPossibilities = ourArt->assemblyPossibilities(ourOwner->curHero);
// If the artifact can be assembled, display dialog.
for(const CArtifact *combination : assemblyPossibilities)
if (askToAssemble(ourArt, slotID, ourOwner->curHero))
{
LOCPLINT->showArtifactAssemblyDialog(
ourArt->artType->id,
combination->id,
true,
std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), ourOwner->curHero, slotID, true, combination->id),
0);
if(assemblyPossibilities.size() > 2)
{
logGlobal->warnStream() << "More than one possibility of assembling... taking only first";
break;
}
return;
}
@ -303,10 +317,10 @@ void CArtPlace::deselect ()
for(int i = 0; i < GameConstants::BACKPACK_START; i++)
{
auto place = ourOwner->getArtPlace(i);
if(nullptr != place)//getArtPlace may return null
place->pickSlot(false);
}
}
}
CCS->curh->dragAndDropCursor(nullptr);
@ -372,70 +386,36 @@ void CArtPlace::setArtifact(const CArtifactInstance *art)
image->disable();
text = std::string();
hoverText = CGI->generaltexth->allTexts[507];
return;
}
image->enable();
image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex);
text = art->getEffectiveDescription(ourOwner->curHero);
if(art->artType->id == ArtifactID::SPELL_SCROLL)
{
int spellID = art->getGivenSpellID();
if(spellID >= 0)
{
//add spell component info (used to provide a pic in r-click popup)
baseType = CComponent::spell;
type = spellID;
bonusValue = 0;
}
}
else
{
image->enable();
image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex);
std::string artDesc = ourArt->artType->Description();
if (vstd::contains (artDesc, '{'))
text = artDesc;
else
text = '{' + ourArt->artType->Name() + "}\n\n" + artDesc; //workaround for new artifacts with single name, turns it to H3-style
if(art->artType->id == ArtifactID::SPELL_SCROLL)
{
// we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll.
// so we want to replace text in [...] with a spell name
// however other language versions don't have name placeholder at all, so we have to be careful
int spellID = art->getGivenSpellID();
size_t nameStart = text.find_first_of('[');
size_t nameEnd = text.find_first_of(']', nameStart);
if(spellID >= 0)
{
if(nameStart != std::string::npos && nameEnd != std::string::npos)
text = text.replace(nameStart, nameEnd - nameStart + 1, CGI->spellh->objects[spellID]->name);
//add spell component info (used to provide a pic in r-click popup)
baseType = CComponent::spell;
type = spellID;
bonusValue = 0;
}
}
else
{
baseType = CComponent::artifact;
type = art->artType->id;
bonusValue = 0;
}
if (art->artType->constituents) //display info about components of combined artifact
{
//TODO
}
else if (art->artType->constituentOf.size()) //display info about set
{
std::string artList;
auto combinedArt = art->artType->constituentOf[0];
text += "\n\n";
text += "{" + combinedArt->Name() + "}";
int wornArtifacts = 0;
for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set?
{
artList += "\n" + a->Name();
if (ourOwner->curHero->hasArt(a->id, true))
wornArtifacts++;
}
text += " (" + boost::str(boost::format("%d") % wornArtifacts) + " / " +
boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList;
//TODO: fancy colors and fonts for this text
}
if (locked) // Locks should appear as empty.
hoverText = CGI->generaltexth->allTexts[507];
else
hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name());
baseType = CComponent::artifact;
type = art->artType->id;
bonusValue = 0;
}
if (locked) // Locks should appear as empty.
hoverText = CGI->generaltexth->allTexts[507];
else
hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name());
}
void CArtifactsOfHero::SCommonPart::reset()
@ -454,12 +434,12 @@ void CArtifactsOfHero::setHero(const CGHeroInstance * hero)
backpackPos = 0;
// Fill the slots for worn artifacts and backpack.
for(auto p : artWorn)
{
setSlotData(p.second, p.first);
}
scrollBackpack(0);
}
@ -587,8 +567,8 @@ void CArtifactsOfHero::setSlotData(CArtPlace* artPlace, ArtifactPosition slotID)
if(const ArtSlotInfo *asi = curHero->getSlot(slotID))
{
artPlace->setArtifact(asi->artifact);
artPlace->lockSlot(asi->locked);
artPlace->setArtifact(asi->artifact);
}
else
artPlace->setArtifact(nullptr);
@ -797,7 +777,7 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact
}
else if(src.slot >= GameConstants::BACKPACK_START &&
src.slot < commonInfo->src.slotID &&
src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one
src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one
{
//int fixedSlot = src.hero->getArtPos(commonInfo->src.art);
vstd::advance(commonInfo->src.slotID, -1);
@ -811,14 +791,14 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact
}
updateParentWindow();
int shift = 0;
int shift = 0;
// if(dst.slot >= Arts::BACKPACK_START && dst.slot - Arts::BACKPACK_START < backpackPos)
// shift++;
//
if(src.slot < GameConstants::BACKPACK_START && dst.slot - GameConstants::BACKPACK_START < backpackPos)
if(src.slot < GameConstants::BACKPACK_START && dst.slot - GameConstants::BACKPACK_START < backpackPos)
shift++;
if(dst.slot < GameConstants::BACKPACK_START && src.slot - GameConstants::BACKPACK_START < backpackPos)
shift--;
shift--;
if( (isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START)
|| (isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START) )
@ -853,7 +833,7 @@ CArtPlace * CArtifactsOfHero::getArtPlace(int slot)
for(CArtPlace *ap : backpack)
if(ap->slotID == slot)
return ap;
return nullptr;
return nullptr;
}
}

View File

@ -74,15 +74,17 @@ public:
void setMeAsDest(bool backpackAsVoid = true);
void setArtifact(const CArtifactInstance *art);
static bool askToAssemble(const CArtifactInstance *art, ArtifactPosition slot,
const CGHeroInstance *hero);
};
/// Contains artifacts of hero. Distincts which artifacts are worn or backpacked
class CArtifactsOfHero : public CIntObject
{
const CGHeroInstance * curHero;
std::map<ArtifactPosition, CArtPlace *> artWorn;
std::vector<CArtPlace *> backpack; //hero's visible backpack (only 5 elements!)
int backpackPos; //number of first art visible in backpack (in hero's vector)

View File

@ -7,6 +7,7 @@
#include "../CMessage.h"
#include "../CGameInfo.h"
#include "../widgets/Images.h"
#include "../widgets/CArtifactHolder.h"
#include "../windows/CAdvmapInterface.h"
#include "../../lib/CArtHandler.h"
@ -144,14 +145,26 @@ size_t CComponent::getIndex()
std::string CComponent::getDescription()
{
switch (compType)
switch(compType)
{
case primskill: return (subtype < 4)? CGI->generaltexth->arraytxt[2+subtype] //Primary skill
: CGI->generaltexth->allTexts[149]; //mana
case secskill: return CGI->generaltexth->skillInfoTexts[subtype][val-1];
case resource: return CGI->generaltexth->allTexts[242];
case creature: return "";
case artifact: return CGI->arth->artifacts[subtype]->Description();
case artifact:
{
std::unique_ptr<CArtifactInstance> art;
if (subtype != ArtifactID::SPELL_SCROLL)
{
art.reset(CArtifactInstance::createNewArtifactInstance(subtype));
}
else
{
art.reset(CArtifactInstance::createScroll(static_cast<SpellID>(val)));
}
return art->getEffectiveDescription();
}
case experience: return CGI->generaltexth->allTexts[241];
case spell: return CGI->spellh->objects[subtype]->getLevelInfo(val).description;
case morale: return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)];
@ -166,7 +179,7 @@ std::string CComponent::getDescription()
std::string CComponent::getSubtitle()
{
if (!perDay)
if(!perDay)
return getSubtitleInternal();
std::string ret = CGI->generaltexth->allTexts[3];
@ -186,19 +199,31 @@ std::string CComponent::getSubtitleInternal()
case artifact: return CGI->arth->artifacts[subtype]->Name();
case experience:
{
if (subtype == 1) //+1 level - tree of knowledge
if(subtype == 1) //+1 level - tree of knowledge
{
std::string level = CGI->generaltexth->allTexts[442];
boost::replace_first(level, "1", boost::lexical_cast<std::string>(val));
return level;
}
else
{
return boost::lexical_cast<std::string>(val); //amount of experience OR level required for seer hut;
}
}
case spell: return CGI->spellh->objects[subtype]->name;
case morale: return "";
case luck: return "";
case building: return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Name();
case building:
{
auto building = CGI->townh->factions[subtype]->town->buildings[BuildingID(val)];
if(!building)
{
logGlobal->errorStream() << boost::format("Town of faction %s has no building #%d")
% CGI->townh->factions[subtype]->town->faction->name % val;
return (boost::format("Missing building #%d") % val).str();
}
return building->Name();
}
case hero: return "";
case flag: return CGI->generaltexth->capColors[subtype];
}

View File

@ -73,11 +73,11 @@ void CGarrisonSlot::hover (bool on)
{
if(upg == EGarrisonType::UP)
{
temp = CGI->generaltexth->tcommands[32]; //Select %s (visiting)
temp = CGI->generaltexth->tcommands[12]; //Select %s (in garrison)
}
else if(owner->armedObjs[0] && (owner->armedObjs[0]->ID == Obj::TOWN || owner->armedObjs[0]->ID == Obj::HERO))
{
temp = CGI->generaltexth->tcommands[12]; //Select %s (in garrison)
temp = CGI->generaltexth->tcommands[32]; //Select %s (visiting)
}
else
{

View File

@ -322,18 +322,21 @@ void CTerrainRect::showAnim(SDL_Surface * to)
show(to); // currently the same; maybe we should pass some flag to map handler so it redraws ONLY tiles that need redraw instead of full
}
int3 CTerrainRect::whichTileIsIt(const int & x, const int & y)
int3 CTerrainRect::whichTileIsIt(const int x, const int y)
{
int3 ret;
ret.x = adventureInt->position.x + ((GH.current->motion.x-CGI->mh->offsetX-pos.x)/32);
ret.y = adventureInt->position.y + ((GH.current->motion.y-CGI->mh->offsetY-pos.y)/32);
ret.x = adventureInt->position.x + ((x-CGI->mh->offsetX-pos.x)/32);
ret.y = adventureInt->position.y + ((y-CGI->mh->offsetY-pos.y)/32);
ret.z = adventureInt->position.z;
return ret;
}
int3 CTerrainRect::whichTileIsIt()
{
return whichTileIsIt(GH.current->motion.x,GH.current->motion.y);
if(GH.current)
return whichTileIsIt(GH.current->motion.x,GH.current->motion.y);
else
return int3(-1);
}
int3 CTerrainRect::tileCountOnScreen()
@ -755,14 +758,15 @@ void CAdvMapInt::updateSleepWake(const CGHeroInstance *h)
void CAdvMapInt::updateMoveHero(const CGHeroInstance *h, tribool hasPath)
{
//default value is for everywhere but CPlayerInterface::moveHero, because paths are not updated from there immediately
if (hasPath == boost::indeterminate)
hasPath = LOCPLINT->paths[h].nodes.size() ? true : false;
if (!h)
if(!h)
{
moveHero->block(true);
return;
}
//default value is for everywhere but CPlayerInterface::moveHero, because paths are not updated from there immediately
if(boost::logic::indeterminate(hasPath))
hasPath = LOCPLINT->paths[h].nodes.size() ? true : false;
moveHero->block(!hasPath || (h->movement == 0));
}
@ -1417,13 +1421,13 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
bool canSelect = topBlocking && topBlocking->ID == Obj::HERO && topBlocking->tempOwner == LOCPLINT->playerID;
canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner);
if (selection->ID != Obj::HERO) //hero is not selected (presumably town)
if(selection->ID != Obj::HERO) //hero is not selected (presumably town)
{
assert(!terrain.currentPath); //path can be active only when hero is selected
if(selection == topBlocking) //selected town clicked
LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(topBlocking));
else if ( canSelect )
select(static_cast<const CArmedInstance*>(topBlocking), false);
else if(canSelect)
select(static_cast<const CArmedInstance*>(topBlocking), false);
return;
}
else if(const CGHeroInstance * currentHero = curHero()) //hero is selected
@ -1441,22 +1445,26 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
}
else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
{
if (terrain.currentPath && terrain.currentPath->endPos() == mapPos)//we'll be moving
if(terrain.currentPath && terrain.currentPath->endPos() == mapPos)//we'll be moving
{
if (CGI->mh->canStartHeroMovement())
LOCPLINT->moveHero(currentHero,*terrain.currentPath);
if(CGI->mh->canStartHeroMovement())
LOCPLINT->moveHero(currentHero, *terrain.currentPath);
return;
}
else/* if(mp.z == currentHero->pos.z)*/ //remove old path and find a new one if we clicked on the map level on which hero is present
else //remove old path and find a new one if we clicked on accessible tile
{
CGPath &path = LOCPLINT->paths[currentHero];
terrain.currentPath = &path;
bool gotPath = LOCPLINT->cb->getPathsInfo(currentHero)->getPath(path, mapPos); //try getting path, erase if failed
updateMoveHero(currentHero);
if (!gotPath)
LOCPLINT->eraseCurrentPathOf(currentHero);
CGPath newpath;
bool gotPath = LOCPLINT->cb->getPathsInfo(currentHero)->getPath(newpath, mapPos); //try getting path, erase if failed
if(gotPath && newpath.nodes.size())
path = newpath;
if(path.nodes.size())
terrain.currentPath = &path;
else
return;
LOCPLINT->eraseCurrentPathOf(currentHero);
updateMoveHero(currentHero);
}
}
} //end of hero is selected "case"
@ -1704,6 +1712,18 @@ void CAdvMapInt::adjustActiveness(bool aiTurnStart)
activate();
}
void CAdvMapInt::quickCombatLock()
{
if(!duringAITurn)
deactivate();
}
void CAdvMapInt::quickCombatUnlock()
{
if(!duringAITurn)
activate();
}
void CAdvMapInt::changeMode(EAdvMapMode newMode, float newScale /* = 0.4f */)
{
if (mode != newMode)

View File

@ -74,7 +74,7 @@ public:
void showAll(SDL_Surface * to) override;
void showAnim(SDL_Surface * to);
void showPath(const SDL_Rect * extRect, SDL_Surface * to);
int3 whichTileIsIt(const int & x, const int & y); //x,y are cursor position
int3 whichTileIsIt(const int x, const int y); //x,y are cursor position
int3 whichTileIsIt(); //uses current cursor pos
/// @returns number of visible tiles on screen respecting current map scaling
int3 tileCountOnScreen();
@ -224,6 +224,8 @@ public:
void aiTurnStarted();
void adjustActiveness(bool aiTurnStart); //should be called every time at AI/human turn transition; blocks GUI during AI turn
void quickCombatLock(); //should be called when quick battle started
void quickCombatUnlock();
void tileLClicked(const int3 &mapPos);
void tileHovered(const int3 &mapPos);
void tileRClicked(const int3 &mapPos);

View File

@ -28,6 +28,7 @@
#include "../lib/CHeroHandler.h"
#include "../lib/mapObjects/CGHeroInstance.h"
#include "../lib/NetPacksBase.h"
#include "../mapHandler.h"
/*
* CHeroWindow.cpp, part of VCMI engine
@ -275,6 +276,9 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals
if(!LOCPLINT->cb->howManyTowns() && LOCPLINT->cb->howManyHeroes() == 1)
noDismiss = true;
if(curHero->isMissionCritical())
noDismiss = true;
dismissButton->block(!!curHero->visitedTown || noDismiss);
if(curHero->getSecSkillLevel(SecondarySkill::TACTICS) == 0)
@ -343,10 +347,10 @@ void CHeroWindow::commanderWindow()
void CHeroWindow::showAll(SDL_Surface * to)
{
CIntObject::showAll(to);
//printing hero's name
printAtMiddleLoc(curHero->name, 190, 38, FONT_BIG, Colors::YELLOW, to);
//printing hero's level
std::string secondLine= CGI->generaltexth->allTexts[342];
boost::algorithm::replace_first(secondLine,"%d",boost::lexical_cast<std::string>(curHero->level));
@ -360,14 +364,14 @@ void CHeroWindow::showAll(SDL_Surface * to)
primarySkill << primSkillAreas[m]->bonusValue;
printAtMiddleLoc(primarySkill.str(), 53 + 70 * m, 166, FONT_SMALL, Colors::WHITE, to);
}
//secondary skills
for(size_t v=0; v<std::min(secSkillAreas.size(), curHero->secSkills.size()); ++v)
{
printAtLoc(CGI->generaltexth->levels[curHero->secSkills[v].second-1], (v%2) ? 212 : 68, 280 + 48 * (v/2), FONT_SMALL, Colors::WHITE, to);
printAtLoc(CGI->generaltexth->skillName[curHero->secSkills[v].first], (v%2) ? 212 : 68, 300 + 48 * (v/2), FONT_SMALL, Colors::WHITE, to);
}
//printing special ability
printAtLoc(curHero->type->specName, 69, 205, FONT_SMALL, Colors::WHITE, to);
std::ostringstream expstr;

View File

@ -269,7 +269,7 @@ void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState)
case ARTIFACT_TYPE:
case ARTIFACT_PLACEHOLDER:
if(id >= 0)
adventureInt->handleRightClick(CGI->arth->artifacts[id]->Description(), down);
adventureInt->handleRightClick(hlp->getEffectiveDescription(), down);
break;
}
}
@ -500,14 +500,14 @@ void CTradeWindow::getPositionsFor(std::vector<Rect> &poss, bool Left, EType typ
int h, w, x, y, dx, dy;
int leftToRightOffset;
getBaseForPositions(type, dx, dy, x, y, h, w, !Left, leftToRightOffset);
const std::vector<Rect> tmp =
const std::vector<Rect> tmp =
{
genRect(h, w, x, y), genRect(h, w, x + dx, y), genRect(h, w, x + 2*dx, y),
genRect(h, w, x, y + dy), genRect(h, w, x + dx, y + dy), genRect(h, w, x + 2*dx, y + dy),
genRect(h, w, x + dx, y + 2*dy)
genRect(h, w, x + dx, y + 2*dy)
};
vstd::concatenate(poss, tmp);
if(!Left)

View File

@ -23,7 +23,8 @@
"ALL_CREATURES_GET_DOUBLE_MONTHS" : false,
"NEGATIVE_LUCK" : false,
"MAX_HEROES_AVAILABLE_PER_PLAYER" : 16,
"MAX_HEROES_ON_MAP_PER_PLAYER" : 8
"MAX_HEROES_ON_MAP_PER_PLAYER" : 8,
"WINNING_HERO_WITH_NO_TROOPS_RETREATS": true
},
"modules":

View File

@ -17,4 +17,13 @@ inline QString pathToQString(const boost::filesystem::path & path)
#else
return QString::fromStdString(path.string());
#endif
}
}
inline boost::filesystem::path qstringToPath(const QString & path)
{
#ifdef VCMI_WINDOWS
return boost::filesystem::path(path.toStdWString());
#else
return boost::filesystem::path(path.toUtf8().data());
#endif
}

103
launcher/VCMI_launcher.cbp Normal file
View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_project_file>
<FileVersion major="1" minor="6" />
<Project>
<Option title="VCMI_launcher" />
<Option pch_mode="0" />
<Option compiler="gcc" />
<Build>
<Target title="Debug Win32">
<Option output="../VCMI_launcher" prefix_auto="1" extension_auto="1" />
<Option working_dir="../" />
<Option object_output=".objs/debug" />
<Option type="0" />
<Option compiler="gcc" />
<Compiler>
<Add option="-g" />
</Compiler>
</Target>
<Target title="Release Win32">
<Option output="../VCMI_launcher" prefix_auto="1" extension_auto="1" />
<Option working_dir="../" />
<Option object_output=".objs/release" />
<Option type="0" />
<Option compiler="gcc" />
<Compiler>
<Add option="-O3" />
<Add option="-flto" />
</Compiler>
<Linker>
<Add option="-s" />
<Add option="-flto" />
</Linker>
</Target>
</Build>
<Compiler>
<Add option="-Wextra" />
<Add option="-Wall" />
<Add option="-std=gnu++11" />
<Add option="-DBOOST_SYSTEM_NO_DEPRECATED" />
<Add option="-D_WIN32" />
<Add directory="." />
<Add directory="../include" />
<Add directory="$(#boost.include)" />
<Add directory="$(#qt.include)" />
<Add directory="$(#qt.include)/QtGui" />
<Add directory="$(#qt.include)/QtCore" />
<Add directory="$(#qt.include)/QtWidgets" />
</Compiler>
<Linker>
<Add option="-lVCMI_lib" />
<Add option="-lQt5Core" />
<Add option="-lQt5Gui" />
<Add option="-lQt5Widgets" />
<Add option="-lQt5Network" />
<Add option="-lboost_filesystem$(#boost.libsuffix)" />
<Add option="-lboost_system$(#boost.libsuffix)" />
<Add directory="../" />
<Add directory="$(#qt.lib)" />
<Add directory="$(#boost.lib)" />
</Linker>
<Unit filename="StdInc.cpp" />
<Unit filename="StdInc.h">
<Option compile="1" />
<Option weight="0" />
</Unit>
<Unit filename="VCMI_launcher.rc">
<Option compilerVar="WINDRES" />
</Unit>
<Unit filename="jsonutils.cpp" />
<Unit filename="jsonutils.h" />
<Unit filename="launcherdirs.cpp" />
<Unit filename="launcherdirs.h" />
<Unit filename="main.cpp" />
<Unit filename="mainwindow_moc.cpp" />
<Unit filename="mainwindow_moc.h" />
<Unit filename="modManager/cdownloadmanager_moc.cpp" />
<Unit filename="modManager/cdownloadmanager_moc.h" />
<Unit filename="modManager/cmodlist.cpp" />
<Unit filename="modManager/cmodlist.h" />
<Unit filename="modManager/cmodlistmodel_moc.cpp" />
<Unit filename="modManager/cmodlistmodel_moc.h" />
<Unit filename="modManager/cmodlistview_moc.cpp" />
<Unit filename="modManager/cmodlistview_moc.h" />
<Unit filename="modManager/cmodmanager.cpp" />
<Unit filename="modManager/cmodmanager.h" />
<Unit filename="modManager/imageviewer_moc.cpp" />
<Unit filename="modManager/imageviewer_moc.h" />
<Unit filename="modManager/qrc_cdownloadmanager_moc.cpp" />
<Unit filename="modManager/qrc_cmodlistmodel_moc.cpp" />
<Unit filename="modManager/qrc_cmodlistview_moc.cpp" />
<Unit filename="modManager/qrc_imageviewer_moc.cpp" />
<Unit filename="qrc_mainwindow_moc.cpp" />
<Unit filename="settingsView/csettingsview_moc.cpp" />
<Unit filename="settingsView/csettingsview_moc.h" />
<Unit filename="settingsView/qrc_csettingsview_moc.cpp" />
<Extensions>
<code_completion />
<envvars />
<debugger />
<lib_finder disable_auto="1" />
</Extensions>
</Project>
</CodeBlocks_project_file>

View File

@ -1,5 +1,6 @@
#include "StdInc.h"
#include "jsonutils.h"
#include "../lib/filesystem/FileStream.h"
static QVariantMap JsonToMap(const JsonMap & json)
{
@ -96,8 +97,7 @@ JsonNode toJson(QVariant object)
void JsonToFile(QString filename, QVariant object)
{
std::ofstream file(filename.toUtf8().data(), std::ofstream::binary);
FileStream file(qstringToPath(filename), std::ios::out | std::ios_base::binary);
file << toJson(object);
}

View File

@ -2,6 +2,7 @@
#include <QVariantMap>
#include <QVariant>
#include <QVector>
class JsonNode;

View File

@ -1,6 +1,6 @@
#pragma once
#include "../../Global.h"
#include "StdInc.h"
#include "../../lib/CConfigHandler.h"
namespace Ui {
@ -55,7 +55,7 @@ class CModListView : public QWidget
public:
explicit CModListView(QWidget *parent = 0);
~CModListView();
void showModInfo();
void hideModInfo();
void loadScreenshots();

View File

@ -11,7 +11,7 @@
static QString detectModArchive(QString path, QString modName)
{
auto files = ZipArchive::listFiles(path.toUtf8().data());
auto files = ZipArchive::listFiles(qstringToPath(path));
QString modDirName;
@ -69,8 +69,8 @@ void CModManager::loadMods()
ResourceID resID(CModInfo::getModFile(modname));
if (CResourceHandler::get()->existsResource(resID))
{
std::string name = *CResourceHandler::get()->getResourceName(resID);
auto mod = JsonUtils::JsonFromFile(QString::fromUtf8(name.c_str()));
boost::filesystem::path name = *CResourceHandler::get()->getResourceName(resID);
auto mod = JsonUtils::JsonFromFile(pathToQString(name));
localMods.insert(QString::fromUtf8(modname.c_str()).toLower(), mod);
}
}
@ -243,7 +243,7 @@ bool CModManager::doInstallMod(QString modname, QString archivePath)
if (!modDirName.size())
return addError(modname, "Mod archive is invalid or corrupted");
if (!ZipArchive::extract(archivePath.toUtf8().data(), destDir.toUtf8().data()))
if (!ZipArchive::extract(qstringToPath(archivePath), qstringToPath(destDir)))
{
QDir(destDir + modDirName).removeRecursively();
return addError(modname, "Failed to extract mod data");
@ -262,7 +262,7 @@ bool CModManager::doUninstallMod(QString modname)
{
ResourceID resID(std::string("Mods/") + modname.toUtf8().data(), EResType::DIRECTORY);
// Get location of the mod, in case-insensitive way
QString modDir = QString::fromUtf8(CResourceHandler::get()->getResourceName(resID)->c_str());
QString modDir = pathToQString(*CResourceHandler::get()->getResourceName(resID));
if (!QDir(modDir).exists())
return addError(modname, "Data with this mod was not found");

View File

@ -1,5 +1,7 @@
#pragma once
#include "StdInc.h"
namespace Ui {
class CSettingsView;
}
@ -7,13 +9,13 @@ namespace Ui {
class CSettingsView : public QWidget
{
Q_OBJECT
public:
explicit CSettingsView(QWidget *parent = 0);
~CSettingsView();
void loadSettings();
private slots:
void on_comboBoxResolution_currentIndexChanged(const QString &arg1);

View File

@ -280,16 +280,16 @@ ArtifactPosition CArtHandler::stringToSlot(std::string slotName)
void CArtHandler::addSlot(CArtifact * art, const std::string & slotID)
{
static const std::vector<ArtifactPosition> miscSlots =
static const std::vector<ArtifactPosition> miscSlots =
{
ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5
};
static const std::vector<ArtifactPosition> ringSlots =
{
ArtifactPosition::LEFT_RING, ArtifactPosition::RIGHT_RING
};
if (slotID == "MISC")
{
vstd::concatenate(art->possibleSlots[ArtBearer::HERO], miscSlots);
@ -323,7 +323,7 @@ void CArtHandler::loadSlots(CArtifact * art, const JsonNode & node)
CArtifact::EartClass CArtHandler::stringToClass(std::string className)
{
static const std::map<std::string, CArtifact::EartClass> artifactClassMap =
{
{
{"TREASURE", CArtifact::ART_TREASURE},
{"MINOR", CArtifact::ART_MINOR},
{"MAJOR", CArtifact::ART_MAJOR},
@ -738,9 +738,14 @@ std::string CArtifactInstance::nodeName() const
}
CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s)
{
return createScroll(s->id);
}
CArtifactInstance *CArtifactInstance::createScroll(SpellID sid)
{
auto ret = new CArtifactInstance(VLC->arth->artifacts[ArtifactID::SPELL_SCROLL]);
auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, s->id);
auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, sid);
ret->addNewBonus(b);
return ret;
}
@ -752,6 +757,48 @@ void CArtifactInstance::init()
setNodeType(ARTIFACT_INSTANCE);
}
std::string CArtifactInstance::getEffectiveDescription(
const CGHeroInstance *hero) const
{
std::string text = artType->Description();
if (!vstd::contains(text, '{'))
text = '{' + artType->Name() + "}\n\n" + text; //workaround for new artifacts with single name, turns it to H3-style
if(artType->id == ArtifactID::SPELL_SCROLL)
{
// we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll.
// so we want to replace text in [...] with a spell name
// however other language versions don't have name placeholder at all, so we have to be careful
int spellID = getGivenSpellID();
size_t nameStart = text.find_first_of('[');
size_t nameEnd = text.find_first_of(']', nameStart);
if(spellID >= 0)
{
if(nameStart != std::string::npos && nameEnd != std::string::npos)
text = text.replace(nameStart, nameEnd - nameStart + 1, VLC->spellh->objects[spellID]->name);
}
}
else if (hero && artType->constituentOf.size()) //display info about set
{
std::string artList;
auto combinedArt = artType->constituentOf[0];
text += "\n\n";
text += "{" + combinedArt->Name() + "}";
int wornArtifacts = 0;
for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set?
{
artList += "\n" + a->Name();
if (hero->hasArt(a->id, true))
wornArtifacts++;
}
text += " (" + boost::str(boost::format("%d") % wornArtifacts) + " / " +
boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList;
//TODO: fancy colors and fonts for this text
}
return text;
}
ArtifactPosition CArtifactInstance::firstAvailableSlot(const CArtifactSet *h) const
{
for(auto slot : artType->possibleSlots.at(h->bearerType()))
@ -900,7 +947,7 @@ SpellID CArtifactInstance::getGivenSpellID() const
const Bonus * b = getBonusLocalFirst(Selector::type(Bonus::SPELL));
if(!b)
{
logGlobal->warnStream() << "Warning: " << nodeName() << " doesn't bear any spell!";
logGlobal->warnStream() << "Warning: " << nodeName() << " doesn't bear any spell!";
return SpellID::NONE;
}
return SpellID(b->subtype);
@ -1152,9 +1199,42 @@ const CArtifactInstance * CArtifactSet::getArtByInstanceId( ArtifactInstanceID a
return nullptr;
}
bool CArtifactSet::hasArt(ui32 aid, bool onlyWorn /*= false*/) const
bool CArtifactSet::hasArt(ui32 aid, bool onlyWorn /*= false*/,
bool searchBackpackAssemblies /*= false*/) const
{
return getArtPos(aid, onlyWorn) != ArtifactPosition::PRE_FIRST;
return getArtPos(aid, onlyWorn) != ArtifactPosition::PRE_FIRST ||
(searchBackpackAssemblies && getHiddenArt(aid));
}
std::pair<const CCombinedArtifactInstance *, const CArtifactInstance *>
CArtifactSet::searchForConstituent(int aid) const
{
for(auto & slot : artifactsInBackpack)
{
auto art = slot.artifact;
if(art->canBeDisassembled())
{
auto ass = static_cast<CCombinedArtifactInstance *>(art.get());
for(auto& ci : ass->constituentsInfo)
{
if(ci.art->artType->id == aid)
{
return {ass, ci.art};
}
}
}
}
return {nullptr, nullptr};
}
const CArtifactInstance *CArtifactSet::getHiddenArt(int aid) const
{
return searchForConstituent(aid).second;
}
const CCombinedArtifactInstance *CArtifactSet::getAssemblyByConstituent(int aid) const
{
return searchForConstituent(aid).first;
}
const ArtSlotInfo * CArtifactSet::getSlot(ArtifactPosition pos) const

View File

@ -119,6 +119,7 @@ public:
void deserializationFix();
void setType(CArtifact *Art);
std::string getEffectiveDescription(const CGHeroInstance *hero = nullptr) const;
ArtifactPosition firstAvailableSlot(const CArtifactSet *h) const;
ArtifactPosition firstBackpackSlot(const CArtifactSet *h) const;
SpellID getGivenSpellID() const; //to be used with scrolls (and similar arts), -1 if none
@ -128,7 +129,9 @@ public:
virtual bool canBeDisassembled() const;
virtual void putAt(ArtifactLocation al);
virtual void removeFrom(ArtifactLocation al);
virtual bool isPart(const CArtifactInstance *supposedPart) const; //checks if this a part of this artifact: artifact instance is a part of itself, additionally truth is returned for consituents of combined arts
/// Checks if this a part of this artifact: artifact instance is a part
/// of itself, additionally truth is returned for constituents of combined arts
virtual bool isPart(const CArtifactInstance *supposedPart) const;
std::vector<const CArtifact *> assemblyPossibilities(const CArtifactSet *h) const;
void move(ArtifactLocation src, ArtifactLocation dst);
@ -141,6 +144,7 @@ public:
}
static CArtifactInstance *createScroll(const CSpell *s);
static CArtifactInstance *createScroll(SpellID sid);
static CArtifactInstance *createNewArtifactInstance(CArtifact *Art);
static CArtifactInstance *createNewArtifactInstance(int aid);
};
@ -172,7 +176,7 @@ public:
void createConstituents();
void addAsConstituent(CArtifactInstance *art, ArtifactPosition slot);
CArtifactInstance *figureMainConstituent(const ArtifactLocation al); //main constituent is replcaed with us (combined art), not lock
CArtifactInstance *figureMainConstituent(const ArtifactLocation al); //main constituent is replaced with us (combined art), not lock
CCombinedArtifactInstance();
@ -265,10 +269,8 @@ struct DLL_LINKAGE ArtSlotInfo
ConstTransitivePtr<CArtifactInstance> artifact;
ui8 locked; //if locked, then artifact points to the combined artifact
ArtSlotInfo()
{
locked = false;
}
ArtSlotInfo() : locked(false) {}
template <typename Handler> void serialize(Handler &h, const int version)
{
h & artifact & locked;
@ -288,10 +290,16 @@ public:
const ArtSlotInfo *getSlot(ArtifactPosition pos) const;
const CArtifactInstance* getArt(ArtifactPosition pos, bool excludeLocked = true) const; //nullptr - no artifact
CArtifactInstance* getArt(ArtifactPosition pos, bool excludeLocked = true); //nullptr - no artifact
ArtifactPosition getArtPos(int aid, bool onlyWorn = true) const; //looks for equipped artifact with given ID and returns its slot ID or -1 if none(if more than one such artifact lower ID is returned)
/// Looks for equipped artifact with given ID and returns its slot ID or -1 if none
/// (if more than one such artifact lower ID is returned)
ArtifactPosition getArtPos(int aid, bool onlyWorn = true) const;
ArtifactPosition getArtPos(const CArtifactInstance *art) const;
const CArtifactInstance *getArtByInstanceId(ArtifactInstanceID artInstId) const;
bool hasArt(ui32 aid, bool onlyWorn = false) const; //checks if hero possess artifact of given id (either in backack or worn)
/// Search for constituents of assemblies in backpack which do not have an ArtifactPosition
const CArtifactInstance *getHiddenArt(int aid) const;
const CCombinedArtifactInstance *getAssemblyByConstituent(int aid) const;
/// Checks if hero possess artifact of given id (either in backack or worn)
bool hasArt(ui32 aid, bool onlyWorn = false, bool searchBackpackAssemblies = false) const;
bool isPositionFree(ArtifactPosition pos, bool onlyLockCheck = false) const;
si32 getArtTypeId(ArtifactPosition pos) const;
@ -304,4 +312,7 @@ public:
}
void artDeserializationFix(CBonusSystemNode *node);
protected:
std::pair<const CCombinedArtifactInstance *, const CArtifactInstance *> searchForConstituent(int aid) const;
};

View File

@ -2,6 +2,7 @@
#include "CConfigHandler.h"
#include "../lib/filesystem/Filesystem.h"
#include "../lib/filesystem/FileStream.h"
#include "../lib/GameConstants.h"
#include "../lib/VCMIDirs.h"
@ -80,7 +81,7 @@ void SettingsStorage::invalidateNode(const std::vector<std::string> &changedPath
savedConf.Struct().erase("session");
JsonUtils::minimize(savedConf, "vcmi:settings");
std::ofstream file(*CResourceHandler::get()->getResourceName(ResourceID("config/settings.json")), std::ofstream::trunc);
FileStream file(*CResourceHandler::get()->getResourceName(ResourceID("config/settings.json")), std::ofstream::out | std::ofstream::trunc);
file << savedConf;
}
@ -173,7 +174,7 @@ JsonNode& Settings::operator [](std::string value)
{
return node[value];
}
//
//
// template DLL_LINKAGE struct SettingsStorage::NodeAccessor<SettingsListener>;
// template DLL_LINKAGE struct SettingsStorage::NodeAccessor<Settings>;
@ -214,14 +215,14 @@ void config::CConfigHandler::init()
const JsonNode config(ResourceID("config/resolutions.json"));
const JsonVector &guisettings_vec = config["GUISettings"].Vector();
for(const JsonNode &g : guisettings_vec)
for(const JsonNode &g : guisettings_vec)
{
std::pair<int,int> curRes(g["resolution"]["x"].Float(), g["resolution"]["y"].Float());
GUIOptions *current = &conf.guiOptions[curRes];
current->ac.inputLineLength = g["InGameConsole"]["maxInputPerLine"].Float();
current->ac.outputLineLength = g["InGameConsole"]["maxOutputPerLine"].Float();
current->ac.advmapX = g["AdvMap"]["x"].Float();
current->ac.advmapY = g["AdvMap"]["y"].Float();
current->ac.advmapW = g["AdvMap"]["width"].Float();

View File

@ -66,6 +66,10 @@ const PlayerState * CGameInfoCallback::getPlayer(PlayerColor color, bool verbose
{
//funtion written from scratch since it's accessed A LOT by AI
if(!color.isValidPlayer())
{
return nullptr;
}
auto player = gs->players.find(color);
if (player != gs->players.end())
{
@ -229,13 +233,13 @@ bool CGameInfoCallback::getTownInfo(const CGObjectInstance * town, InfoAboutTown
{
if(!detailed && nullptr != selectedObject)
{
const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
if(nullptr != selectedHero)
detailed = selectedHero->hasVisions(town, 1);
detailed = selectedHero->hasVisions(town, 1);
}
dest.initFromTown(static_cast<const CGTownInstance *>(town), detailed);
}
}
else if(town->ID == Obj::GARRISON || town->ID == Obj::GARRISON2)
dest.initFromArmy(static_cast<const CArmedInstance *>(town), detailed);
else
@ -268,28 +272,28 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
ERROR_RET_VAL_IF(!isVisible(h->getPosition(false)), "That hero is not visible!", false);
bool accessFlag = hasAccess(h->tempOwner);
if(!accessFlag && nullptr != selectedObject)
{
const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
if(nullptr != selectedHero)
accessFlag = selectedHero->hasVisions(hero, 1);
accessFlag = selectedHero->hasVisions(hero, 1);
}
dest.initFromHero(h, accessFlag);
//DISGUISED bonus implementation
if(getPlayerRelations(getLocalPlayer(), hero->tempOwner) == PlayerRelations::ENEMIES)
{
//todo: bonus cashing
//todo: bonus cashing
int disguiseLevel = h->valOfBonuses(Selector::typeSubtype(Bonus::DISGUISED, 0));
auto doBasicDisguise = [disguiseLevel](InfoAboutHero & info)
auto doBasicDisguise = [disguiseLevel](InfoAboutHero & info)
{
int maxAIValue = 0;
const CCreature * mostStrong = nullptr;
for(auto & elem : info.army)
{
if(elem.second.type->AIValue > maxAIValue)
@ -298,7 +302,7 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
mostStrong = elem.second.type;
}
}
if(nullptr == mostStrong)//just in case
logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Unable to select most strong stack" << disguiseLevel;
else
@ -307,25 +311,25 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
elem.second.type = mostStrong;
}
};
auto doAdvancedDisguise = [accessFlag, &doBasicDisguise](InfoAboutHero & info)
auto doAdvancedDisguise = [accessFlag, &doBasicDisguise](InfoAboutHero & info)
{
doBasicDisguise(info);
for(auto & elem : info.army)
elem.second.count = 0;
};
auto doExpertDisguise = [this,h](InfoAboutHero & info)
auto doExpertDisguise = [this,h](InfoAboutHero & info)
{
for(auto & elem : info.army)
elem.second.count = 0;
const auto factionIndex = getStartInfo(false)->playerInfos.at(h->tempOwner).castle;
int maxAIValue = 0;
const CCreature * mostStrong = nullptr;
for(auto creature : VLC->creh->creatures)
{
if(creature->faction == factionIndex && creature->AIValue > maxAIValue)
@ -334,35 +338,35 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
mostStrong = creature;
}
}
if(nullptr != mostStrong) //possible, faction may have no creatures at all
for(auto & elem : info.army)
elem.second.type = mostStrong;
};
};
switch (disguiseLevel)
{
case 0:
//no bonus at all - do nothing
break;
break;
case 1:
doBasicDisguise(dest);
break;
break;
case 2:
doAdvancedDisguise(dest);
break;
break;
case 3:
doExpertDisguise(dest);
break;
break;
default:
//invalid value
logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Invalid DISGUISED bonus value " << disguiseLevel;
break;
}
}
return true;
}
@ -486,7 +490,7 @@ std::shared_ptr<boost::multi_array<TerrainTile*, 3>> CGameInfoCallback::getAllVi
boost::multi_array<TerrainTile*, 3> tileArray(boost::extents[width][height][levels]);
for (size_t x = 0; x < width; x++)
for (size_t y = 0; y < height; y++)
for (size_t z = 0; z < levels; z++)
@ -723,15 +727,16 @@ int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool
return -1;
}
int3 CPlayerSpecificInfoCallback::getGrailPos( double &outKnownRatio )
int3 CPlayerSpecificInfoCallback::getGrailPos( double *outKnownRatio )
{
if (!player || CGObelisk::obeliskCount == 0)
{
outKnownRatio = 0.0;
*outKnownRatio = 0.0;
}
else
{
outKnownRatio = static_cast<double>(CGObelisk::visited[gs->getPlayerTeam(*player)->id]) / CGObelisk::obeliskCount;
*outKnownRatio = static_cast<double>(CGObelisk::visited[gs->getPlayerTeam(*player)->id])
/ CGObelisk::obeliskCount;
}
return gs->map->grailPos;
}
@ -964,4 +969,3 @@ void IGameEventRealizer::setObjProperty(ObjectInstanceID objid, int prop, si64 v
sob.val = static_cast<ui32>(val);
commitPackage(&sob);
}

View File

@ -125,7 +125,7 @@ class DLL_LINKAGE CPlayerSpecificInfoCallback : public CGameInfoCallback
public:
int howManyTowns() const;
int howManyHeroes(bool includeGarrisoned = true) const;
int3 getGrailPos(double &outKnownRatio);
int3 getGrailPos(double *outKnownRatio);
boost::optional<PlayerColor> getMyColor() const;
std::vector <const CGTownInstance *> getTownsInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
@ -139,7 +139,7 @@ public:
int getResourceAmount(Res::ERes type) const;
TResources getResourceAmount() const;
const std::vector< std::vector< std::vector<ui8> > > & getVisibilityMap()const; //returns visibility map
const std::vector< std::vector< std::vector<ui8> > > & getVisibilityMap()const; //returns visibility map
const PlayerSettings * getPlayerSettings(PlayerColor color) const;
};
@ -154,4 +154,3 @@ public:
virtual void showInfoDialog(const std::string &msg, PlayerColor player);
};

View File

@ -1219,8 +1219,8 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
// remove heroes which didn't reached the end of the scenario, but were available at the start
for(auto hero : lostCrossoverHeroes)
{
crossoverHeroes.heroesFromAnyPreviousScenarios.erase(range::remove_if(crossoverHeroes.heroesFromAnyPreviousScenarios,
CGObjectInstanceBySubIdFinder(hero)), crossoverHeroes.heroesFromAnyPreviousScenarios.end());
vstd::erase_if(crossoverHeroes.heroesFromAnyPreviousScenarios,
CGObjectInstanceBySubIdFinder(hero));
}
// now add heroes which completed the scenario
@ -1658,15 +1658,16 @@ void CGameState::initStartingBonus()
switch(scenarioOps->playerInfos[elem.first].bonus)
{
case PlayerSettings::GOLD:
elem.second.resources[Res::GOLD] += rand.nextInt(500, 1000);
elem.second.resources[Res::GOLD] += rand.nextInt(5, 10) * 100;
break;
case PlayerSettings::RESOURCE:
{
int res = VLC->townh->factions[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes;
if(res == Res::WOOD_AND_ORE)
{
elem.second.resources[Res::WOOD] += rand.nextInt(5, 10);
elem.second.resources[Res::ORE] += rand.nextInt(5, 10);
int amount = rand.nextInt(5, 10);
elem.second.resources[Res::WOOD] += amount;
elem.second.resources[Res::ORE] += amount;
}
else
{
@ -2267,7 +2268,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c
for (const TriggeredEvent & event : map->triggeredEvents)
{
if ((event.trigger.test(evaluateEvent)))
if (event.trigger.test(evaluateEvent))
{
if (event.effect.type == EventEffect::VICTORY)
return EVictoryLossCheckResult::victory(event.onFulfill, event.effect.toOtherMessage);
@ -2284,7 +2285,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c
return EVictoryLossCheckResult();
}
bool CGameState::checkForVictory( PlayerColor player, const EventCondition & condition ) const
bool CGameState::checkForVictory(PlayerColor player, const EventCondition & condition) const
{
const PlayerState *p = CGameInfoCallback::getPlayer(player);
switch (condition.condition)

View File

@ -17,6 +17,7 @@ set(lib_SRCS
filesystem/CFileInputStream.cpp
filesystem/CZipLoader.cpp
filesystem/Filesystem.cpp
filesystem/FileStream.cpp
filesystem/ResourceID.cpp
mapObjects/CArmedInstance.cpp

View File

@ -2,6 +2,7 @@
#include "CModHandler.h"
#include "mapObjects/CObjectClassesHandler.h"
#include "JsonNode.h"
#include "filesystem/FileStream.h"
#include "filesystem/Filesystem.h"
#include "filesystem/AdapterLoaders.h"
#include "filesystem/CFilesystemLoader.h"
@ -101,9 +102,9 @@ void CIdentifierStorage::requestIdentifier(std::string scope, std::string type,
void CIdentifierStorage::requestIdentifier(std::string scope, std::string fullName, const std::function<void(si32)>& callback)
{
auto scopeAndFullName = splitString(fullName, ':');
auto typeAndName = splitString(scopeAndFullName.second, '.');
auto scopeAndFullName = splitString(fullName, ':');
auto typeAndName = splitString(scopeAndFullName.second, '.');
requestIdentifier(ObjectCallback(scope, scopeAndFullName.first, typeAndName.first, typeAndName.second, callback, false));
}
@ -331,11 +332,11 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali
{
ModInfo & modInfo = modData[modName];
bool result = true;
auto performValidate = [&,this](JsonNode & data, const std::string & name){
handler->beforeValidate(data);
if (validate)
result &= JsonUtils::validate(data, "vcmi:" + objectName, name);
result &= JsonUtils::validate(data, "vcmi:" + objectName, name);
};
// apply patches
@ -355,7 +356,7 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali
if (originalData.size() > index)
{
JsonUtils::merge(originalData[index], data);
performValidate(originalData[index],name);
handler->loadObject(modName, name, originalData[index], index);
@ -550,21 +551,42 @@ CModHandler::CModHandler()
void CModHandler::loadConfigFromFile (std::string name)
{
std::string paths;
for(auto& p : CResourceHandler::get()->getResourceNames(ResourceID("config/" + name)))
{
paths += p.string() + ", ";
}
paths = paths.substr(0, paths.size() - 2);
logGlobal->debugStream() << "Loading hardcoded features settings from [" << paths << "], result:";
settings.data = JsonUtils::assembleFromFiles("config/" + name);
const JsonNode & hardcodedFeatures = settings.data["hardcodedFeatures"];
settings.MAX_HEROES_AVAILABLE_PER_PLAYER = hardcodedFeatures["MAX_HEROES_AVAILABLE_PER_PLAYER"].Float();
logGlobal->debugStream() << "\tMAX_HEROES_AVAILABLE_PER_PLAYER\t" << settings.MAX_HEROES_AVAILABLE_PER_PLAYER;
settings.MAX_HEROES_ON_MAP_PER_PLAYER = hardcodedFeatures["MAX_HEROES_ON_MAP_PER_PLAYER"].Float();
logGlobal->debugStream() << "\tMAX_HEROES_ON_MAP_PER_PLAYER\t" << settings.MAX_HEROES_ON_MAP_PER_PLAYER;
settings.CREEP_SIZE = hardcodedFeatures["CREEP_SIZE"].Float();
logGlobal->debugStream() << "\tCREEP_SIZE\t" << settings.CREEP_SIZE;
settings.WEEKLY_GROWTH = hardcodedFeatures["WEEKLY_GROWTH_PERCENT"].Float();
logGlobal->debugStream() << "\tWEEKLY_GROWTH\t" << settings.WEEKLY_GROWTH;
settings.NEUTRAL_STACK_EXP = hardcodedFeatures["NEUTRAL_STACK_EXP_DAILY"].Float();
logGlobal->debugStream() << "\tNEUTRAL_STACK_EXP\t" << settings.NEUTRAL_STACK_EXP;
settings.MAX_BUILDING_PER_TURN = hardcodedFeatures["MAX_BUILDING_PER_TURN"].Float();
logGlobal->debugStream() << "\tMAX_BUILDING_PER_TURN\t" << settings.MAX_BUILDING_PER_TURN;
settings.DWELLINGS_ACCUMULATE_CREATURES = hardcodedFeatures["DWELLINGS_ACCUMULATE_CREATURES"].Bool();
logGlobal->debugStream() << "\tDWELLINGS_ACCUMULATE_CREATURES\t" << settings.DWELLINGS_ACCUMULATE_CREATURES;
settings.ALL_CREATURES_GET_DOUBLE_MONTHS = hardcodedFeatures["ALL_CREATURES_GET_DOUBLE_MONTHS"].Bool();
logGlobal->debugStream() << "\tALL_CREATURES_GET_DOUBLE_MONTHS\t" << settings.ALL_CREATURES_GET_DOUBLE_MONTHS;
settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS = hardcodedFeatures["WINNING_HERO_WITH_NO_TROOPS_RETREATS"].Bool();
logGlobal->debugStream() << "\tWINNING_HERO_WITH_NO_TROOPS_RETREATS\t" << settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS;
const JsonNode & gameModules = settings.data["modules"];
modules.STACK_EXP = gameModules["STACK_EXPERIENCE"].Bool();
logGlobal->debugStream() << "\tSTACK_EXP\t" << modules.STACK_EXP;
modules.STACK_ARTIFACT = gameModules["STACK_ARTIFACTS"].Bool();
logGlobal->debugStream() << "\tSTACK_ARTIFACT\t" << modules.STACK_ARTIFACT;
modules.COMMANDERS = gameModules["COMMANDERS"].Bool();
logGlobal->debugStream() << "\tCOMMANDERS\t" << modules.COMMANDERS;
modules.MITHRIL = gameModules["MITHRIL"].Bool();
logGlobal->debugStream() << "\tMITHRIL\t" << modules.MITHRIL;
}
// currentList is passed by value to get current list of depending mods
@ -880,6 +902,6 @@ void CModHandler::afterLoad()
}
modSettings["core"] = coreMod.saveLocalData();
std::ofstream file(*CResourceHandler::get()->getResourceName(ResourceID("config/modSettings.json")), std::ofstream::trunc);
FileStream file(*CResourceHandler::get()->getResourceName(ResourceID("config/modSettings.json")), std::ofstream::out | std::ofstream::trunc);
file << modSettings;
}

View File

@ -73,7 +73,7 @@ public:
/// Function callback will be called during ID resolution phase of loading
void requestIdentifier(std::string scope, std::string type, std::string name, const std::function<void(si32)> & callback);
///fullName = [remoteScope:]type.name
void requestIdentifier(std::string scope, std::string fullName, const std::function<void(si32)> & callback);
void requestIdentifier(std::string scope, std::string fullName, const std::function<void(si32)> & callback);
void requestIdentifier(std::string type, const JsonNode & name, const std::function<void(si32)> & callback);
void requestIdentifier(const JsonNode & name, const std::function<void(si32)> & callback);
@ -253,17 +253,27 @@ public:
int CREEP_SIZE; // neutral stacks won't grow beyond this number
int WEEKLY_GROWTH; //percent
int NEUTRAL_STACK_EXP;
int NEUTRAL_STACK_EXP;
int MAX_BUILDING_PER_TURN;
bool DWELLINGS_ACCUMULATE_CREATURES;
bool ALL_CREATURES_GET_DOUBLE_MONTHS;
int MAX_HEROES_AVAILABLE_PER_PLAYER;
int MAX_HEROES_ON_MAP_PER_PLAYER;
bool WINNING_HERO_WITH_NO_TROOPS_RETREATS;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & data & CREEP_SIZE & WEEKLY_GROWTH & NEUTRAL_STACK_EXP & MAX_BUILDING_PER_TURN;
h & DWELLINGS_ACCUMULATE_CREATURES & ALL_CREATURES_GET_DOUBLE_MONTHS & MAX_HEROES_AVAILABLE_PER_PLAYER & MAX_HEROES_ON_MAP_PER_PLAYER;
h & DWELLINGS_ACCUMULATE_CREATURES & ALL_CREATURES_GET_DOUBLE_MONTHS &
MAX_HEROES_AVAILABLE_PER_PLAYER & MAX_HEROES_ON_MAP_PER_PLAYER;
if(version >= 756)
{
h & WINNING_HERO_WITH_NO_TROOPS_RETREATS;
}
else if(!h.saving)
{
WINNING_HERO_WITH_NO_TROOPS_RETREATS = true;
}
}
} settings;

View File

@ -4,6 +4,7 @@
#include "registerTypes/RegisterTypes.h"
#include "mapping/CMap.h"
#include "CGameState.h"
#include "filesystem/FileStream.h"
#include <boost/asio.hpp>
@ -282,7 +283,7 @@ void CConnection::enableSmartVectorMemberSerializatoin()
CSerializer::smartVectorMembersSerialization = true;
}
CSaveFile::CSaveFile( const std::string &fname ): serializer(this)
CSaveFile::CSaveFile( const boost::filesystem::path &fname ): serializer(this)
{
registerTypes(serializer);
openNextFile(fname);
@ -298,12 +299,12 @@ int CSaveFile::write( const void * data, unsigned size )
return size;
}
void CSaveFile::openNextFile(const std::string &fname)
void CSaveFile::openNextFile(const boost::filesystem::path &fname)
{
fName = fname;
try
{
sfile = make_unique<std::ofstream>(fname.c_str(), std::ios::binary);
sfile = make_unique<FileStream>(fname, std::ios::out | std::ios::binary);
sfile->exceptions(std::ifstream::failbit | std::ifstream::badbit); //we throw a lot anyway
if(!(*sfile))
@ -364,7 +365,7 @@ void CLoadFile::openNextFile(const boost::filesystem::path & fname, int minimalV
try
{
fName = fname.string();
sfile = make_unique<boost::filesystem::ifstream>(fname, std::ios::binary);
sfile = make_unique<FileStream>(fname, std::ios::in | std::ios::binary);
sfile->exceptions(std::ifstream::failbit | std::ifstream::badbit); //we throw a lot anyway
if(!(*sfile))
@ -569,7 +570,7 @@ void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib)
smartVectorMembersSerialization = true;
}
CLoadIntegrityValidator::CLoadIntegrityValidator( const std::string &primaryFileName, const std::string &controlFileName, int minimalVersion /*= version*/ )
CLoadIntegrityValidator::CLoadIntegrityValidator( const boost::filesystem::path &primaryFileName, const boost::filesystem::path &controlFileName, int minimalVersion /*= version*/ )
: serializer(this), foundDesync(false)
{
registerTypes(serializer);

View File

@ -27,7 +27,7 @@
#include "mapping/CCampaignHandler.h" //for CCampaignState
#include "rmg/CMapGenerator.h" // for CMapGenOptions
const ui32 version = 755;
const ui32 version = 757;
const ui32 minSupportedVersion = 753;
class CISer;
@ -39,6 +39,7 @@ class CGameState;
class CCreature;
class LibClasses;
class CHero;
class FileStream;
struct CPack;
extern DLL_LINKAGE LibClasses * VLC;
namespace mpl = boost::mpl;
@ -1550,14 +1551,14 @@ public:
COSer serializer;
std::string fName;
std::unique_ptr<std::ofstream> sfile;
boost::filesystem::path fName;
std::unique_ptr<FileStream> sfile;
CSaveFile(const std::string &fname); //throws!
CSaveFile(const boost::filesystem::path &fname); //throws!
~CSaveFile();
int write(const void * data, unsigned size) override;
void openNextFile(const std::string &fname); //throws!
void openNextFile(const boost::filesystem::path &fname); //throws!
void clear();
void reportState(CLogger * out) override;
@ -1577,8 +1578,8 @@ class DLL_LINKAGE CLoadFile
public:
CISer serializer;
std::string fName;
std::unique_ptr<boost::filesystem::ifstream> sfile;
boost::filesystem::path fName;
std::unique_ptr<FileStream> sfile;
CLoadFile(const boost::filesystem::path & fname, int minimalVersion = version); //throws!
~CLoadFile();
@ -1606,7 +1607,7 @@ public:
std::unique_ptr<CLoadFile> primaryFile, controlFile;
bool foundDesync;
CLoadIntegrityValidator(const std::string &primaryFileName, const std::string &controlFileName, int minimalVersion = version); //throws!
CLoadIntegrityValidator(const boost::filesystem::path &primaryFileName, const boost::filesystem::path &controlFileName, int minimalVersion = version); //throws!
int read( void * data, unsigned size) override; //throws!
void checkMagicBytes(const std::string &text);

View File

@ -56,6 +56,15 @@ JsonNode::JsonNode(ResourceID && fileURI):
*this = parser.parse(fileURI.getName());
}
JsonNode::JsonNode(const ResourceID & fileURI):
type(DATA_NULL)
{
auto file = CResourceHandler::get()->load(fileURI)->readAll();
JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second);
*this = parser.parse(fileURI.getName());
}
JsonNode::JsonNode(ResourceID && fileURI, bool &isValidSyntax):
type(DATA_NULL)
{
@ -328,7 +337,7 @@ void JsonUtils::parseTypedBonusShort(const JsonVector& source, Bonus *dest)
resolveIdentifier(source[2],dest->subtype);
dest->additionalInfo = source[3].Float();
dest->duration = Bonus::PERMANENT; //TODO: handle flags (as integer)
dest->turnsRemain = 0;
dest->turnsRemain = 0;
}
@ -343,7 +352,7 @@ Bonus * JsonUtils::parseBonus (const JsonVector &ability_vec) //TODO: merge with
return b;
}
b->type = it->second;
parseTypedBonusShort(ability_vec, b);
return b;
}

View File

@ -7,7 +7,7 @@
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
class JsonNode;
@ -55,6 +55,7 @@ public:
explicit JsonNode(const char * data, size_t datasize);
//Create tree from JSON file
explicit JsonNode(ResourceID && fileURI);
explicit JsonNode(const ResourceID & fileURI);
explicit JsonNode(ResourceID && fileURI, bool & isValidSyntax);
//Copy c-tor
JsonNode(const JsonNode &copy);
@ -125,9 +126,9 @@ namespace JsonUtils
/**
* @brief parse short bonus format, excluding type
* @note sets duration to Permament
*/
*/
DLL_LINKAGE void parseTypedBonusShort(const JsonVector &source, Bonus *dest);
///
DLL_LINKAGE Bonus * parseBonus (const JsonVector &ability_vec);
DLL_LINKAGE Bonus * parseBonus (const JsonNode &bonus);
@ -144,7 +145,7 @@ namespace JsonUtils
* @note this function will destroy data in source
*/
DLL_LINKAGE void merge(JsonNode & dest, JsonNode & source);
/**
* @brief recursively merges source into dest, replacing identical fields
* struct : recursively calls this function
@ -152,12 +153,12 @@ namespace JsonUtils
* values : value in source will replace value in dest
* null : if value in source is present but set to null it will delete entry in dest
* @note this function will preserve data stored in source by creating copy
*/
*/
DLL_LINKAGE void mergeCopy(JsonNode & dest, JsonNode source);
/** @brief recursively merges descendant into copy of base node
* Result emulates inheritance semantic
*
*
*
*/
DLL_LINKAGE void inherit(JsonNode & descendant, const JsonNode & base);
@ -200,7 +201,7 @@ namespace JsonDetail
{
// conversion helpers for JsonNode::convertTo (partial template function instantiation is illegal in c++)
template <typename T, int arithm>
template <typename T, int arithm>
struct JsonConvImpl;
template <typename T>
@ -229,7 +230,7 @@ namespace JsonDetail
///this should be triggered only for numeric types and enums
static_assert(boost::mpl::or_<std::is_arithmetic<Type>, std::is_enum<Type>, boost::is_class<Type> >::value, "Unsupported type for JsonNode::convertTo()!");
return JsonConvImpl<Type, boost::mpl::or_<std::is_enum<Type>, boost::is_class<Type> >::value >::convertImpl(node);
}
};

View File

@ -340,7 +340,7 @@ DLL_LINKAGE void RemoveBonus::applyGs( CGameState *gs )
if(b->source == source && b->sid == id)
{
bonus = *b; //backup bonus (to show to interfaces later)
node->removeBonus(b);
node->removeBonus(b);
break;
}
}
@ -362,6 +362,10 @@ DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs )
p->heroes -= h;
h->detachFrom(h->whereShouldBeAttached(gs));
h->tempOwner = PlayerColor::NEUTRAL; //no one owns beaten hero
vstd::erase_if(h->artifactsInBackpack, [](const ArtSlotInfo& asi)
{
return asi.artifact->artType->id == ArtifactID::GRAIL;
});
if(h->visitedTown)
{
@ -590,7 +594,11 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs )
h->setOwner(player);
h->pos = tile;
h->movement = h->maxMovePoints(true);
bool fresh = !h->isInitialized();
if(fresh)
{ // this is a fresh hero who hasn't appeared yet
h->movement = h->maxMovePoints(true);
}
gs->hpool.heroesPool.erase(hid);
if(h->id == ObjectInstanceID())
@ -604,7 +612,10 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs )
gs->map->heroesOnMap.push_back(h);
p->heroes.push_back(h);
h->attachTo(p);
h->initObj();
if(fresh)
{
h->initObj();
}
gs->map->addBlockVisTiles(h);
if(t)
@ -754,7 +765,7 @@ DLL_LINKAGE const CArtifactInstance *ArtifactLocation::getArt() const
return s->artifact;
else
{
logNetwork->warnStream() << "ArtifactLocation::getArt: That location is locked!";
logNetwork->warnStream() << "ArtifactLocation::getArt: This location is locked!";
return nullptr;
}
}
@ -914,6 +925,32 @@ DLL_LINKAGE void PutArtifact::applyGs( CGameState *gs )
DLL_LINKAGE void EraseArtifact::applyGs( CGameState *gs )
{
auto slot = al.getSlot();
if(slot->locked)
{
logGlobal->debugStream() << "Erasing locked artifact: " << slot->artifact->artType->Name();
DisassembledArtifact dis;
dis.al.artHolder = al.artHolder;
auto aset = al.getHolderArtSet();
bool found = false;
for(auto& p : aset->artifactsWorn)
{
auto art = p.second.artifact;
if(art->canBeDisassembled() && art->isPart(slot->artifact))
{
dis.al.slot = aset->getArtPos(art);
found = true;
break;
}
}
assert(found && "Failed to determine the assembly this locked artifact belongs to");
logGlobal->debugStream() << "Found the corresponding assembly: " << dis.al.getSlot()->artifact->artType->Name();
dis.applyGs(gs);
}
else
{
logGlobal->debugStream() << "Erasing artifact " << slot->artifact->artType->Name();
}
al.removeArtifact();
}
@ -1262,7 +1299,7 @@ DLL_LINKAGE void BattleStackAttacked::applyGs( CGameState *gs )
{
//"hide" killed creatures instead so we keep info about it
at->state.insert(EBattleStackState::DEAD_CLONE);
for(CStack * s : gs->curB->stacks)
{
if(s->cloneID == at->ID)
@ -1375,7 +1412,7 @@ void actualizeEffect(CStack * s, const Bonus & ef)
stackBonus->turnsRemain = std::max(stackBonus->turnsRemain, ef.turnsRemain);
}
}
CBonusSystemNode::treeHasChanged();
CBonusSystemNode::treeHasChanged();
}
void actualizeEffect(CStack * s, const std::vector<Bonus> & ef)

View File

@ -4,7 +4,7 @@
<Project>
<Option title="VCMI_lib" />
<Option execution_dir="D:/projects/vcmi/engine/VCMI_lib/" />
<Option pch_mode="2" />
<Option pch_mode="0" />
<Option compiler="gcc" />
<Build>
<Target title="Debug-win32">
@ -17,8 +17,8 @@
<Option run_host_application_in_terminal="1" />
<Option createStaticLib="1" />
<Compiler>
<Add option="-Og" />
<Add option="-g" />
<Add option="-Og" />
<Add directory="$(#zlib.include)" />
</Compiler>
<Linker>
@ -62,7 +62,6 @@
<Add option="-lboost_locale$(#boost.libsuffix)" />
<Add option="-lboost_date_time$(#boost.libsuffix)" />
<Add option="-liconv" />
<Add option="-ldbghelp" />
<Add directory="$(#sdl2.lib)" />
<Add directory="$(#boost.lib32)" />
<Add directory="$(#zlib.lib)" />
@ -103,6 +102,7 @@
<Compiler>
<Add option="-Wextra" />
<Add option="-Wall" />
<Add option="-std=gnu++11" />
<Add option="-fexceptions" />
<Add option="-Wpointer-arith" />
<Add option="-Wno-switch" />
@ -112,7 +112,9 @@
<Add option="-Wno-unused-local-typedefs" />
<Add option="-DVCMI_DLL" />
<Add option="-DBOOST_THREAD_USE_LIB" />
<Add option="-DBOOST_SYSTEM_NO_DEPRECATED" />
<Add option="-D_WIN32_WINNT=0x0501" />
<Add option="-D_WIN32" />
<Add directory="$(#boost.include)" />
<Add directory="../include" />
<Add directory="../lib" />
@ -200,7 +202,6 @@
<Unit filename="StartInfo.h" />
<Unit filename="StdInc.h">
<Option weight="0" />
<Option target="Debug-win64" />
</Unit>
<Unit filename="StringConstants.h" />
<Unit filename="UnlockGuard.h" />
@ -227,6 +228,8 @@
<Unit filename="filesystem/CMemoryStream.h" />
<Unit filename="filesystem/CZipLoader.cpp" />
<Unit filename="filesystem/CZipLoader.h" />
<Unit filename="filesystem/FileStream.cpp" />
<Unit filename="filesystem/FileStream.h" />
<Unit filename="filesystem/Filesystem.cpp" />
<Unit filename="filesystem/Filesystem.h" />
<Unit filename="filesystem/ISimpleResourceLoader.h" />

View File

@ -27,7 +27,7 @@ std::string CMappedFileLoader::getMountPoint() const
return ""; // does not have any meaning with this type of data source
}
boost::optional<std::string> CMappedFileLoader::getResourceName(const ResourceID & resourceName) const
boost::optional<boost::filesystem::path> CMappedFileLoader::getResourceName(const ResourceID & resourceName) const
{
return CResourceHandler::get()->getResourceName(fileList.at(resourceName));
}
@ -80,11 +80,25 @@ std::string CFilesystemList::getMountPoint() const
return "";
}
boost::optional<std::string> CFilesystemList::getResourceName(const ResourceID & resourceName) const
boost::optional<boost::filesystem::path> CFilesystemList::getResourceName(const ResourceID & resourceName) const
{
if (existsResource(resourceName))
return getResourcesWithName(resourceName).back()->getResourceName(resourceName);
return boost::optional<std::string>();
return boost::optional<boost::filesystem::path>();
}
std::set<boost::filesystem::path> CFilesystemList::getResourceNames(const ResourceID & resourceName) const
{
std::set<boost::filesystem::path> paths;
for(auto& loader : getResourcesWithName(resourceName))
{
auto rn = loader->getResourceName(resourceName);
if(rn)
{
paths.insert(*rn);
}
}
return std::move(paths);
}
std::unordered_set<ResourceID> CFilesystemList::getFilteredFiles(std::function<bool(const ResourceID &)> filter) const

View File

@ -1,93 +1,87 @@
#pragma once
/*
* AdapterLoaders.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
*
*/
#include "ISimpleResourceLoader.h"
#include "ResourceID.h"
class CFileInfo;
class CInputStream;
class JsonNode;
/**
* 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 std::string &mountPoint, const JsonNode & config);
/// Interface implementation
/// @see ISimpleResourceLoader
std::unique_ptr<CInputStream> load(const ResourceID & resourceName) const override;
bool existsResource(const ResourceID & resourceName) const override;
std::string getMountPoint() const override;
boost::optional<std::string> getResourceName(const ResourceID & resourceName) const override;
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) 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
*/
std::unordered_map<ResourceID, ResourceID> fileList;
};
class DLL_LINKAGE CFilesystemList : public ISimpleResourceLoader
{
std::vector<std::unique_ptr<ISimpleResourceLoader> > loaders;
std::set<ISimpleResourceLoader *> writeableLoaders;
//FIXME: this is only compile fix, should be removed in the end
CFilesystemList(CFilesystemList &)
{
//class is not copyable
}
CFilesystemList &operator=(CFilesystemList &)
{
//class is not copyable
return *this;
}
public:
CFilesystemList();
~CFilesystemList();
/// Interface implementation
/// @see ISimpleResourceLoader
std::unique_ptr<CInputStream> load(const ResourceID & resourceName) const override;
bool existsResource(const ResourceID & resourceName) const override;
std::string getMountPoint() const override;
boost::optional<std::string> getResourceName(const ResourceID & resourceName) const override;
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override;
bool createResource(std::string filename, bool update = false) override;
std::vector<const ISimpleResourceLoader *> getResourcesWithName(const ResourceID & resourceName) const override;
/**
* Adds a resource loader to the loaders list
* Passes loader ownership to this object
*
* @param loader The simple resource loader object to add
* @param writeable - resource shall be treated as writeable
*/
void addLoader(ISimpleResourceLoader * loader, bool writeable);
};
#pragma once
/*
* AdapterLoaders.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
*
*/
#include "ISimpleResourceLoader.h"
#include "ResourceID.h"
class CFileInfo;
class CInputStream;
class JsonNode;
/**
* 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 std::string &mountPoint, const JsonNode & config);
/// Interface implementation
/// @see ISimpleResourceLoader
std::unique_ptr<CInputStream> load(const ResourceID & resourceName) const override;
bool existsResource(const ResourceID & resourceName) const override;
std::string getMountPoint() const override;
boost::optional<boost::filesystem::path> getResourceName(const ResourceID & resourceName) const override;
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) 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
*/
std::unordered_map<ResourceID, ResourceID> fileList;
};
class DLL_LINKAGE CFilesystemList : public ISimpleResourceLoader
{
std::vector<std::unique_ptr<ISimpleResourceLoader> > loaders;
std::set<ISimpleResourceLoader *> writeableLoaders;
//FIXME: this is only compile fix, should be removed in the end
CFilesystemList(CFilesystemList &) = delete;
CFilesystemList &operator=(CFilesystemList &) = delete;
public:
CFilesystemList();
~CFilesystemList();
/// Interface implementation
/// @see ISimpleResourceLoader
std::unique_ptr<CInputStream> load(const ResourceID & resourceName) const override;
bool existsResource(const ResourceID & resourceName) const override;
std::string getMountPoint() const override;
boost::optional<boost::filesystem::path> getResourceName(const ResourceID & resourceName) const override;
std::set<boost::filesystem::path> getResourceNames(const ResourceID & resourceName) const override;
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override;
bool createResource(std::string filename, bool update = false) override;
std::vector<const ISimpleResourceLoader *> getResourcesWithName(const ResourceID & resourceName) const override;
/**
* Adds a resource loader to the loaders list
* Passes loader ownership to this object
*
* @param loader The simple resource loader object to add
* @param writeable - resource shall be treated as writeable
*/
void addLoader(ISimpleResourceLoader * loader, bool writeable);
};

View File

@ -4,39 +4,25 @@
#include "CFileInfo.h"
CFileInputStream::CFileInputStream(const boost::filesystem::path & file, si64 start, si64 size)
: dataStart{start},
dataSize{size},
fileStream{file, std::ios::in | std::ios::binary}
{
open(file, start, size);
}
CFileInputStream::CFileInputStream(const CFileInfo & file, si64 start, si64 size)
{
open(file.getName(), start, size);
}
CFileInputStream::~CFileInputStream()
{
fileStream.close();
}
void CFileInputStream::open(const boost::filesystem::path & file, si64 start, si64 size)
{
fileStream.open(file, std::ios::in | std::ios::binary);
if (fileStream.fail())
throw std::runtime_error("File " + file.string() + " isn't available.");
dataStart = start;
dataSize = size;
if (dataSize == 0)
{
fileStream.seekg(0, std::ios::end);
dataSize = tell();
}
fileStream.seekg(start, std::ios::beg);
fileStream.seekg(dataStart, std::ios::beg);
}
CFileInputStream::CFileInputStream(const CFileInfo & file, si64 start, si64 size)
: CFileInputStream{file.getName(), start, size} {}
si64 CFileInputStream::read(ui8 * data, si64 size)
{
si64 origin = tell();

View File

@ -11,6 +11,7 @@
*/
#include "CInputStream.h"
#include "FileStream.h"
class CFileInfo;
@ -23,22 +24,21 @@ public:
/**
* C-tor. Opens the specified file.
*
* @see CFileInputStream::open
* @param file Path to the file.
* @param start - offset from file start where real data starts (e.g file on archive)
* @param size - size of real data in file (e.g file on archive) or 0 to use whole file
*
* @throws std::runtime_error if file wasn't found
*/
CFileInputStream(const boost::filesystem::path & file, si64 start = 0, si64 size = 0);
/**
* C-tor. Opens the specified file.
*
* @see CFileInputStream::open
* @see CFileInputStream::CFileInputStream(const boost::filesystem::path &, si64, si64)
*/
CFileInputStream(const CFileInfo & file, si64 start=0, si64 size=0);
/**
* D-tor. Calls the close method implicitely, if the file is still opened.
*/
~CFileInputStream();
/**
* Reads n bytes from the stream into the data buffer.
*
@ -79,20 +79,9 @@ public:
si64 getSize() override;
private:
/**
* Opens a file. If a file is currently opened, it will be closed.
*
* @param file Path to the file.
* @param start - offset from file start where real data starts (e.g file on archive)
* @param size - size of real data in file (e.g file on archive) or 0 to use whole file
*
* @throws std::runtime_error if file wasn't found
*/
void open(const boost::filesystem::path & file, si64 start, si64 size);
si64 dataStart;
si64 dataSize;
/** Native c++ input file stream object. */
boost::filesystem::ifstream fileStream;
FileStream fileStream;
};

View File

@ -3,6 +3,7 @@
#include "CFileInfo.h"
#include "CFileInputStream.h"
#include "FileStream.h"
namespace bfs = boost::filesystem;
@ -32,11 +33,11 @@ std::string CFilesystemLoader::getMountPoint() const
return mountPoint;
}
boost::optional<std::string> CFilesystemLoader::getResourceName(const ResourceID & resourceName) const
boost::optional<boost::filesystem::path> CFilesystemLoader::getResourceName(const ResourceID & resourceName) const
{
assert(existsResource(resourceName));
return (baseDirectory / fileList.at(resourceName)).string();
return baseDirectory / fileList.at(resourceName);
}
std::unordered_set<ResourceID> CFilesystemLoader::getFilteredFiles(std::function<bool(const ResourceID &)> filter) const
@ -68,8 +69,7 @@ bool CFilesystemLoader::createResource(std::string filename, bool update)
if (!update)
{
bfs::ofstream newfile(baseDirectory / filename);
if (!newfile.good())
if (!FileStream::CreateFile(baseDirectory / filename))
return false;
}
fileList[resID] = filename;

View File

@ -38,7 +38,7 @@ public:
bool existsResource(const ResourceID & resourceName) const override;
std::string getMountPoint() const override;
bool createResource(std::string filename, bool update = false) override;
boost::optional<std::string> getResourceName(const ResourceID & resourceName) const override;
boost::optional<boost::filesystem::path> getResourceName(const ResourceID & resourceName) const override;
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override;
private:

View File

@ -1,6 +1,7 @@
#include "StdInc.h"
#include "../../Global.h"
//#include "../../Global.h"
#include "CZipLoader.h"
#include "FileStream.h"
#include "../ScopeGuard.h"
@ -14,10 +15,10 @@
*
*/
CZipStream::CZipStream(const std::string & archive, unz_file_pos filepos)
CZipStream::CZipStream(const boost::filesystem::path & archive, unz64_file_pos filepos)
{
file = unzOpen(archive.c_str());
unzGoToFilePos(file, &filepos);
file = unzOpen2_64(archive.c_str(), FileStream::GetMinizipFilefunc());
unzGoToFilePos64(file, &filepos);
unzOpenCurrentFile(file);
}
@ -34,19 +35,19 @@ si64 CZipStream::readMore(ui8 * data, si64 size)
si64 CZipStream::getSize()
{
unz_file_info info;
unzGetCurrentFileInfo (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
unz_file_info64 info;
unzGetCurrentFileInfo64 (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
return info.uncompressed_size;
}
ui32 CZipStream::calculateCRC32()
{
unz_file_info info;
unzGetCurrentFileInfo (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
unz_file_info64 info;
unzGetCurrentFileInfo64 (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
return info.crc;
}
CZipLoader::CZipLoader(const std::string & mountPoint, const std::string & archive):
CZipLoader::CZipLoader(const std::string & mountPoint, const boost::filesystem::path & archive):
archiveName(archive),
mountPoint(mountPoint),
files(listFiles(mountPoint, archive))
@ -54,27 +55,27 @@ CZipLoader::CZipLoader(const std::string & mountPoint, const std::string & archi
logGlobal->traceStream() << "Zip archive loaded, " << files.size() << " files found";
}
std::unordered_map<ResourceID, unz_file_pos> CZipLoader::listFiles(const std::string & mountPoint, const std::string & archive)
std::unordered_map<ResourceID, unz64_file_pos> CZipLoader::listFiles(const std::string & mountPoint, const boost::filesystem::path & archive)
{
std::unordered_map<ResourceID, unz_file_pos> ret;
std::unordered_map<ResourceID, unz64_file_pos> ret;
unzFile file = unzOpen(archive.c_str());
unzFile file = unzOpen2_64(archive.c_str(), FileStream::GetMinizipFilefunc());
if (unzGoToFirstFile(file) == UNZ_OK)
{
do
{
unz_file_info info;
unz_file_info64 info;
std::vector<char> filename;
// Fill unz_file_info structure with current file info
unzGetCurrentFileInfo (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
unzGetCurrentFileInfo64 (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
filename.resize(info.size_filename);
// Get name of current file. Contrary to docs "info" parameter can't be null
unzGetCurrentFileInfo (file, &info, filename.data(), filename.size(), nullptr, 0, nullptr, 0);
unzGetCurrentFileInfo64 (file, &info, filename.data(), filename.size(), nullptr, 0, nullptr, 0);
std::string filenameString(filename.data(), filename.size());
unzGetFilePos(file, &ret[ResourceID(mountPoint + filenameString)]);
unzGetFilePos64(file, &ret[ResourceID(mountPoint + filenameString)]);
}
while (unzGoToNextFile(file) == UNZ_OK);
}
@ -140,24 +141,24 @@ static bool extractCurrent(unzFile file, std::ostream & where)
return false;
}
std::vector<std::string> ZipArchive::listFiles(std::string filename)
std::vector<std::string> ZipArchive::listFiles(boost::filesystem::path filename)
{
std::vector<std::string> ret;
unzFile file = unzOpen(filename.c_str());
unzFile file = unzOpen2_64(filename.c_str(), FileStream::GetMinizipFilefunc());
if (unzGoToFirstFile(file) == UNZ_OK)
{
do
{
unz_file_info info;
unz_file_info64 info;
std::vector<char> filename;
unzGetCurrentFileInfo (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
unzGetCurrentFileInfo64 (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
filename.resize(info.size_filename);
// Get name of current file. Contrary to docs "info" parameter can't be null
unzGetCurrentFileInfo (file, &info, filename.data(), filename.size(), nullptr, 0, nullptr, 0);
unzGetCurrentFileInfo64 (file, &info, filename.data(), filename.size(), nullptr, 0, nullptr, 0);
ret.push_back(std::string(filename.data(), filename.size()));
}
@ -168,29 +169,29 @@ std::vector<std::string> ZipArchive::listFiles(std::string filename)
return ret;
}
bool ZipArchive::extract(std::string from, std::string where)
bool ZipArchive::extract(boost::filesystem::path from, boost::filesystem::path where)
{
// Note: may not be fast enough for large archives (should NOT happen with mods)
// because locating each file by name may be slow. Unlikely slower than decompression though
return extract(from, where, listFiles(from));
}
bool ZipArchive::extract(std::string from, std::string where, std::vector<std::string> what)
bool ZipArchive::extract(boost::filesystem::path from, boost::filesystem::path where, std::vector<std::string> what)
{
unzFile archive = unzOpen(from.c_str());
unzFile archive = unzOpen2_64(from.c_str(), FileStream::GetMinizipFilefunc());
auto onExit = vstd::makeScopeGuard([&]()
{
unzClose(archive);
});
for (std::string & file : what)
for (const std::string & file : what)
{
if (unzLocateFile(archive, file.c_str(), 1) != UNZ_OK)
return false;
std::string fullName = where + '/' + file;
std::string fullPath = fullName.substr(0, fullName.find_last_of("/"));
const boost::filesystem::path fullName = where / file;
const boost::filesystem::path fullPath = fullName.parent_path();
boost::filesystem::create_directories(fullPath);
// directory. No file to extract
@ -198,7 +199,7 @@ bool ZipArchive::extract(std::string from, std::string where, std::vector<std::s
if (boost::algorithm::ends_with(file, "/"))
continue;
std::ofstream destFile(fullName, std::ofstream::binary);
FileStream destFile(fullName, std::ios::out | std::ios::binary);
if (!destFile.good())
return false;

View File

@ -32,7 +32,7 @@ public:
* @param archive path to archive to open
* @param filepos position of file to open
*/
CZipStream(const std::string & archive, unz_file_pos filepos);
CZipStream(const boost::filesystem::path & archive, unz64_file_pos filepos);
~CZipStream();
si64 getSize() override;
@ -44,14 +44,14 @@ protected:
class DLL_LINKAGE CZipLoader : public ISimpleResourceLoader
{
std::string archiveName;
boost::filesystem::path archiveName;
std::string mountPoint;
std::unordered_map<ResourceID, unz_file_pos> files;
std::unordered_map<ResourceID, unz64_file_pos> files;
std::unordered_map<ResourceID, unz_file_pos> listFiles(const std::string & mountPoint, const std::string &archive);
std::unordered_map<ResourceID, unz64_file_pos> listFiles(const std::string & mountPoint, const boost::filesystem::path &archive);
public:
CZipLoader(const std::string & mountPoint, const std::string & archive);
CZipLoader(const std::string & mountPoint, const boost::filesystem::path & archive);
/// Interface implementation
/// @see ISimpleResourceLoader
@ -65,11 +65,11 @@ public:
namespace ZipArchive
{
/// List all files present in archive
std::vector<std::string> DLL_LINKAGE listFiles(std::string filename);
std::vector<std::string> DLL_LINKAGE listFiles(boost::filesystem::path filename);
/// extracts all files from archive "from" into destination directory "where". Directory must exist
bool DLL_LINKAGE extract(std::string from, std::string where);
bool DLL_LINKAGE extract(boost::filesystem::path from, boost::filesystem::path where);
///same as above, but extracts only files mentioned in "what" list
bool DLL_LINKAGE extract(std::string from, std::string where, std::vector<std::string> what);
bool DLL_LINKAGE extract(boost::filesystem::path from, boost::filesystem::path where, std::vector<std::string> what);
}

View File

@ -0,0 +1,156 @@
#include "StdInc.h"
#include "FileStream.h"
#ifdef USE_SYSTEM_MINIZIP
#include <minizip/unzip.h>
#else
#include "../minizip/unzip.h"
#endif
#include <cstdio>
#ifdef VCMI_WINDOWS
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <cwchar>
#define CHAR_LITERAL(s) L##s
using CharType = wchar_t;
#else
#define CHAR_LITERAL(s) s
using CharType = char;
#endif
inline FILE* do_open(const CharType* name, const CharType* mode)
{
#ifdef VCMI_WINDOWS
return _wfopen(name, mode);
#else
return std::fopen(name, mode);
#endif
}
#define GETFILE static_cast<std::FILE*>(filePtr)
voidpf ZCALLBACK MinizipOpenFunc(voidpf opaque, const void* filename, int mode)
{
const CharType* mode_fopen = [mode]() -> const CharType*
{
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)
return CHAR_LITERAL("rb");
else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
return CHAR_LITERAL("r+b");
else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
return CHAR_LITERAL("wb");
return nullptr;
}();
if (filename != nullptr && mode_fopen != nullptr)
return do_open(static_cast<const CharType*>(filename), mode_fopen);
else
return nullptr;
}
zlib_filefunc64_def* FileStream::GetMinizipFilefunc()
{
static zlib_filefunc64_def MinizipFilefunc;
static bool initialized = false;
if (!initialized) {
fill_fopen64_filefunc((&MinizipFilefunc));
MinizipFilefunc.zopen64_file = &MinizipOpenFunc;
initialized = true;
}
return &MinizipFilefunc;
}
template class boost::iostreams::stream<FileBuf>;
/*static*/
bool FileStream::CreateFile(const boost::filesystem::path& filename)
{
FILE* f = do_open(filename.c_str(), CHAR_LITERAL("wb"));
bool result = (f != nullptr);
fclose(f);
return result;
}
FileBuf::FileBuf(const boost::filesystem::path& filename, std::ios_base::openmode mode)
{
auto openmode = [mode]() -> std::basic_string<CharType>
{
using namespace std;
switch (mode & (~ios_base::ate & ~ios_base::binary))
{
case (ios_base::in):
return CHAR_LITERAL("r");
case (ios_base::out):
case (ios_base::out | ios_base::trunc):
return CHAR_LITERAL("w");
case (ios_base::app):
case (ios_base::out | ios_base::app):
return CHAR_LITERAL("a");
case (ios_base::out | ios_base::in):
return CHAR_LITERAL("r+");
case (ios_base::out | ios_base::in | ios_base::trunc):
return CHAR_LITERAL("w+");
case (ios_base::out | ios_base::in | ios_base::app):
case (ios_base::in | ios_base::app):
return CHAR_LITERAL("a+");
default:
throw std::ios_base::failure("invalid open mode");
}
}();
if (mode & std::ios_base::binary)
openmode += CHAR_LITERAL('b');
filePtr = do_open(filename.c_str(), openmode.c_str());
if (filePtr == nullptr)
throw std::ios_base::failure("could not open file");
if (mode & std::ios_base::ate) {
if (std::fseek(GETFILE, 0, SEEK_END)) {
fclose(GETFILE);
throw std::ios_base::failure("could not open file");
}
}
}
void FileBuf::close()
{
std::fclose(GETFILE);
}
std::streamsize FileBuf::read(char* s, std::streamsize n)
{
return static_cast<std::streamsize>(std::fread(s, 1, n, GETFILE));
}
std::streamsize FileBuf::write(const char* s, std::streamsize n)
{
return static_cast<std::streamsize>(std::fwrite(s, 1, n, GETFILE));
}
std::streamoff FileBuf::seek(std::streamoff off, std::ios_base::seekdir way)
{
const auto src = [way]() -> int
{
switch(way)
{
case std::ios_base::beg:
return SEEK_SET;
case std::ios_base::cur:
return SEEK_CUR;
case std::ios_base::end:
return SEEK_END;
default:
throw std::ios_base::failure("bad seek direction");
}
}();
if(std::fseek(GETFILE, off, src))
throw std::ios_base::failure("bad seek offset");
return static_cast<std::streamsize>(std::ftell(GETFILE));
}

View File

@ -0,0 +1,53 @@
#pragma once
/*
* FileStream.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
*
*/
#include <boost/iostreams/categories.hpp>
#include <boost/iostreams/stream.hpp>
class DLL_LINKAGE FileBuf
{
public:
typedef char char_type;
typedef struct category_ :
boost::iostreams::seekable_device_tag,
boost::iostreams::closable_tag
{} category;
FileBuf(const boost::filesystem::path& filename, std::ios_base::openmode mode);
std::streamsize read(char* s, std::streamsize n);
std::streamsize write(const char* s, std::streamsize n);
std::streamoff seek(std::streamoff off, std::ios_base::seekdir way);
void close();
private:
void* filePtr;
};
struct zlib_filefunc64_def_s;
typedef zlib_filefunc64_def_s zlib_filefunc64_def;
#ifdef VCMI_DLL
extern template class DLL_LINKAGE boost::iostreams::stream<FileBuf>;
#endif
class DLL_LINKAGE FileStream : public boost::iostreams::stream<FileBuf>
{
public:
FileStream() = default;
explicit FileStream(const boost::filesystem::path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
: boost::iostreams::stream<FileBuf>(p, mode) {}
static bool CreateFile(const boost::filesystem::path& filename);
static zlib_filefunc64_def* GetMinizipFilefunc();
};

View File

@ -48,9 +48,25 @@ public:
*
* @return path or empty optional if file can't be accessed independently (e.g. file in archive)
*/
virtual boost::optional<std::string> getResourceName(const ResourceID & resourceName) const
virtual boost::optional<boost::filesystem::path> getResourceName(const ResourceID & resourceName) const
{
return boost::optional<std::string>();
return boost::optional<boost::filesystem::path>();
}
/**
* Gets all full names of matching resources, e.g. names of files in filesystem.
*
* @return std::set with names.
*/
virtual std::set<boost::filesystem::path> getResourceNames(const ResourceID & resourceName) const
{
std::set<boost::filesystem::path> result;
auto rn = getResourceName(resourceName);
if(rn)
{
result.insert(*rn);
}
return result;
}
/**

View File

@ -12,6 +12,7 @@
#pragma once
#include "../CConsoleHandler.h"
#include "../filesystem/FileStream.h"
class CLogger;
struct LogRecord;
@ -147,7 +148,7 @@ private:
/// Macros for tracing the control flow of the application conveniently. If the LOG_TRACE macro is used it should be
/// the first statement in the function. Logging traces via this macro have almost no impact when the trace is disabled.
///
///
#define RAII_TRACE(logger, onEntry, onLeave) \
std::unique_ptr<CTraceLogger> ctl00; \
if(logger->isTraceEnabled()) \
@ -217,7 +218,7 @@ public:
CLogFormatter(CLogFormatter && move);
CLogFormatter(const std::string & pattern);
CLogFormatter & operator=(const CLogFormatter & copy);
CLogFormatter & operator=(CLogFormatter && move);
@ -302,7 +303,7 @@ public:
void write(const LogRecord & record) override;
private:
boost::filesystem::ofstream file;
FileStream file;
CLogFormatter formatter;
mutable boost::mutex mx;
};

View File

@ -23,6 +23,7 @@
#include "../CCreatureHandler.h"
#include "../BattleState.h"
#include "../CTownHandler.h"
#include "../mapping/CMap.h"
#include "CGTownInstance.h"
///helpers
@ -220,7 +221,9 @@ CGHeroInstance::CGHeroInstance()
setNodeType(HERO);
ID = Obj::HERO;
tacticFormationEnabled = inTownGarrison = false;
mana = movement = portrait = -1;
mana = UNINITIALIZED_MANA;
movement = UNINITIALIZED_MOVEMENT;
portrait = UNINITIALIZED_PORTRAIT;
isStanding = true;
moveDir = 4;
level = 1;
@ -868,18 +871,18 @@ TExpType CGHeroInstance::calculateXp(TExpType exp) const
ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool) const
{
si16 skill = -1; //skill level
spell->forEachSchool([&, this](const SpellSchoolInfo & cnf, bool & stop)
{
int thisSchool = std::max<int>(getSecSkillLevel(cnf.skill), valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << ((ui8)cnf.id))); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
if(thisSchool > skill)
{
skill = thisSchool;
if(outSelectedSchool)
*outSelectedSchool = (ui8)cnf.id;
}
if(thisSchool > skill)
{
skill = thisSchool;
if(outSelectedSchool)
*outSelectedSchool = (ui8)cnf.id;
}
});
vstd::amax(skill, valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 0)); //any school bonus
vstd::amax(skill, valOfBonuses(Bonus::SPELL, spell->id.toEnum())); //given by artifact or other effect
@ -904,7 +907,7 @@ ui32 CGHeroInstance::getSpellBonus(const CSpell * spell, ui32 base, const CStack
if (affectedStack && affectedStack->getCreature()->level) //Hero specials like Solmyr, Deemer
base *= (100. + ((valOfBonuses(Bonus::SPECIAL_SPELL_LEV, spell->id.toEnum()) * level) / affectedStack->getCreature()->level)) / 100.0;
return base;
return base;
}
int CGHeroInstance::getEffectLevel(const CSpell * spell) const
@ -912,7 +915,7 @@ int CGHeroInstance::getEffectLevel(const CSpell * spell) const
if(hasBonusOfType(Bonus::MAXED_SPELL, spell->id))
return 3;//todo: recheck specialty from where this bonus is. possible bug
else
return getSpellSchoolLevel(spell);
return getSpellSchoolLevel(spell);
}
int CGHeroInstance::getEffectPower(const CSpell * spell) const
@ -922,7 +925,7 @@ int CGHeroInstance::getEffectPower(const CSpell * spell) const
int CGHeroInstance::getEnchantPower(const CSpell * spell) const
{
return getPrimSkillLevel(PrimarySkill::SPELL_POWER) + valOfBonuses(Bonus::SPELL_DURATION);
return getPrimSkillLevel(PrimarySkill::SPELL_POWER) + valOfBonuses(Bonus::SPELL_DURATION);
}
int CGHeroInstance::getEffectValue(const CSpell * spell) const
@ -1107,8 +1110,8 @@ void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
{
// FIXME: Offsets need to be fixed once we get rid of convertPosition
// Check issue 515 for details
offsets =
{
offsets =
{
int3(-1,1,0), int3(-1,-1,0), int3(-2,0,0), int3(0,0,0), int3(0,1,0), int3(-2,1,0), int3(0,-1,0), int3(-2,-1,0)
};
}
@ -1436,20 +1439,43 @@ void CGHeroInstance::levelUpAutomatically()
bool CGHeroInstance::hasVisions(const CGObjectInstance * target, const int subtype) const
{
//VISIONS spell support
const std::string cached = boost::to_string((boost::format("type_%d__subtype_%d") % Bonus::VISIONS % subtype));
const std::string cached = boost::to_string((boost::format("type_%d__subtype_%d") % Bonus::VISIONS % subtype));
const int visionsMultiplier = valOfBonuses(Selector::typeSubtype(Bonus::VISIONS,subtype), cached);
int visionsRange = visionsMultiplier * getPrimSkillLevel(PrimarySkill::SPELL_POWER);
if (visionsMultiplier > 0)
if (visionsMultiplier > 0)
vstd::amax(visionsRange, 3); //minimum range is 3 tiles, but only if VISIONS bonus present
const int distance = target->pos.dist2d(getPosition(false));
//logGlobal->debug(boost::to_string(boost::format("Visions: dist %d, mult %d, range %d") % distance % visionsMultiplier % visionsRange));
return (distance < visionsRange) && (target->pos.z == pos.z);
return (distance < visionsRange) && (target->pos.z == pos.z);
}
bool CGHeroInstance::isMissionCritical() const
{
for(const TriggeredEvent & event : IObjectInterface::cb->getMapHeader()->triggeredEvents)
{
if(event.trigger.test([&](const EventCondition & condition)
{
if (condition.condition == EventCondition::CONTROL && condition.object)
{
auto hero = dynamic_cast<const CGHeroInstance*>(condition.object);
return (hero != this);
}
else if(condition.condition == EventCondition::IS_HUMAN)
{
return true;
}
return false;
}))
{
return true;
}
}
return false;
}

View File

@ -20,6 +20,7 @@
class CHero;
class CGBoat;
class CGTownInstance;
class CMap;
struct TerrainTile;
struct TurnInfo;
@ -64,6 +65,9 @@ public:
ConstTransitivePtr<CCommanderInstance> commander;
const CGBoat *boat; //set to CGBoat when sailing
static const ui32 UNINITIALIZED_PORTRAIT = -1;
static const ui32 UNINITIALIZED_MANA = -1;
static const ui32 UNINITIALIZED_MOVEMENT = -1;
//std::vector<const CArtifact*> artifacts; //hero's artifacts from bag
//std::map<ui16, const CArtifact*> artifWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
@ -124,6 +128,11 @@ public:
}
} skillsInfo;
inline bool isInitialized() const
{ // has this hero been on the map at least once?
return movement != UNINITIALIZED_MOVEMENT && mana != UNINITIALIZED_MANA;
}
//int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
int getSightRadious() const override; //sight distance (should be used if player-owned structure)
//////////////////////////////////////////////////////////////////////////
@ -177,7 +186,7 @@ public:
double getHeroStrength() const; // includes fighting and magic strength
ui64 getTotalStrength() const; // includes fighting strength and army strength
TExpType calculateXp(TExpType exp) const; //apply learning skill
bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses
CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
@ -201,23 +210,25 @@ public:
void Updatespecialty();
void recreateSecondarySkillsBonuses();
void updateSkill(SecondarySkill which, int val);
bool hasVisions(const CGObjectInstance * target, const int subtype) const;
/// If this hero perishes, the scenario is failed
bool isMissionCritical() const;
CGHeroInstance();
virtual ~CGHeroInstance();
///ArtBearer
ArtBearer::ArtBearer bearerType() const override;
///IBonusBearer
CBonusSystemNode *whereShouldBeAttached(CGameState *gs) override;
std::string nodeName() const override;
///ISpellCaster
ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const override;
ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override;
///default spell school level for effect calculation
int getEffectLevel(const CSpell * spell) const override;
@ -229,9 +240,9 @@ public:
///damage/heal override(ignores spell configuration, effect level and effect power)
int getEffectValue(const CSpell * spell) const override;
const PlayerColor getOwner() const override;
void deserializationFix();
void initObj() override;

View File

@ -295,10 +295,10 @@ void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int val, int nega
void CGPandoraBox::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
{
if(result.winner)
return;
giveContentsUpToExp(hero);
if(result.winner == 0)
{
giveContentsUpToExp(hero);
}
}
void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const

View File

@ -299,6 +299,16 @@ void CObjectClassesHandler::afterLoadFinalization()
logGlobal->warnStream() << "No templates found for " << entry.first << ":" << obj.first;
}
}
//duplicate existing two-way portals to make reserve for RMG
auto& portalVec = objects[Obj::MONOLITH_TWO_WAY]->objects;
size_t portalCount = portalVec.size();
size_t currentIndex = portalCount;
while (portalVec.size() < 100)
{
portalVec[currentIndex] = portalVec[currentIndex % portalCount];
currentIndex++;
}
}
std::string CObjectClassesHandler::getObjectName(si32 type) const

View File

@ -169,7 +169,7 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
{
for(int h=0; h<getHeight(); ++h)
{
if (appearance.isBlockedAt(w, h))
if(appearance.isBlockedAt(w, h))
ret.insert(int3(pos.x - w, pos.y - h, pos.z));
}
}
@ -191,7 +191,13 @@ void CGObjectInstance::setType(si32 ID, si32 subID)
//recalculate blockvis tiles - new appearance might have different blockmap than before
cb->gameState()->map->removeBlockVisTiles(this, true);
auto handler = VLC->objtypeh->getHandlerFor(ID, subID);
if (!handler->getTemplates(tile.terType).empty())
if(!handler)
{
logGlobal->errorStream() << boost::format(
"Unknown object type %d:%d at %s") % ID % subID % visitablePos();
return;
}
if(!handler->getTemplates(tile.terType).empty())
appearance = handler->getTemplates(tile.terType)[0];
else
appearance = handler->getTemplates()[0]; // get at least some appearance since alternative is crash
@ -328,9 +334,9 @@ int3 IBoatGenerator::bestLocation() const
for (auto & offset : offsets)
{
if (const TerrainTile *tile = IObjectInterface::cb->getTile(o->pos + offset, false)) //tile is in the map
if(const TerrainTile *tile = IObjectInterface::cb->getTile(o->pos + offset, false)) //tile is in the map
{
if (tile->terType == ETerrainType::WATER && (!tile->blocked || tile->blockingObjects.front()->ID == Obj::BOAT)) //and is water and is not blocked or is blocked by boat
if(tile->terType == ETerrainType::WATER && (!tile->blocked || tile->blockingObjects.front()->ID == Obj::BOAT)) //and is water and is not blocked or is blocked by boat
return o->pos + offset;
}
}

View File

@ -70,7 +70,7 @@ bool CQuest::checkQuest (const CGHeroInstance * h) const
case MISSION_ART:
for (auto & elem : m5arts)
{
if (h->hasArt(elem))
if (h->hasArt(elem, false, true))
continue;
return false; //if the artifact was not found
}
@ -416,6 +416,7 @@ void CGSeerHut::init()
{
seerName = *RandomGeneratorUtil::nextItem(VLC->generaltexth->seerNames, cb->gameState()->getRandomGenerator());
quest->textOption = cb->gameState()->getRandomGenerator().nextInt(2);
quest->completedOption = cb->gameState()->getRandomGenerator().nextInt(1, 3);
}
void CGSeerHut::initObj()
@ -435,7 +436,7 @@ void CGSeerHut::initObj()
else
{
quest->progress = CQuest::COMPLETE;
quest->firstVisitText = VLC->generaltexth->seerEmpty[quest->textOption];
quest->firstVisitText = VLC->generaltexth->seerEmpty[quest->completedOption];
}
}
@ -533,7 +534,7 @@ void CGSeerHut::newTurn() const
{
if (quest->lastDay >= 0 && quest->lastDay < cb->getDate()-1) //time is up
{
cb->setObjProperty (id, 10, CQuest::COMPLETE);
cb->setObjProperty (id, CGSeerHut::OBJPROP_VISITED, CQuest::COMPLETE);
}
}
@ -550,7 +551,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
if (firstVisit)
{
isCustom = quest->isCustomFirst;
cb->setObjProperty (id, 10, CQuest::IN_PROGRESS);
cb->setObjProperty (id, CGSeerHut::OBJPROP_VISITED, CQuest::IN_PROGRESS);
AddQuest aq;
aq.quest = QuestInfo (quest, this, visitablePos());
@ -582,7 +583,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
}
else
{
iw.text << VLC->generaltexth->seerEmpty[quest->textOption];
iw.text << VLC->generaltexth->seerEmpty[quest->completedOption];
if (ID == Obj::SEER_HUT)
iw.text.addReplacement(seerName);
cb->showInfoDialog(&iw);
@ -630,6 +631,18 @@ void CGSeerHut::finishQuest(const CGHeroInstance * h, ui32 accept) const
case CQuest::MISSION_ART:
for (auto & elem : quest->m5arts)
{
if(!h->hasArt(elem))
{
// first we need to disassemble this backpack artifact
auto assembly = h->getAssemblyByConstituent(elem);
assert(assembly);
for(auto & ci : assembly->constituentsInfo)
{
cb->giveHeroNewArtifact(h, ci.art->artType, ArtifactPosition::PRE_FIRST);
}
// remove the assembly
cb->removeArtifact(ArtifactLocation(h, h->getArtPos(assembly)));
}
cb->removeArtifact(ArtifactLocation(h, h->getArtPos(elem, false)));
}
break;
@ -645,7 +658,7 @@ void CGSeerHut::finishQuest(const CGHeroInstance * h, ui32 accept) const
default:
break;
}
cb->setObjProperty (id, 10, CQuest::COMPLETE); //mission complete
cb->setObjProperty (id, CGSeerHut::OBJPROP_VISITED, CQuest::COMPLETE); //mission complete
completeQuest(h); //make sure to remove QuestGuard at the very end
}
}
@ -733,6 +746,7 @@ void CGQuestGuard::init()
{
blockVisit = true;
quest->textOption = cb->gameState()->getRandomGenerator().nextInt(3, 5);
quest->completedOption = cb->gameState()->getRandomGenerator().nextInt(4, 5);
}
void CGQuestGuard::completeQuest(const CGHeroInstance *h) const

View File

@ -37,8 +37,11 @@ public:
std::vector<CStackBasicDescriptor> m6creatures; //pair[cre id, cre count], CreatureSet info irrelevant
std::vector<ui32> m7resources; //TODO: use resourceset?
//following field are used only for kill creature/hero missions, the original objects became inaccessible after their removal, so we need to store info needed for messages / hover text
// following fields are used only for kill creature/hero missions, the original
// objects became inaccessible after their removal, so we need to store info
// needed for messages / hover text
ui8 textOption;
ui8 completedOption;
CStackBasicDescriptor stackToKill;
ui8 stackDirection;
std::string heroName; //backup of hero name
@ -66,7 +69,16 @@ public:
{
h & qid & missionType & progress & lastDay & m13489val & m2stats & m5arts & m6creatures & m7resources
& textOption & stackToKill & stackDirection & heroName & heroPortrait
& firstVisitText & nextVisitText & completedText & isCustomFirst & isCustomNext & isCustomComplete;
& firstVisitText & nextVisitText & completedText & isCustomFirst
& isCustomNext & isCustomComplete;
if(version >= 757)
{
h & completedOption;
}
else if(!h.saving)
{
completedOption = 1;
}
}
};
@ -118,6 +130,8 @@ public:
h & rewardType & rID & rVal & seerName;
}
protected:
static constexpr int OBJPROP_VISITED = 10;
void setPropertyDer(ui8 what, ui32 val) override;
};

View File

@ -496,6 +496,7 @@ void CGPickable::initObj()
info.resize(1);
info[0].message.addTxt(MetaString::ADVOB_TXT, 51);
info[0].reward.removeObject = true;
break;
case 1:
{
info.resize(1);
@ -682,7 +683,6 @@ void CGBonusingObject::initObj()
info[i].message.addTxt(MetaString::ADVOB_TXT, 62);
soundID = soundBase::experience;
}
onVisited.addTxt(MetaString::ADVOB_TXT, 63);
info.back().limiter.dayOfWeek = 7;
configureBonus(info.back(), Bonus::MORALE, 1, 68); // on last day of week
configureBonus(info.back(), Bonus::LUCK, 1, 68);

View File

@ -133,10 +133,10 @@ namespace JsonRandom
if (value["type"].getType() == JsonNode::DATA_STRING)
return SpellID(VLC->modh->identifiers.getIdentifier("spell", value["type"]).get());
spells.erase(std::remove_if(spells.begin(), spells.end(), [=](SpellID spell)
vstd::erase_if(spells, [=](SpellID spell)
{
return VLC->spellh->objects[spell]->level != si32(value["level"].Float());
}), spells.end());
});
return SpellID(*RandomGeneratorUtil::nextItem(spells, rng));
}

View File

@ -24,7 +24,7 @@
#include "../CPlayerState.h"
std::map <si32, std::vector<ObjectInstanceID> > CGMagi::eyelist;
ui8 CGObelisk::obeliskCount; //how many obelisks are on map
ui8 CGObelisk::obeliskCount = 0; //how many obelisks are on map
std::map<TeamID, ui8> CGObelisk::visited; //map: team_id => how many obelisks has been visited
///helpers
@ -60,7 +60,7 @@ static std::string & visitedTxt(const bool visited)
void CPlayersVisited::setPropertyDer( ui8 what, ui32 val )
{
if(what == 10)
if(what == CPlayersVisited::OBJPROP_VISITED)
players.insert(PlayerColor(val));
}
@ -103,16 +103,16 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
{
std::string hoverName;
if(hero->hasVisions(this, 0))
{
{
MetaString ms;
ms << stacks.begin()->second->count;
ms << " " ;
ms.addTxt(MetaString::CRE_PL_NAMES,subID);
ms << "\n";
int decision = takenAction(hero, true);
switch (decision)
{
case FIGHT:
@ -123,19 +123,19 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
break;
case JOIN_FOR_FREE:
ms.addTxt(MetaString::GENERAL_TXT,243);
break;
break;
default: //decision = cost in gold
VLC->generaltexth->allTexts[244];
ms << boost::to_string(boost::format(VLC->generaltexth->allTexts[244]) % decision);
ms << boost::to_string(boost::format(VLC->generaltexth->allTexts[244]) % decision);
break;
}
}
ms.toString(hoverName);
ms.toString(hoverName);
}
else
{
hoverName = getHoverText(hero->tempOwner);
}
hoverName = getHoverText(hero->tempOwner);
}
const JsonNode & texts = VLC->generaltexth->localizedTexts["adventureMap"]["monsterThreat"];
@ -392,6 +392,7 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co
if(cost)
cb->giveResource(h->tempOwner,Res::GOLD,-cost);
giveReward(h);
cb->tryJoiningArmy(this, h, true, true);
}
}
@ -452,9 +453,14 @@ void CGCreature::flee( const CGHeroInstance * h ) const
void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
{
if(result.winner==0)
if(result.winner == 0)
{
giveReward(hero);
cb->removeObject(this);
}
else if(result.winner > 1) // draw
{
// guarded reward is lost forever on draw
cb->removeObject(this);
}
else
@ -462,11 +468,11 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
//merge stacks into one
TSlots::const_iterator i;
CCreature * cre = VLC->creh->creatures[formation.basicType];
for (i = stacks.begin(); i != stacks.end(); i++)
for(i = stacks.begin(); i != stacks.end(); i++)
{
if (cre->isMyUpgrade(i->second->type))
if(cre->isMyUpgrade(i->second->type))
{
cb->changeStackType (StackLocation(this, i->first), cre); //un-upgrade creatures
cb->changeStackType(StackLocation(this, i->first), cre); //un-upgrade creatures
}
}
@ -474,16 +480,16 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
if(!hasStackAtSlot(SlotID(0)))
cb->moveStack(StackLocation(this, stacks.begin()->first), StackLocation(this, SlotID(0)), stacks.begin()->second->count);
while (stacks.size() > 1) //hopefully that's enough
while(stacks.size() > 1) //hopefully that's enough
{
// TODO it's either overcomplicated (if we assume there'll be only one stack) or buggy (if we allow multiple stacks... but that'll also cause troubles elsewhere)
i = stacks.end();
i--;
SlotID slot = getSlotFor(i->second->type);
if (slot == i->first) //no reason to move stack to its own slot
if(slot == i->first) //no reason to move stack to its own slot
break;
else
cb->moveStack (StackLocation(this, i->first), StackLocation(this, slot), i->second->count);
cb->moveStack(StackLocation(this, i->first), StackLocation(this, slot), i->second->count);
}
cb->setObjProperty(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties
@ -557,6 +563,35 @@ int CGCreature::getNumberOfStacks(const CGHeroInstance *hero) const
return split;
}
void CGCreature::giveReward(const CGHeroInstance * h) const
{
InfoWindow iw;
iw.player = h->tempOwner;
if(resources.size())
{
cb->giveResources(h->tempOwner, resources);
for(int i = 0; i < resources.size(); i++)
{
if(resources[i] > 0)
iw.components.push_back(Component(Component::RESOURCE, i, resources[i], 0));
}
}
if(gainedArtifact != ArtifactID::NONE)
{
cb->giveHeroNewArtifact(h, VLC->arth->artifacts[gainedArtifact], ArtifactPosition::FIRST_AVAILABLE);
iw.components.push_back(Component(Component::ARTIFACT, gainedArtifact, 0, 0));
}
if(iw.components.size())
{
iw.text.addTxt(MetaString::ADVOB_TXT, 183); // % has found treasure
iw.text.addReplacement(h->name);
cb->showInfoDialog(&iw);
}
}
void CGMine::onHeroVisit( const CGHeroInstance * h ) const
{
int relations = cb->gameState()->getPlayerRelations(h->tempOwner, tempOwner);
@ -1279,7 +1314,7 @@ void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const
iw.soundID = soundBase::gazebo;
iw.player = h->getOwner();
if(!wasVisited(h->tempOwner))
cb->setObjProperty(id, 10, h->tempOwner.getNum());
cb->setObjProperty(id, CGWitchHut::OBJPROP_VISITED, h->tempOwner.getNum());
ui32 txt_id;
if(h->getSecSkillLevel(SecondarySkill(ability))) //you already know this skill
{
@ -1389,7 +1424,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
}
if(!wasVisited(h->tempOwner))
cb->setObjProperty(id, 10, h->tempOwner.getNum());
cb->setObjProperty(id, CGShrine::OBJPROP_VISITED, h->tempOwner.getNum());
InfoWindow iw;
iw.soundID = soundBase::temple;
@ -1636,6 +1671,7 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
cb->sendAndApply(&cv);
}
cv.pos = h->getPosition(false);
cv.focusTime = 0;
cb->sendAndApply(&cv);
}
}
@ -1714,7 +1750,7 @@ void CGShipyard::getOutOffsets( std::vector<int3> &offsets ) const
int3(-3,0,0), int3(1,0,0), //AB
int3(-3,1,0), int3(1,1,0), int3(-2,1,0), int3(0,1,0), int3(-1,1,0), //CDEFG
int3(-3,-1,0), int3(1,-1,0), int3(-2,-1,0), int3(0,-1,0), int3(-1,-1,0) //HIJKL
};
};
}
void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const
@ -1790,7 +1826,7 @@ void CCartographer::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answ
//water = 0; land = 1; underground = 2;
cb->getAllTiles (fw.tiles, hero->tempOwner, subID - 1, !subID + 1); //reveal appropriate tiles
cb->sendAndApply (&fw);
cb->setObjProperty (id, 10, hero->tempOwner.getNum());
cb->setObjProperty (id, CCartographer::OBJPROP_VISITED, hero->tempOwner.getNum());
}
}
@ -1812,11 +1848,16 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
iw.text.addTxt(MetaString::ADVOB_TXT, 96);
cb->sendAndApply(&iw);
cb->setObjProperty(id, 20, h->tempOwner.getNum()); //increment general visited obelisks counter
// increment general visited obelisks counter
cb->setObjProperty(id, CGObelisk::OBJPROP_INC, team.getNum());
openWindow(OpenWindow::PUZZLE_MAP, h->tempOwner.getNum());
cb->setObjProperty(id, 10, h->tempOwner.getNum()); //mark that particular obelisk as visited
// mark that particular obelisk as visited for all players in the team
for (auto & color : ts->players)
{
cb->setObjProperty(id, CGObelisk::OBJPROP_VISITED, color.getNum());
}
}
else
{
@ -1831,6 +1872,12 @@ void CGObelisk::initObj()
obeliskCount++;
}
void CGObelisk::reset()
{
obeliskCount = 0;
visited.clear();
}
std::string CGObelisk::getHoverText(PlayerColor player) const
{
return getObjectName() + " " + visitedTxt(wasVisited(player));
@ -1838,20 +1885,26 @@ std::string CGObelisk::getHoverText(PlayerColor player) const
void CGObelisk::setPropertyDer( ui8 what, ui32 val )
{
CPlayersVisited::setPropertyDer(what, val);
switch(what)
{
case 20:
assert(val < PlayerColor::PLAYER_LIMIT_I);
visited[TeamID(val)]++;
case CGObelisk::OBJPROP_INC:
{
assert(val < PlayerColor::PLAYER_LIMIT_I);
auto progress = ++visited[TeamID(val)];
logGlobal->debugStream() << boost::format("Player %d: obelisk progress %d / %d")
% val % static_cast<int>(progress) % static_cast<int>(obeliskCount);
if(visited[TeamID(val)] > obeliskCount)
{
logGlobal->errorStream() << "Error: Visited " << visited[TeamID(val)] << "\t\t" << obeliskCount;
assert(0);
}
if(progress > obeliskCount)
{
logGlobal->errorStream() << "Error: Visited " << progress << "\t\t" << obeliskCount;
assert(0);
}
break;
break;
}
default:
CPlayersVisited::setPropertyDer(what, val);
break;
}
}

View File

@ -28,6 +28,8 @@ public:
h & static_cast<CGObjectInstance&>(*this);
h & players;
}
static constexpr int OBJPROP_VISITED = 10;
};
class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map
@ -90,6 +92,7 @@ private:
void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const;
int takenAction(const CGHeroInstance *h, bool allowJoin=true) const; //action on confrontation: -2 - fight, -1 - flee, >=0 - will join for given value of gold (may be 0)
void giveReward(const CGHeroInstance * h) const;
};
@ -450,12 +453,14 @@ class DLL_LINKAGE CGDenOfthieves : public CGObjectInstance
class DLL_LINKAGE CGObelisk : public CPlayersVisited
{
public:
static constexpr int OBJPROP_INC = 20;
static ui8 obeliskCount; //how many obelisks are on map
static std::map<TeamID, ui8> visited; //map: team_id => how many obelisks has been visited
void onHeroVisit(const CGHeroInstance * h) const override;
void initObj() override;
std::string getHoverText(PlayerColor player) const override;
static void reset();
template <typename Handler> void serialize(Handler &h, const int version)
{

View File

@ -20,6 +20,8 @@ void CMapGenerator::foreach_neighbour(const int3 &pos, std::function<void(int3&
for(const int3 &dir : int3::getDirs())
{
int3 n = pos + dir;
/*important notice: perform any translation before this function is called,
so the actual map position is checked*/
if(map->isInTheMap(n))
foo(n);
}

View File

@ -1177,25 +1177,23 @@ bool CRmgTemplateZone::createTreasurePile(CMapGenerator* gen, int3 &pos, float m
int3 closestTile = int3(-1,-1,-1);
float minDistance = 1e10;
for (auto visitablePos : info.visitableFromBottomPositions) //objects that are not visitable from top must be accessible from bottom or side
{
int3 closestFreeTile = findClosestTile(freePaths, visitablePos);
if (closestFreeTile.dist2d(visitablePos) < minDistance)
{
closestTile = visitablePos + int3 (0, 1, 0); //start below object (y+1), possibly even outside the map (?)
closestTile = visitablePos + int3 (0, 1, 0); //start below object (y+1), possibly even outside the map, to not make path up through it
minDistance = closestFreeTile.dist2d(visitablePos);
}
}
if (!closestTile.valid())
for (auto visitablePos : info.visitableFromTopPositions) //all objects are accessible from any direction
{
for (auto visitablePos : info.visitableFromTopPositions) //all objects are accessible from any direction
int3 closestFreeTile = findClosestTile(freePaths, visitablePos);
if (closestFreeTile.dist2d(visitablePos) < minDistance)
{
int3 closestFreeTile = findClosestTile(freePaths, visitablePos);
if (closestFreeTile.dist2d(visitablePos) < minDistance)
{
closestTile = visitablePos;
minDistance = closestFreeTile.dist2d(visitablePos);
}
closestTile = visitablePos;
minDistance = closestFreeTile.dist2d(visitablePos);
}
}
assert (closestTile.valid());
@ -1246,6 +1244,8 @@ bool CRmgTemplateZone::createTreasurePile(CMapGenerator* gen, int3 &pos, float m
for (auto treasure : treasures)
{
int3 visitableOffset = treasure.second->getVisitableOffset();
if (treasure.second->ID == Obj::SEER_HUT) //FIXME: find generic solution or figure out why Seer Hut doesn't behave correctly
visitableOffset.x += 1;
placeObject(gen, treasure.second, treasure.first + visitableOffset);
}
if (addMonster(gen, guardPos, currentValue, false))
@ -2668,8 +2668,7 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
//seer huts with creatures or generic rewards
//if (questArtZone) //we won't be placing seer huts if there is no zone left to place arties
if (false) //FIXME: Seer Huts are bugged
if (questArtZone) //we won't be placing seer huts if there is no zone left to place arties
{
static const int genericSeerHuts = 8;
int seerHutsPerType = 0;
@ -2726,6 +2725,9 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
obj->quest->missionType = CQuest::MISSION_ART;
ArtifactID artid = *RandomGeneratorUtil::nextItem(gen->getQuestArtsRemaning(), gen->rand);
obj->quest->m5arts.push_back(artid);
obj->quest->lastDay = -1;
obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
gen->banQuestArt(artid);
gen->map->addQuest(obj);
@ -2762,6 +2764,9 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
obj->quest->missionType = CQuest::MISSION_ART;
ArtifactID artid = *RandomGeneratorUtil::nextItem(gen->getQuestArtsRemaning(), gen->rand);
obj->quest->m5arts.push_back(artid);
obj->quest->lastDay = -1;
obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
gen->banQuestArt(artid);
gen->map->addQuest(obj);
@ -2784,6 +2789,9 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
obj->quest->missionType = CQuest::MISSION_ART;
ArtifactID artid = *RandomGeneratorUtil::nextItem(gen->getQuestArtsRemaning(), gen->rand);
obj->quest->m5arts.push_back(artid);
obj->quest->lastDay = -1;
obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
gen->banQuestArt(artid);
gen->map->addQuest(obj);

View File

@ -124,6 +124,11 @@ static inline double distance(int3 a, int3 b)
}
static void giveExp(BattleResult &r)
{
if(r.winner > 1)
{
// draw
return;
}
r.exp[0] = 0;
r.exp[1] = 0;
for(auto i = r.casualties[!r.winner].begin(); i!=r.casualties[!r.winner].end(); i++)
@ -471,7 +476,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
const CArmedInstance *bEndArmy2 = gs->curB->sides.at(1).armyObject;
const BattleResult::EResult result = battleResult.get()->result;
auto findBattleQuery = [this] () -> std::shared_ptr<CBattleQuery>
auto findBattleQuery = [this]() -> std::shared_ptr<CBattleQuery>
{
for(auto &q : queries.allQueries())
{
@ -526,66 +531,70 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
}
std::vector<ui32> arts; //display them in window
std::vector<const CArtifactInstance *> arts; //display them in window
if (result == BattleResult::NORMAL && finishingBattle->winnerHero)
if(result == BattleResult::NORMAL && finishingBattle->winnerHero)
{
if (finishingBattle->loserHero)
auto sendMoveArtifact = [&](const CArtifactInstance *art, MoveArtifact *ma)
{
auto artifactsWorn = finishingBattle->loserHero->artifactsWorn; //TODO: wrap it into a function, somehow (boost::variant -_-)
arts.push_back(art);
ma->dst = ArtifactLocation(finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero));
sendAndApply(ma);
};
if(finishingBattle->loserHero)
{
//TODO: wrap it into a function, somehow (boost::variant -_-)
auto artifactsWorn = finishingBattle->loserHero->artifactsWorn;
for (auto artSlot : artifactsWorn)
{
MoveArtifact ma;
ma.src = ArtifactLocation (finishingBattle->loserHero, artSlot.first);
ma.src = ArtifactLocation(finishingBattle->loserHero, artSlot.first);
const CArtifactInstance * art = ma.src.getArt();
if (art && !art->artType->isBig() && art->artType->id != ArtifactID::SPELLBOOK) // don't move war machines or locked arts (spellbook)
if(art && !art->artType->isBig() &&
art->artType->id != ArtifactID::SPELLBOOK)
// don't move war machines or locked arts (spellbook)
{
arts.push_back (art->artType->id);
ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero));
sendAndApply(&ma);
sendMoveArtifact(art, &ma);
}
}
while (!finishingBattle->loserHero->artifactsInBackpack.empty())
while(!finishingBattle->loserHero->artifactsInBackpack.empty())
{
//we assume that no big artifacts can be found
MoveArtifact ma;
ma.src = ArtifactLocation (finishingBattle->loserHero,
ma.src = ArtifactLocation(finishingBattle->loserHero,
ArtifactPosition(GameConstants::BACKPACK_START)); //backpack automatically shifts arts to beginning
const CArtifactInstance * art = ma.src.getArt();
arts.push_back (art->artType->id);
ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero));
sendAndApply(&ma);
if(art->artType->id != ArtifactID::GRAIL) //grail may not be won
{
sendMoveArtifact(art, &ma);
}
}
if (finishingBattle->loserHero->commander) //TODO: what if commanders belong to no hero?
if(finishingBattle->loserHero->commander) //TODO: what if commanders belong to no hero?
{
artifactsWorn = finishingBattle->loserHero->commander->artifactsWorn;
for (auto artSlot : artifactsWorn)
for(auto artSlot : artifactsWorn)
{
MoveArtifact ma;
ma.src = ArtifactLocation (finishingBattle->loserHero->commander.get(), artSlot.first);
ma.src = ArtifactLocation(finishingBattle->loserHero->commander.get(), artSlot.first);
const CArtifactInstance * art = ma.src.getArt();
if (art && !art->artType->isBig())
{
arts.push_back (art->artType->id);
ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero));
sendAndApply(&ma);
sendMoveArtifact(art, &ma);
}
}
}
}
for (auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks)
for(auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks)
{
auto artifactsWorn = armySlot.second->artifactsWorn;
for (auto artSlot : artifactsWorn)
{
MoveArtifact ma;
ma.src = ArtifactLocation (armySlot.second, artSlot.first);
ma.src = ArtifactLocation(armySlot.second, artSlot.first);
const CArtifactInstance * art = ma.src.getArt();
if (art && !art->artType->isBig())
{
arts.push_back (art->artType->id);
ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero));
sendAndApply(&ma);
sendMoveArtifact(art, &ma);
}
}
}
@ -593,23 +602,25 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
sendAndApply(battleResult.data); //after this point casualties objects are destroyed
if (arts.size()) //display loot
if(arts.size()) //display loot
{
InfoWindow iw;
iw.player = finishingBattle->winnerHero->tempOwner;
iw.text.addTxt (MetaString::GENERAL_TXT, 30); //You have captured enemy artifact
for (auto id : arts) //TODO; separate function to display loot for various ojects?
for(auto art : arts) //TODO; separate function to display loot for various ojects?
{
iw.components.push_back (Component (Component::ARTIFACT, id, 0, 0));
iw.components.push_back(Component(
Component::ARTIFACT, art->artType->id,
art->artType->id == ArtifactID::SPELL_SCROLL? art->getGivenSpellID() : 0, 0));
if(iw.components.size() >= 14)
{
sendAndApply(&iw);
iw.components.clear();
}
}
if (iw.components.size())
if(iw.components.size())
{
sendAndApply(&iw);
}
@ -712,7 +723,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result )
setBattle(nullptr);
if(visitObjectAfterVictory && result.winner==0)
if(visitObjectAfterVictory && result.winner==0 && !finishingBattle->winnerHero->stacks.empty())
{
logGlobal->traceStream() << "post-victory visit";
visitObjectOnTile(*getTile(finishingBattle->winnerHero->getPosition()), finishingBattle->winnerHero);
@ -734,13 +745,34 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result )
sah.army[0].setCreature(SlotID(0), finishingBattle->loserHero->type->initialArmy.at(0).creature, 1);
}
if(const CGHeroInstance *another = getPlayer(finishingBattle->loser)->availableHeroes.at(1))
if(const CGHeroInstance *another = getPlayer(finishingBattle->loser)->availableHeroes.at(0))
sah.hid[1] = another->subID;
else
sah.hid[1] = -1;
sendAndApply(&sah);
}
if(result.winner != 2 && finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty())
{
RemoveObject ro(finishingBattle->winnerHero->id);
sendAndApply(&ro);
if (VLC->modh->settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS)
{
SetAvailableHeroes sah;
sah.player = finishingBattle->victor;
sah.hid[0] = finishingBattle->winnerHero->subID;
sah.army[0].clear();
sah.army[0].setCreature(SlotID(0), finishingBattle->winnerHero->type->initialArmy.at(0).creature, 1);
if(const CGHeroInstance *another = getPlayer(finishingBattle->victor)->availableHeroes.at(0))
sah.hid[1] = another->subID;
else
sah.hid[1] = -1;
sendAndApply(&sah);
}
}
}
void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance, int targetHex)
@ -926,13 +958,16 @@ void CGameHandler::handleConnection(std::set<PlayerColor> players, CConnection &
{
const bool result = apply->applyOnGH(this,&c,pack, player);
if(!result)
complain("Got false in applying... that request must have been fishy!");
logGlobal->traceStream() << "Message successfully applied (result=" << result << ")!";
{
complain((boost::format("Got false in applying %s... that request must have been fishy!")
% typeid(*pack).name()).str());
}
logGlobal->traceStream() << "Message successfully applied (result=" << result << ")!";
sendPackageResponse(true);
}
else
{
logGlobal->errorStream() << "Message cannot be applied, cannot find applier (unregistered type)!";
logGlobal->errorStream() << "Message cannot be applied, cannot find applier (unregistered type)!";
sendPackageResponse(false);
}
@ -2113,7 +2148,6 @@ void CGameHandler::stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroI
void CGameHandler::removeArtifact(const ArtifactLocation &al)
{
assert(al.getArt());
EraseArtifact ea;
ea.al = al;
sendAndApply(&ea);
@ -3038,7 +3072,7 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition
sendAndApply(&da);
}
return false;
return true;
}
bool CGameHandler::buyArtifact( ObjectInstanceID hid, ArtifactID aid )
@ -3320,23 +3354,27 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl
// || (getHeroCount(player, false) >= GameConstants::MAX_HEROES_PER_PLAYER && complain("Cannot hire hero, only 8 wandering heroes are allowed!")))
if((p->resources.at(Res::GOLD) < GameConstants::HERO_GOLD_COST && complain("Not enough gold for buying hero!"))
|| ((!t) && (getHeroCount(player, false) >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER && complain("Cannot hire hero, too many wandering heroes already!")))
|| ((t) && (getHeroCount(player, true) >= VLC->modh->settings.MAX_HEROES_AVAILABLE_PER_PLAYER && complain("Cannot hire hero, too many heroes garrizoned and wandering already!"))) )
return false;
|| ((t) && (getHeroCount(player, true) >= VLC->modh->settings.MAX_HEROES_AVAILABLE_PER_PLAYER && complain("Cannot hire hero, too many heroes garrizoned and wandering already!"))) )
{
return false;
}
if(t) //tavern in town
{
if( (!t->hasBuilt(BuildingID::TAVERN) && complain("No tavern!"))
|| (t->visitingHero && complain("There is visiting hero - no place!")))
if((!t->hasBuilt(BuildingID::TAVERN) && complain("No tavern!"))
|| (t->visitingHero && complain("There is visiting hero - no place!")))
{
return false;
}
}
else if(obj->ID == Obj::TAVERN)
{
if(getTile(obj->visitablePos())->visitableObjects.back() != obj && complain("Tavern entry must be unoccupied!"))
if(getTile(obj->visitablePos())->visitableObjects.back() != obj && complain("Tavern entry must be unoccupied!"))
{
return false;
}
}
const CGHeroInstance *nh = p->availableHeroes.at(hid);
if (!nh)
{
@ -3351,13 +3389,14 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl
hr.tile = obj->visitablePos() + nh->getVisitableOffset();
sendAndApply(&hr);
std::map<ui32, ConstTransitivePtr<CGHeroInstance> > pool = gs->unusedHeroesFromPool();
const CGHeroInstance *theOtherHero = p->availableHeroes.at(!hid);
const CGHeroInstance *newHero = nullptr;
if (theOtherHero) //on XXL maps all heroes can be imprisoned :(
{
newHero = gs->hpool.pickHeroFor(false, player, getNativeTown(player), pool, gs->getRandomGenerator(), theOtherHero->type->heroClass);
}
SetAvailableHeroes sah;
sah.player = player;
@ -3369,7 +3408,9 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl
sah.army[hid].setCreature(SlotID(0), newHero->type->initialArmy[0].creature, 1);
}
else
{
sah.hid[hid] = -1;
}
sah.hid[!hid] = theOtherHero ? theOtherHero->subID : -1;
sendAndApply(&sah);
@ -4026,6 +4067,16 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
if(!hero->hasStackAtSlot(SlotID(i)))
insertNewStack(StackLocation(hero, SlotID(i)), blackKnight, 10);
}
else if(message == "vcmiglaurung") //gives 5000 crystal dragons into each slot
{
CGHeroInstance *hero = gs->getHero(currObj);
const CCreature *crystalDragon = VLC->creh->creatures.at(133);
if(!hero) return;
for(int i = 0; i < GameConstants::ARMY_SIZE; i++)
if(!hero->hasStackAtSlot(SlotID(i)))
insertNewStack(StackLocation(hero, SlotID(i)), crystalDragon, 5000);
}
else if(message == "vcminoldor") //all war machines
{
CGHeroInstance *hero = gs->getHero(currObj);
@ -5651,16 +5702,18 @@ void CGameHandler::giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact
void CGameHandler::setBattleResult(BattleResult::EResult resultType, int victoriusSide)
{
if(battleResult.get())
boost::unique_lock<boost::mutex> guard(battleResult.mx);
if(battleResult.data)
{
complain("There is already set result?");
complain((boost::format("The battle result has been already set (to %d, asked to %d)")
% battleResult.data->result % resultType).str());
return;
}
auto br = new BattleResult;
br->result = resultType;
br->winner = victoriusSide; //surrendering side loses
gs->curB->calculateCasualties(br->casualties);
battleResult.set(br);
battleResult.data = br;
}
void CGameHandler::commitPackage( CPackForClient *pack )

View File

@ -321,7 +321,8 @@ CBlockingDialogQuery::CBlockingDialogQuery(const BlockingDialog &bd)
void CTeleportDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
{
auto obj = dynamic_ptr_cast<const CGTeleport>(objectVisit.visitedObject);
// do not change to dynamic_ptr_cast - SIGSEGV!
auto obj = dynamic_cast<const CGTeleport*>(objectVisit.visitedObject);
obj->teleportDialogAnswered(objectVisit.visitingHero, *answer, td.exits);
}

View File

@ -3,13 +3,14 @@
<FileVersion major="1" minor="6" />
<Project>
<Option title="VCMI_server" />
<Option pch_mode="2" />
<Option pch_mode="0" />
<Option compiler="gcc" />
<Build>
<Target title="Debug-win32">
<Option platforms="Windows;" />
<Option output="../VCMI_server" prefix_auto="1" extension_auto="1" />
<Option object_output="../obj/Debug/Server" />
<Option working_dir="../" />
<Option object_output="../obj/Server/Debug/x86" />
<Option type="1" />
<Option compiler="gcc" />
<Option use_console_runner="0" />
@ -25,7 +26,8 @@
<Target title="Release-win32">
<Option platforms="Windows;" />
<Option output="../VCMI_server" prefix_auto="1" extension_auto="1" />
<Option object_output="../obj/Release/Server" />
<Option working_dir="../" />
<Option object_output="../obj/Server/Release/x86" />
<Option type="1" />
<Option compiler="gcc" />
<Option use_console_runner="0" />
@ -42,7 +44,8 @@
<Target title="Debug-win64">
<Option platforms="Windows;" />
<Option output="../VCMI_server" prefix_auto="1" extension_auto="1" />
<Option object_output="../obj/Debug/Server" />
<Option working_dir="../" />
<Option object_output="../obj/Server/Debug/x86" />
<Option type="1" />
<Option compiler="gnu_gcc_compiler_x64" />
<Option use_console_runner="0" />
@ -59,6 +62,7 @@
<Compiler>
<Add option="-Wextra" />
<Add option="-Wall" />
<Add option="-std=gnu++11" />
<Add option="-fexceptions" />
<Add option="-Wpointer-arith" />
<Add option="-Wno-switch" />
@ -67,6 +71,8 @@
<Add option="-Wno-overloaded-virtual" />
<Add option="-D_WIN32_WINNT=0x0501" />
<Add option="-DBOOST_THREAD_USE_LIB" />
<Add option="-DBOOST_SYSTEM_NO_DEPRECATED" />
<Add option="-D_WIN32" />
<Add directory="$(#boost.include)" />
<Add directory="../include" />
<Add directory="$(#sdl2.include)" />