mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Merge branch 'develop' of https://github.com/vcmi/vcmi into develop
This commit is contained in:
commit
e901fcd293
12
.travis.yml
12
.travis.yml
@ -36,9 +36,6 @@ matrix:
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: VCMI_PLATFORM='linux' REAL_CC=clang-3.6 REAL_CXX=clang++-3.6 PACKAGE=clang-3.6 SUPPORT=libstdc++-4.8-dev VCMI_CMAKE_FLAGS='-DENABLE_TEST=0'
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: VCMI_PLATFORM='linux' REAL_CC=clang-3.5 REAL_CXX=clang++-3.5 PACKAGE=clang-3.5 SUPPORT=libstdc++-4.8-dev VCMI_CMAKE_FLAGS='-DENABLE_TEST=0'
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: VCMI_PLATFORM='linux' REAL_CC=clang-3.4 REAL_CXX=clang++-3.4 PACKAGE=clang-3.4 SUPPORT=libstdc++-4.8-dev VCMI_CMAKE_FLAGS='-DENABLE_TEST=0'
|
||||
@ -57,14 +54,15 @@ addons:
|
||||
name: vcmi/vcmi
|
||||
description: Build submitted via Travis CI
|
||||
notification_email: coverity@arseniyshestakov.com
|
||||
build_command_prepend: "cov-configure --compiler clang-3.6 --comptype clangcc && cov-configure --comptype clangcxx --compiler clang++-3.6 && cmake -G Ninja .. -DCMAKE_BUILD_TYPE=DEBUG -DENABLE_LAUNCHER=1 -DENABLE_TEST=0"
|
||||
build_command: ninja -j 2
|
||||
build_command_prepend: "cov-configure --compiler clang-3.6 --comptype clangcc && cov-configure --comptype clangcxx --compiler clang++-3.6 && cmake -G Ninja .. -DCMAKE_BUILD_TYPE=DEBUG -DENABLE_LAUNCHER=0 -DENABLE_TEST=0"
|
||||
build_command: ninja -j 3
|
||||
branch_pattern: coverity_scan
|
||||
|
||||
notifications:
|
||||
email:
|
||||
recipients:
|
||||
- vcmi.fail@mixaill.tk
|
||||
- saven.ivan@gmail.com
|
||||
- noreply@vcmi.eu
|
||||
on_success: change
|
||||
on_failure: always
|
||||
slack:
|
||||
secure: "KHXFe14FFKtw5mErWbj730+utqy7i/3AUobWfAMAGvWI5sJYlhbBU+KvvCoD2SlRQg3mQqgwVw8NBJF1Mffs7WcRmrFFFmuMqZxFLAfKBd3T0CxWpAGfnfNgDmlfV4OfEgQWk1pakEPOymhxbbmLUuCjykZDuTcioxAk0UAHDwY="
|
||||
|
@ -186,16 +186,15 @@ void CBattleAI::attemptCastingSpell()
|
||||
if(!hero)
|
||||
return;
|
||||
|
||||
if(!cb->battleCanCastSpell())
|
||||
if(cb->battleCanCastSpell(hero, ECastingMode::HERO_CASTING) != ESpellCastProblem::OK)
|
||||
return;
|
||||
|
||||
LOGL("Casting spells sounds like fun. Let's see...");
|
||||
//Get all spells we can cast
|
||||
std::vector<const CSpell*> possibleSpells;
|
||||
vstd::copy_if(VLC->spellh->objects, std::back_inserter(possibleSpells), [this] (const CSpell *s) -> bool
|
||||
vstd::copy_if(VLC->spellh->objects, std::back_inserter(possibleSpells), [this, hero] (const CSpell *s) -> bool
|
||||
{
|
||||
auto problem = getCbc()->battleCanCastThisSpell(s);
|
||||
return problem == ESpellCastProblem::OK;
|
||||
return s->canBeCast(getCbc().get(), ECastingMode::HERO_CASTING, hero) == ESpellCastProblem::OK;
|
||||
});
|
||||
LOGFL("I can cast %d spells.", possibleSpells.size());
|
||||
|
||||
|
@ -1,3 +1,8 @@
|
||||
0.99 -> 1.00
|
||||
|
||||
GENERAL:
|
||||
* Spectator mode was implemented through command-line options
|
||||
|
||||
0.98 -> 0.99
|
||||
|
||||
GENERAL:
|
||||
|
4
Global.h
4
Global.h
@ -143,6 +143,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
|
||||
//The only available version is 3, as of Boost 1.50
|
||||
#include <boost/version.hpp>
|
||||
@ -184,6 +185,9 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/math/special_functions/round.hpp>
|
||||
#include <boost/multi_array.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
|
||||
#ifndef M_PI
|
||||
# define M_PI 3.14159265358979323846
|
||||
|
157
client/CMT.cpp
157
client/CMT.cpp
@ -91,7 +91,6 @@ SDL_Surface *screen = nullptr, //main screen surface
|
||||
std::queue<SDL_Event> events;
|
||||
boost::mutex eventsM;
|
||||
|
||||
bool gNoGUI = false;
|
||||
CondSh<bool> serverAlive(false);
|
||||
static po::variables_map vm;
|
||||
|
||||
@ -114,6 +113,29 @@ void endGame();
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
void startTestMap(const std::string &mapname)
|
||||
{
|
||||
StartInfo si;
|
||||
si.mapname = mapname;
|
||||
si.mode = StartInfo::NEW_GAME;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
PlayerSettings &pset = si.playerInfos[PlayerColor(i)];
|
||||
pset.color = PlayerColor(i);
|
||||
pset.name = CGI->generaltexth->allTexts[468];//Computer
|
||||
pset.playerID = PlayerSettings::PLAYER_AI;
|
||||
pset.compOnly = true;
|
||||
pset.castle = 0;
|
||||
pset.hero = -1;
|
||||
pset.heroPortrait = -1;
|
||||
pset.handicap = PlayerSettings::NO_HANDICAP;
|
||||
}
|
||||
|
||||
while(GH.topInt())
|
||||
GH.popIntTotally(GH.topInt());
|
||||
startGame(&si);
|
||||
}
|
||||
|
||||
void startGameFromFile(const bfs::path &fname)
|
||||
{
|
||||
StartInfo si;
|
||||
@ -150,7 +172,7 @@ void init()
|
||||
logGlobal->infoStream()<<"Initializing VCMI_Lib: "<<tmh.getDiff();
|
||||
|
||||
|
||||
if(!gNoGUI)
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
pomtime.getDiff();
|
||||
CCS->curh = new CCursorHandler;
|
||||
@ -238,10 +260,19 @@ int main(int argc, char** argv)
|
||||
opts.add_options()
|
||||
("help,h", "display help and exit")
|
||||
("version,v", "display version information and exit")
|
||||
("disable-shm", "force disable shared memory usage")
|
||||
("enable-shm-uuid", "use UUID for shared memory identifier")
|
||||
("battle,b", po::value<std::string>(), "runs game in duel mode (battle-only")
|
||||
("start", po::value<bfs::path>(), "starts game from saved StartInfo file")
|
||||
("testmap", po::value<std::string>(), "")
|
||||
("spectate,s", "enable spectator interface for AI-only games")
|
||||
("spectate-ignore-hero", "wont follow heroes on adventure map")
|
||||
("spectate-hero-speed", po::value<int>(), "hero movement speed on adventure map")
|
||||
("spectate-battle-speed", po::value<int>(), "battle animation speed for spectator")
|
||||
("spectate-skip-battle", "skip battles in spectator view")
|
||||
("spectate-skip-battle-result", "skip battle result window")
|
||||
("onlyAI", "runs without human player, all players will be default AI")
|
||||
("noGUI", "runs without GUI, implies --onlyAI")
|
||||
("headless", "runs without GUI, implies --onlyAI")
|
||||
("ai", po::value<std::vector<std::string>>(), "AI to be used for the player, can be specified several times for the consecutive players")
|
||||
("oneGoodAI", "puts one default AI and the rest will be EmptyAI")
|
||||
("autoSkip", "automatically skip turns in GUI")
|
||||
@ -254,9 +285,9 @@ int main(int argc, char** argv)
|
||||
("loadplayer", po::value<int>(),"specifies which player we are in multiplayer loaded games (0=Red, etc.)")
|
||||
("loadserverip",po::value<std::string>(),"IP for loaded game server")
|
||||
("loadserverport",po::value<std::string>(),"port for loaded game server")
|
||||
("testingport",po::value<std::string>(),"port for testing, override specified in config file")
|
||||
("testingfileprefix",po::value<std::string>(),"prefix for auto save files")
|
||||
("testingsavefrequency",po::value<int>(),"how often auto save should be created");
|
||||
("serverport", po::value<si64>(), "override port specified in config file")
|
||||
("saveprefix", po::value<std::string>(), "prefix for auto save files")
|
||||
("savefrequency", po::value<si64>(), "limit auto save creation to each N days");
|
||||
|
||||
if(argc > 1)
|
||||
{
|
||||
@ -281,11 +312,6 @@ int main(int argc, char** argv)
|
||||
prog_version();
|
||||
return 0;
|
||||
}
|
||||
if(vm.count("noGUI"))
|
||||
{
|
||||
gNoGUI = true;
|
||||
vm.insert(std::pair<std::string, po::variable_value>("onlyAI", po::variable_value()));
|
||||
}
|
||||
if(vm.count("donotstartserver"))
|
||||
{
|
||||
CServerHandler::DO_NOT_START_SERVER = true;
|
||||
@ -314,16 +340,21 @@ int main(int argc, char** argv)
|
||||
// Init filesystem and settings
|
||||
preinitDLL(::console);
|
||||
settings.init();
|
||||
Settings session = settings.write["session"];
|
||||
session["onlyai"].Bool() = vm.count("onlyAI");
|
||||
if(vm.count("headless"))
|
||||
{
|
||||
session["headless"].Bool() = true;
|
||||
session["onlyai"].Bool() = true;
|
||||
}
|
||||
// Shared memory options
|
||||
session["disable-shm"].Bool() = vm.count("disable-shm");
|
||||
session["enable-shm-uuid"].Bool() = vm.count("enable-shm-uuid");
|
||||
|
||||
// Init special testing settings
|
||||
Settings testingSettings = settings.write["testing"];
|
||||
if(vm.count("testingport") && vm.count("testingfileprefix"))
|
||||
{
|
||||
testingSettings["enabled"].Bool() = true;
|
||||
testingSettings["port"].String() = vm["testingport"].as<std::string>();
|
||||
testingSettings["prefix"].String() = vm["testingfileprefix"].as<std::string>();
|
||||
testingSettings["savefrequency"].Float() = vm.count("testingsavefrequency") ? vm["testingsavefrequency"].as<int>() : 1;
|
||||
}
|
||||
session["serverport"].Integer() = vm.count("serverport") ? vm["serverport"].as<si64>() : 0;
|
||||
session["saveprefix"].String() = vm.count("saveprefix") ? vm["saveprefix"].as<std::string>() : "";
|
||||
session["savefrequency"].Integer() = vm.count("savefrequency") ? vm["savefrequency"].as<si64>() : 1;
|
||||
|
||||
// Initialize logging based on settings
|
||||
logConfig.configure();
|
||||
@ -368,7 +399,7 @@ int main(int argc, char** argv)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(!gNoGUI)
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO|SDL_INIT_NOPARACHUTE))
|
||||
{
|
||||
@ -431,7 +462,7 @@ int main(int argc, char** argv)
|
||||
#ifdef DISABLE_VIDEO
|
||||
CCS->videoh = new CEmptyVideoPlayer;
|
||||
#else
|
||||
if (!gNoGUI && !vm.count("disable-video"))
|
||||
if (!settings["session"]["headless"].Bool() && !vm.count("disable-video"))
|
||||
CCS->videoh = new CVideoPlayer;
|
||||
else
|
||||
CCS->videoh = new CEmptyVideoPlayer;
|
||||
@ -460,7 +491,7 @@ int main(int argc, char** argv)
|
||||
init();
|
||||
#endif
|
||||
|
||||
if(!gNoGUI )
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
if(!vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool())
|
||||
playIntro();
|
||||
@ -484,7 +515,6 @@ int main(int argc, char** argv)
|
||||
|
||||
if(!vm.count("battle"))
|
||||
{
|
||||
Settings session = settings.write["session"];
|
||||
session["autoSkip"].Bool() = vm.count("autoSkip");
|
||||
session["oneGoodAI"].Bool() = vm.count("oneGoodAI");
|
||||
session["aiSolo"].Bool() = false;
|
||||
@ -492,17 +522,39 @@ int main(int argc, char** argv)
|
||||
bfs::path fileToStartFrom; //none by default
|
||||
if(vm.count("start"))
|
||||
fileToStartFrom = vm["start"].as<bfs::path>();
|
||||
if(vm.count("testmap"))
|
||||
{
|
||||
session["testmap"].String() = vm["testmap"].as<std::string>();
|
||||
}
|
||||
|
||||
if(!fileToStartFrom.empty() && bfs::exists(fileToStartFrom))
|
||||
startGameFromFile(fileToStartFrom); //ommit pregame and start the game using settings from file
|
||||
session["spectate"].Bool() = vm.count("spectate");
|
||||
if(session["spectate"].Bool())
|
||||
{
|
||||
session["spectate-ignore-hero"].Bool() = vm.count("spectate-ignore-hero");
|
||||
session["spectate-skip-battle"].Bool() = vm.count("spectate-skip-battle");
|
||||
session["spectate-skip-battle-result"].Bool() = vm.count("spectate-skip-battle-result");
|
||||
if(vm.count("spectate-hero-speed"))
|
||||
session["spectate-hero-speed"].Integer() = vm["spectate-hero-speed"].as<int>();
|
||||
if(vm.count("spectate-battle-speed"))
|
||||
session["spectate-battle-speed"].Float() = vm["spectate-battle-speed"].as<int>();
|
||||
}
|
||||
if(!session["testmap"].isNull())
|
||||
{
|
||||
startTestMap(session["testmap"].String());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!fileToStartFrom.empty())
|
||||
if(!fileToStartFrom.empty() && bfs::exists(fileToStartFrom))
|
||||
startGameFromFile(fileToStartFrom); //ommit pregame and start the game using settings from file
|
||||
else
|
||||
{
|
||||
logGlobal->warnStream() << "Warning: cannot find given file to start from (" << fileToStartFrom
|
||||
<< "). Falling back to main menu.";
|
||||
if(!fileToStartFrom.empty())
|
||||
{
|
||||
logGlobal->warnStream() << "Warning: cannot find given file to start from (" << fileToStartFrom
|
||||
<< "). Falling back to main menu.";
|
||||
}
|
||||
GH.curInt = CGPreGame::create(); //will set CGP pointer to itself
|
||||
}
|
||||
GH.curInt = CGPreGame::create(); //will set CGP pointer to itself
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -515,7 +567,7 @@ int main(int argc, char** argv)
|
||||
startGame(si);
|
||||
}
|
||||
|
||||
if(!gNoGUI)
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
mainLoop();
|
||||
}
|
||||
@ -558,6 +610,20 @@ void printInfoAboutIntObject(const CIntObject *obj, int level)
|
||||
printInfoAboutIntObject(child, level+1);
|
||||
}
|
||||
|
||||
void removeGUI()
|
||||
{
|
||||
// CClient::endGame
|
||||
GH.curInt = nullptr;
|
||||
if(GH.topInt())
|
||||
GH.topInt()->deactivate();
|
||||
GH.listInt.clear();
|
||||
GH.objsToBlit.clear();
|
||||
GH.statusbar = nullptr;
|
||||
logGlobal->infoStream() << "Removed GUI.";
|
||||
|
||||
LOCPLINT = nullptr;
|
||||
};
|
||||
|
||||
void processCommand(const std::string &message)
|
||||
{
|
||||
std::istringstream readed;
|
||||
@ -678,10 +744,6 @@ void processCommand(const std::string &message)
|
||||
*ptr = 666;
|
||||
//disaster!
|
||||
}
|
||||
else if(cn == "onlyai")
|
||||
{
|
||||
vm.insert(std::pair<std::string, po::variable_value>("onlyAI", po::variable_value()));
|
||||
}
|
||||
else if(cn == "mp" && adventureInt)
|
||||
{
|
||||
if(const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(adventureInt->selection))
|
||||
@ -817,20 +879,6 @@ void processCommand(const std::string &message)
|
||||
}
|
||||
}
|
||||
|
||||
auto removeGUI = [&]()
|
||||
{
|
||||
// CClient::endGame
|
||||
GH.curInt = nullptr;
|
||||
if(GH.topInt())
|
||||
GH.topInt()->deactivate();
|
||||
GH.listInt.clear();
|
||||
GH.objsToBlit.clear();
|
||||
GH.statusbar = nullptr;
|
||||
logNetwork->infoStream() << "Removed GUI.";
|
||||
|
||||
LOCPLINT = nullptr;
|
||||
|
||||
};
|
||||
auto giveTurn = [&](PlayerColor player)
|
||||
{
|
||||
YourTurn yt;
|
||||
@ -1273,10 +1321,13 @@ static void mainLoop()
|
||||
|
||||
void startGame(StartInfo * options, CConnection *serv/* = nullptr*/)
|
||||
{
|
||||
serverAlive.waitWhileTrue();
|
||||
serverAlive.setn(true);
|
||||
if(!CServerHandler::DO_NOT_START_SERVER)
|
||||
{
|
||||
serverAlive.waitWhileTrue();
|
||||
serverAlive.setn(true);
|
||||
}
|
||||
|
||||
if(vm.count("onlyAI"))
|
||||
if(settings["session"]["onlyai"].Bool())
|
||||
{
|
||||
auto ais = vm.count("ai") ? vm["ai"].as<std::vector<std::string>>() : std::vector<std::string>();
|
||||
|
||||
@ -1306,7 +1357,7 @@ void startGame(StartInfo * options, CConnection *serv/* = nullptr*/)
|
||||
if(!vm.count("loadplayer"))
|
||||
client->loadGame(fname);
|
||||
else
|
||||
client->loadGame(fname,vm.count("loadserver"),vm.count("loadhumanplayerindices") ? vm["loadhumanplayerindices"].as<std::vector<int>>() : std::vector<int>(),vm.count("loadnumplayers") ? vm["loadnumplayers"].as<int>() : 1,vm["loadplayer"].as<int>(),vm.count("loadserverip") ? vm["loadserverip"].as<std::string>() : "", vm.count("loadserverport") ? vm["loadserverport"].as<std::string>() : "3030");
|
||||
client->loadGame(fname,vm.count("loadserver"),vm.count("loadhumanplayerindices") ? vm["loadhumanplayerindices"].as<std::vector<int>>() : std::vector<int>(),vm.count("loadnumplayers") ? vm["loadnumplayers"].as<int>() : 1,vm["loadplayer"].as<int>(),vm.count("loadserverip") ? vm["loadserverip"].as<std::string>() : "", vm.count("loadserverport") ? vm["loadserverport"].as<ui16>() : CServerHandler::getDefaultPort());
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1328,7 +1379,7 @@ void handleQuit(bool ask/* = true*/)
|
||||
dispose();
|
||||
vstd::clear_pointer(console);
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(750));
|
||||
if(!gNoGUI)
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
cleanupRenderer();
|
||||
SDL_Quit();
|
||||
|
@ -11,9 +11,7 @@ extern SDL_Surface *screen; // main screen surface
|
||||
extern SDL_Surface *screen2; // and hlp surface (used to store not-active interfaces layer)
|
||||
extern SDL_Surface *screenBuf; // points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
|
||||
|
||||
|
||||
extern bool gNoGUI; //if true there is no client window and game is silently played between AIs
|
||||
|
||||
extern CondSh<bool> serverAlive; //used to prevent game start from executing if server is already running
|
||||
|
||||
void removeGUI();
|
||||
void handleQuit(bool ask = true);
|
||||
|
@ -296,6 +296,8 @@ void CMessage::drawIWindow(CInfoWindow * ret, std::string text, PlayerColor play
|
||||
|
||||
void CMessage::drawBorder(PlayerColor playerColor, SDL_Surface * ret, int w, int h, int x, int y)
|
||||
{
|
||||
if(playerColor.isSpectator())
|
||||
playerColor = PlayerColor(1);
|
||||
std::vector<const IImage*> &box = piecesOfBox.at(playerColor.getNum());
|
||||
|
||||
// Note: this code assumes that the corner dimensions are all the same.
|
||||
|
@ -171,12 +171,7 @@ void CPlayerInterface::yourTurn()
|
||||
GH.curInt = this;
|
||||
adventureInt->selection = nullptr;
|
||||
|
||||
std::string prefix = "";
|
||||
if (settings["testing"]["enabled"].Bool())
|
||||
{
|
||||
prefix = settings["testing"]["prefix"].String();
|
||||
}
|
||||
|
||||
std::string prefix = settings["session"]["saveprefix"].String();
|
||||
if (firstCall)
|
||||
{
|
||||
if (howManyPeople == 1)
|
||||
@ -192,7 +187,7 @@ void CPlayerInterface::yourTurn()
|
||||
}
|
||||
firstCall = 0;
|
||||
}
|
||||
else if (settings["testing"].isNull() || cb->getDate() % static_cast<int>(settings["testing"]["savefrequency"].Float()) == 0)
|
||||
else if (cb->getDate() % static_cast<int>(settings["session"]["savefrequency"].Integer()) == 0)
|
||||
{
|
||||
LOCPLINT->cb->save("Saves/" + prefix + "Autosave_" + boost::lexical_cast<std::string>(autosaveCount++ + 1));
|
||||
autosaveCount %= 5;
|
||||
@ -250,6 +245,9 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
|
||||
if (LOCPLINT != this)
|
||||
return;
|
||||
|
||||
if(settings["session"]["spectate"].Bool() && settings["session"]["spectate-ignore-hero"].Bool())
|
||||
return;
|
||||
|
||||
const CGHeroInstance * hero = cb->getHero(details.id); //object representing this hero
|
||||
int3 hp = details.start;
|
||||
|
||||
@ -326,7 +324,12 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
|
||||
}
|
||||
|
||||
ui32 speed;
|
||||
if (makingTurn) // our turn, our hero moves
|
||||
if(settings["session"]["spectate"].Bool())
|
||||
{
|
||||
if(!settings["session"]["spectate-hero-speed"].isNull())
|
||||
speed = settings["session"]["spectate-hero-speed"].Integer();
|
||||
}
|
||||
else if (makingTurn) // our turn, our hero moves
|
||||
speed = settings["adventure"]["heroSpeed"].Float();
|
||||
else
|
||||
speed = settings["adventure"]["enemySpeed"].Float();
|
||||
@ -339,7 +342,6 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
|
||||
return; // no animation
|
||||
}
|
||||
|
||||
|
||||
adventureInt->centerOn(hero); //actualizing screen pos
|
||||
adventureInt->minimap.redraw();
|
||||
adventureInt->heroList.redraw();
|
||||
@ -476,6 +478,14 @@ int3 CPlayerInterface::repairScreenPos(int3 pos)
|
||||
pos.y = CGI->mh->sizes.y - adventureInt->terrain.tilesh + CGI->mh->frameH;
|
||||
return pos;
|
||||
}
|
||||
|
||||
void CPlayerInterface::activateForSpectator()
|
||||
{
|
||||
adventureInt->state = CAdvMapInt::INGAME;
|
||||
adventureInt->activate();
|
||||
adventureInt->minimap.activate();
|
||||
}
|
||||
|
||||
void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
@ -1642,7 +1652,8 @@ void CPlayerInterface::update()
|
||||
}
|
||||
|
||||
//in some conditions we may receive calls before selection is initialized - we must ignore them
|
||||
if (adventureInt && !adventureInt->selection && GH.topInt() == adventureInt)
|
||||
if(adventureInt && GH.topInt() == adventureInt
|
||||
&& (!adventureInt->selection && !settings["session"]["spectate"].Bool()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -2136,7 +2147,7 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
|
||||
|
||||
--howManyPeople;
|
||||
|
||||
if (howManyPeople == 0) //all human players eliminated
|
||||
if(howManyPeople == 0 && !settings["session"]["spectate"].Bool()) //all human players eliminated
|
||||
{
|
||||
if (adventureInt)
|
||||
{
|
||||
@ -2157,7 +2168,7 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
|
||||
}
|
||||
else
|
||||
{
|
||||
if (howManyPeople == 0) //all human players eliminated
|
||||
if(howManyPeople == 0 && !settings["session"]["spectate"].Bool()) //all human players eliminated
|
||||
{
|
||||
requestReturningToMainMenu();
|
||||
}
|
||||
|
@ -236,6 +236,7 @@ public:
|
||||
void updateInfo(const CGObjectInstance * specific);
|
||||
void init(std::shared_ptr<CCallback> CB) override;
|
||||
int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
|
||||
void activateForSpectator(); // TODO: spectator probably need own player interface class
|
||||
|
||||
// show dialogs
|
||||
void showInfoDialog(const std::string &text, CComponent * component);
|
||||
|
@ -83,7 +83,7 @@ struct EvilHlpStruct
|
||||
|
||||
void reset()
|
||||
{
|
||||
vstd::clear_pointer(serv);
|
||||
// vstd::clear_pointer(serv);
|
||||
vstd::clear_pointer(sInfo);
|
||||
}
|
||||
|
||||
@ -131,10 +131,10 @@ void setPlayer(PlayerSettings &pset, ui8 player, const std::map<ui8, std::string
|
||||
playerColor = pset.color;
|
||||
}
|
||||
|
||||
void updateStartInfo(std::string filename, StartInfo & sInfo, const CMapHeader * mapHeader, const std::map<ui8, std::string> &playerNames)
|
||||
void updateStartInfo(std::string filename, StartInfo & sInfo, const std::unique_ptr<CMapHeader> & mapHeader, const std::map<ui8, std::string> &playerNames)
|
||||
{
|
||||
sInfo.playerInfos.clear();
|
||||
if(!mapHeader)
|
||||
if(!mapHeader.get())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -559,7 +559,7 @@ void CGPreGame::removeFromGui()
|
||||
GH.popInt(GH.topInt()); //remove background
|
||||
}
|
||||
|
||||
CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMultiMode MultiPlayer /*= CMenuScreen::SINGLE_PLAYER*/, const std::map<ui8, std::string> * Names /*= nullptr*/, const std::string & Address /*=""*/, const std::string & Port /*= ""*/)
|
||||
CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMultiMode MultiPlayer /*= CMenuScreen::SINGLE_PLAYER*/, const std::map<ui8, std::string> * Names /*= nullptr*/, const std::string & Address /*=""*/, const ui16 Port)
|
||||
: ISelectionScreenInfo(Names), serverHandlingThread(nullptr), mx(new boost::recursive_mutex),
|
||||
serv(nullptr), ongoingClosing(false), myNameID(255)
|
||||
{
|
||||
@ -799,7 +799,12 @@ void CSelectionScreen::changeSelection(const CMapInfo * to)
|
||||
SEL->sInfo.difficulty = to->scenarioOpts->difficulty;
|
||||
if(screenType != CMenuScreen::campaignList)
|
||||
{
|
||||
updateStartInfo(to ? to->fileURI : "", sInfo, to ? to->mapHeader.get() : nullptr);
|
||||
std::unique_ptr<CMapHeader> emptyHeader;
|
||||
if(to)
|
||||
updateStartInfo(to->fileURI, sInfo, to->mapHeader);
|
||||
else
|
||||
updateStartInfo("", sInfo, emptyHeader);
|
||||
|
||||
if(screenType == CMenuScreen::newGame)
|
||||
{
|
||||
if(to && to->isRandomMap)
|
||||
@ -3219,7 +3224,7 @@ void CHotSeatPlayers::enterSelectionScreen()
|
||||
void CBonusSelection::init()
|
||||
{
|
||||
highlightedRegion = nullptr;
|
||||
ourHeader = nullptr;
|
||||
ourHeader.reset();
|
||||
diffLb = nullptr;
|
||||
diffRb = nullptr;
|
||||
bonuses = nullptr;
|
||||
@ -3342,7 +3347,6 @@ CBonusSelection::CBonusSelection(const std::string & campaignFName)
|
||||
CBonusSelection::~CBonusSelection()
|
||||
{
|
||||
SDL_FreeSurface(background);
|
||||
delete ourHeader;
|
||||
sFlags->unload();
|
||||
}
|
||||
|
||||
@ -3423,10 +3427,9 @@ void CBonusSelection::selectMap(int mapNr, bool initialSelect)
|
||||
scenarioName += ':' + boost::lexical_cast<std::string>(selectedMap);
|
||||
|
||||
//get header
|
||||
delete ourHeader;
|
||||
std::string & headerStr = ourCampaign->camp->mapPieces.find(mapNr)->second;
|
||||
auto buffer = reinterpret_cast<const ui8 *>(headerStr.data());
|
||||
ourHeader = CMapService::loadMapHeader(buffer, headerStr.size(), scenarioName).release();
|
||||
ourHeader = CMapService::loadMapHeader(buffer, headerStr.size(), scenarioName);
|
||||
|
||||
std::map<ui8, std::string> names;
|
||||
names[1] = settings["general"]["playerName"].String();
|
||||
@ -3922,7 +3925,7 @@ ISelectionScreenInfo::~ISelectionScreenInfo()
|
||||
SEL = nullptr;
|
||||
}
|
||||
|
||||
void ISelectionScreenInfo::updateStartInfo(std::string filename, StartInfo & sInfo, const CMapHeader * mapHeader)
|
||||
void ISelectionScreenInfo::updateStartInfo(std::string filename, StartInfo & sInfo, const std::unique_ptr<CMapHeader> & mapHeader)
|
||||
{
|
||||
::updateStartInfo(filename, sInfo, mapHeader, playerNames);
|
||||
}
|
||||
@ -4320,7 +4323,7 @@ CSimpleJoinScreen::CSimpleJoinScreen(CMenuScreen::EMultiMode mode)
|
||||
cancel = new CButton(Point(142, 142), "MUBCANC.DEF", CGI->generaltexth->zelp[561], std::bind(&CGuiHandler::popIntTotally, std::ref(GH), this), SDLK_ESCAPE);
|
||||
bar = new CGStatusBar(new CPicture(Rect(7, 186, 218, 18), 0));
|
||||
|
||||
port->setText(boost::lexical_cast<std::string>(settings["server"]["port"].Float()), true);
|
||||
port->setText(CServerHandler::getDefaultPortStr(), true);
|
||||
address->setText(settings["server"]["server"].String(), true);
|
||||
address->giveFocus();
|
||||
}
|
||||
@ -4332,7 +4335,7 @@ void CSimpleJoinScreen::enterSelectionScreen(CMenuScreen::EMultiMode mode)
|
||||
|
||||
GH.popIntTotally(this);
|
||||
|
||||
GH.pushInt(new CSelectionScreen(CMenuScreen::newGame, mode, nullptr, textAddress, textPort));
|
||||
GH.pushInt(new CSelectionScreen(CMenuScreen::newGame, mode, nullptr, textAddress, boost::lexical_cast<ui16>(textPort)));
|
||||
}
|
||||
void CSimpleJoinScreen::onChange(const std::string & newText)
|
||||
{
|
||||
|
@ -341,7 +341,7 @@ public:
|
||||
virtual void postChatMessage(const std::string &txt){};
|
||||
|
||||
void setPlayer(PlayerSettings &pset, ui8 player);
|
||||
void updateStartInfo( std::string filename, StartInfo & sInfo, const CMapHeader * mapHeader );
|
||||
void updateStartInfo(std::string filename, StartInfo & sInfo, const std::unique_ptr<CMapHeader> & mapHeader);
|
||||
|
||||
ui8 getIdOfFirstUnallocatedPlayer(); //returns 0 if none
|
||||
bool isGuest() const;
|
||||
@ -371,7 +371,7 @@ public:
|
||||
bool ongoingClosing;
|
||||
ui8 myNameID; //used when networking - otherwise all player are "mine"
|
||||
|
||||
CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMultiMode MultiPlayer = CMenuScreen::SINGLE_PLAYER, const std::map<ui8, std::string> * Names = nullptr, const std::string & Address = "", const std::string & Port = "");
|
||||
CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMultiMode MultiPlayer = CMenuScreen::SINGLE_PLAYER, const std::map<ui8, std::string> * Names = nullptr, const std::string & Address = "", const ui16 Port = 0);
|
||||
~CSelectionScreen();
|
||||
void toggleTab(CIntObject *tab);
|
||||
void changeSelection(const CMapInfo *to);
|
||||
@ -539,7 +539,7 @@ private:
|
||||
int selectedMap;
|
||||
boost::optional<int> selectedBonus;
|
||||
StartInfo startInfo;
|
||||
CMapHeader * ourHeader;
|
||||
std::unique_ptr<CMapHeader> ourHeader;
|
||||
};
|
||||
|
||||
/// Campaign selection screen
|
||||
|
@ -41,9 +41,7 @@
|
||||
#include "CMT.h"
|
||||
|
||||
extern std::string NAME;
|
||||
#ifndef VCMI_ANDROID
|
||||
namespace intpr = boost::interprocess;
|
||||
#else
|
||||
#ifdef VCMI_ANDROID
|
||||
#include "lib/CAndroidVMHelper.h"
|
||||
#endif
|
||||
|
||||
@ -251,24 +249,16 @@ void CClient::endGame(bool closeConnection /*= true*/)
|
||||
}
|
||||
|
||||
#if 1
|
||||
void CClient::loadGame(const std::string & fname, const bool server, const std::vector<int>& humanplayerindices, const int loadNumPlayers, int player_, const std::string & ipaddr, const std::string & port)
|
||||
void CClient::loadGame(const std::string & fname, const bool server, const std::vector<int>& humanplayerindices, const int loadNumPlayers, int player_, const std::string & ipaddr, const ui16 port)
|
||||
{
|
||||
PlayerColor player(player_); //intentional shadowing
|
||||
logNetwork->infoStream() << "Loading procedure started!";
|
||||
|
||||
std::string realPort;
|
||||
if(settings["testing"]["enabled"].Bool())
|
||||
realPort = settings["testing"]["port"].String();
|
||||
else if(port.size())
|
||||
realPort = port;
|
||||
else
|
||||
realPort = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
|
||||
|
||||
CServerHandler sh;
|
||||
if(server)
|
||||
sh.startServer();
|
||||
else
|
||||
serv = sh.justConnectToServer(ipaddr, realPort);
|
||||
serv = sh.justConnectToServer(ipaddr, port);
|
||||
|
||||
CStopWatch tmh;
|
||||
std::unique_ptr<CLoadFile> loader;
|
||||
@ -391,7 +381,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
|
||||
else
|
||||
{
|
||||
serv = con;
|
||||
networkMode = (con->connectionID == 1) ? HOST : GUEST;
|
||||
networkMode = con->isHost() ? HOST : GUEST;
|
||||
}
|
||||
|
||||
CConnection &c = *serv;
|
||||
@ -416,10 +406,9 @@ void CClient::newGame( CConnection *con, StartInfo *si )
|
||||
|
||||
// Initialize game state
|
||||
gs = new CGameState();
|
||||
logNetwork->infoStream() <<"\tCreating gamestate: "<<tmh.getDiff();
|
||||
logNetwork->info("\tCreating gamestate: %i",tmh.getDiff());
|
||||
|
||||
gs->scenarioOps = si;
|
||||
gs->init(si);
|
||||
gs->init(si, settings["general"]["saveRandomMaps"].Bool());
|
||||
logNetwork->infoStream() <<"Initializing GameState (together): "<<tmh.getDiff();
|
||||
|
||||
// Now after possible random map gen, we know exact player count.
|
||||
@ -441,10 +430,13 @@ void CClient::newGame( CConnection *con, StartInfo *si )
|
||||
// Init map handler
|
||||
if(gs->map)
|
||||
{
|
||||
const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
|
||||
CGI->mh->map = gs->map;
|
||||
logNetwork->infoStream() << "Creating mapHandler: " << tmh.getDiff();
|
||||
CGI->mh->init();
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
const_cast<CGameInfo*>(CGI)->mh = new CMapHandler();
|
||||
CGI->mh->map = gs->map;
|
||||
logNetwork->infoStream() << "Creating mapHandler: " << tmh.getDiff();
|
||||
CGI->mh->init();
|
||||
}
|
||||
pathInfo = make_unique<CPathsInfo>(getMapSize());
|
||||
logNetwork->infoStream() << "Initializing mapHandler (together): " << tmh.getDiff();
|
||||
}
|
||||
@ -481,7 +473,7 @@ void CClient::newGame( CConnection *con, StartInfo *si )
|
||||
|
||||
if(si->mode == StartInfo::DUEL)
|
||||
{
|
||||
if(!gNoGUI)
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
auto p = std::make_shared<CPlayerInterface>(PlayerColor::NEUTRAL);
|
||||
@ -493,6 +485,10 @@ void CClient::newGame( CConnection *con, StartInfo *si )
|
||||
}
|
||||
else
|
||||
{
|
||||
if(settings["session"]["spectate"].Bool())
|
||||
{
|
||||
installNewPlayerInterface(std::make_shared<CPlayerInterface>(PlayerColor::SPECTATOR), PlayerColor::SPECTATOR, true);
|
||||
}
|
||||
loadNeutralBattleAI();
|
||||
}
|
||||
|
||||
@ -644,10 +640,33 @@ void CClient::serialize(BinaryDeserializer & h, const int version, const std::se
|
||||
nInt->human = isHuman;
|
||||
nInt->playerID = pid;
|
||||
|
||||
if(playerIDs.count(pid))
|
||||
installNewPlayerInterface(nInt, pid);
|
||||
|
||||
nInt->loadGame(h, version);
|
||||
if(settings["session"]["onlyai"].Bool() && isHuman)
|
||||
{
|
||||
removeGUI();
|
||||
nInt.reset();
|
||||
dllname = aiNameForPlayer(false);
|
||||
nInt = CDynLibHandler::getNewAI(dllname);
|
||||
nInt->dllName = dllname;
|
||||
nInt->human = false;
|
||||
nInt->playerID = pid;
|
||||
installNewPlayerInterface(nInt, pid);
|
||||
GH.totalRedraw();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(playerIDs.count(pid))
|
||||
installNewPlayerInterface(nInt, pid);
|
||||
}
|
||||
}
|
||||
if(settings["session"]["spectate"].Bool())
|
||||
{
|
||||
removeGUI();
|
||||
auto p = std::make_shared<CPlayerInterface>(PlayerColor::SPECTATOR);
|
||||
installNewPlayerInterface(p, PlayerColor::SPECTATOR, true);
|
||||
GH.curInt = p.get();
|
||||
LOCPLINT->activateForSpectator();
|
||||
GH.totalRedraw();
|
||||
}
|
||||
|
||||
if(playerIDs.count(PlayerColor::NEUTRAL))
|
||||
@ -693,13 +712,22 @@ void CClient::stopConnection()
|
||||
{
|
||||
terminate = true;
|
||||
|
||||
if (serv) //request closing connection
|
||||
if(serv)
|
||||
{
|
||||
logNetwork->infoStream() << "Connection has been requested to be closed.";
|
||||
boost::unique_lock<boost::mutex>(*serv->wmx);
|
||||
CloseServer close_server;
|
||||
sendRequest(&close_server, PlayerColor::NEUTRAL);
|
||||
logNetwork->infoStream() << "Sent closing signal to the server";
|
||||
if(serv->isHost()) //request closing connection
|
||||
{
|
||||
logNetwork->infoStream() << "Connection has been requested to be closed.";
|
||||
CloseServer close_server;
|
||||
sendRequest(&close_server, PlayerColor::NEUTRAL);
|
||||
logNetwork->infoStream() << "Sent closing signal to the server";
|
||||
}
|
||||
else
|
||||
{
|
||||
LeaveGame leave_Game;
|
||||
sendRequest(&leave_Game, PlayerColor::NEUTRAL);
|
||||
logNetwork->infoStream() << "Sent leaving signal to the server";
|
||||
}
|
||||
}
|
||||
|
||||
if(connectionHandler)//end connection handler
|
||||
@ -708,16 +736,13 @@ void CClient::stopConnection()
|
||||
connectionHandler->join();
|
||||
|
||||
logNetwork->infoStream() << "Connection handler thread joined";
|
||||
|
||||
delete connectionHandler;
|
||||
connectionHandler = nullptr;
|
||||
vstd::clear_pointer(connectionHandler);
|
||||
}
|
||||
|
||||
if (serv) //and delete connection
|
||||
{
|
||||
serv->close();
|
||||
delete serv;
|
||||
serv = nullptr;
|
||||
vstd::clear_pointer(serv);
|
||||
logNetwork->warnStream() << "Our socket has been closed.";
|
||||
}
|
||||
}
|
||||
@ -750,14 +775,29 @@ void CClient::battleStarted(const BattleInfo * info)
|
||||
def = std::dynamic_pointer_cast<CPlayerInterface>( playerint[rightSide.color] );
|
||||
}
|
||||
|
||||
if(!gNoGUI && (!!att || !!def || gs->scenarioOps->mode == StartInfo::DUEL))
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
|
||||
Rect((screen->w - 800)/2,
|
||||
(screen->h - 600)/2, 800, 600), att, def);
|
||||
if(!!att || !!def || gs->scenarioOps->mode == StartInfo::DUEL)
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
|
||||
Rect((screen->w - 800)/2,
|
||||
(screen->h - 600)/2, 800, 600), att, def);
|
||||
|
||||
GH.pushInt(bi);
|
||||
GH.pushInt(bi);
|
||||
}
|
||||
else if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
|
||||
{
|
||||
//TODO: This certainly need improvement
|
||||
auto spectratorInt = std::dynamic_pointer_cast<CPlayerInterface>(playerint[PlayerColor::SPECTATOR]);
|
||||
spectratorInt->cb->setBattle(info);
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
|
||||
Rect((screen->w - 800)/2,
|
||||
(screen->h - 600)/2, 800, 600), att, def, spectratorInt);
|
||||
|
||||
GH.pushInt(bi);
|
||||
}
|
||||
}
|
||||
|
||||
auto callBattleStart = [&](PlayerColor color, ui8 side){
|
||||
@ -768,6 +808,8 @@ void CClient::battleStarted(const BattleInfo * info)
|
||||
callBattleStart(leftSide.color, 0);
|
||||
callBattleStart(rightSide.color, 1);
|
||||
callBattleStart(PlayerColor::UNFLAGGABLE, 1);
|
||||
if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
|
||||
callBattleStart(PlayerColor::SPECTATOR, 1);
|
||||
|
||||
if(info->tacticDistance && vstd::contains(battleints,info->sides[info->tacticsSide].color))
|
||||
{
|
||||
@ -780,6 +822,9 @@ void CClient::battleFinished()
|
||||
for(auto & side : gs->curB->sides)
|
||||
if(battleCallbacks.count(side.color))
|
||||
battleCallbacks[side.color]->setBattle(nullptr);
|
||||
|
||||
if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
|
||||
battleCallbacks[PlayerColor::SPECTATOR]->setBattle(nullptr);
|
||||
}
|
||||
|
||||
void CClient::loadNeutralBattleAI()
|
||||
@ -877,7 +922,7 @@ void CClient::campaignMapFinished( std::shared_ptr<CCampaignState> camp )
|
||||
}
|
||||
}
|
||||
|
||||
void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color)
|
||||
void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color, bool battlecb)
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
|
||||
@ -893,7 +938,7 @@ void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInte
|
||||
battleCallbacks[colorUsed] = cb;
|
||||
gameInterface->init(cb);
|
||||
|
||||
installNewBattleInterface(gameInterface, color, false);
|
||||
installNewBattleInterface(gameInterface, color, battlecb);
|
||||
}
|
||||
|
||||
void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback /*= true*/)
|
||||
@ -924,6 +969,11 @@ std::string CClient::aiNameForPlayer(const PlayerSettings &ps, bool battleAI)
|
||||
return ps.name;
|
||||
}
|
||||
|
||||
return aiNameForPlayer(battleAI);
|
||||
}
|
||||
|
||||
std::string CClient::aiNameForPlayer(bool battleAI)
|
||||
{
|
||||
const int sensibleAILimit = settings["session"]["oneGoodAI"].Bool() ? 1 : PlayerColor::PLAYER_LIMIT_I;
|
||||
std::string goodAI = battleAI ? settings["server"]["neutralAI"].String() : settings["server"]["playerAI"].String();
|
||||
std::string badAI = battleAI ? "StupidAI" : "EmptyAI";
|
||||
@ -966,11 +1016,8 @@ void CServerHandler::waitForServer()
|
||||
th.update();
|
||||
|
||||
#ifndef VCMI_ANDROID
|
||||
intpr::scoped_lock<intpr::interprocess_mutex> slock(shared->sr->mutex);
|
||||
while(!shared->sr->ready)
|
||||
{
|
||||
shared->sr->cond.wait(slock);
|
||||
}
|
||||
if(shared)
|
||||
shared->sr->waitTillReady();
|
||||
#else
|
||||
logNetwork->infoStream() << "waiting for server";
|
||||
while (!androidTestServerReadyFlag.load())
|
||||
@ -987,16 +1034,15 @@ void CServerHandler::waitForServer()
|
||||
|
||||
CConnection * CServerHandler::connectToServer()
|
||||
{
|
||||
#ifndef VCMI_ANDROID
|
||||
if(!shared->sr->ready)
|
||||
waitForServer();
|
||||
#else
|
||||
waitForServer();
|
||||
#endif
|
||||
|
||||
th.update(); //put breakpoint here to attach to server before it does something stupid
|
||||
|
||||
CConnection *ret = justConnectToServer(settings["server"]["server"].String(), port);
|
||||
#ifndef VCMI_ANDROID
|
||||
CConnection *ret = justConnectToServer(settings["server"]["server"].String(), shared ? shared->sr->port : 0);
|
||||
#else
|
||||
CConnection *ret = justConnectToServer(settings["server"]["server"].String());
|
||||
#endif
|
||||
|
||||
if(verbose)
|
||||
logNetwork->infoStream()<<"\tConnecting to the server: "<<th.getDiff();
|
||||
@ -1004,24 +1050,43 @@ CConnection * CServerHandler::connectToServer()
|
||||
return ret;
|
||||
}
|
||||
|
||||
ui16 CServerHandler::getDefaultPort()
|
||||
{
|
||||
if(settings["session"]["serverport"].Integer())
|
||||
return settings["session"]["serverport"].Integer();
|
||||
else
|
||||
return settings["server"]["port"].Integer();
|
||||
}
|
||||
|
||||
std::string CServerHandler::getDefaultPortStr()
|
||||
{
|
||||
return boost::lexical_cast<std::string>(getDefaultPort());
|
||||
}
|
||||
|
||||
CServerHandler::CServerHandler(bool runServer /*= false*/)
|
||||
{
|
||||
serverThread = nullptr;
|
||||
shared = nullptr;
|
||||
if(settings["testing"]["enabled"].Bool())
|
||||
port = settings["testing"]["port"].String();
|
||||
else
|
||||
port = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
|
||||
verbose = true;
|
||||
uuid = boost::uuids::to_string(boost::uuids::random_generator()());
|
||||
|
||||
#ifndef VCMI_ANDROID
|
||||
boost::interprocess::shared_memory_object::remove("vcmi_memory"); //if the application has previously crashed, the memory may not have been removed. to avoid problems - try to destroy it
|
||||
if(DO_NOT_START_SERVER || settings["session"]["disable-shm"].Bool())
|
||||
return;
|
||||
|
||||
std::string sharedMemoryName = "vcmi_memory";
|
||||
if(settings["session"]["enable-shm-uuid"].Bool())
|
||||
{
|
||||
//used or automated testing when multiple clients start simultaneously
|
||||
sharedMemoryName += "_" + uuid;
|
||||
}
|
||||
try
|
||||
{
|
||||
shared = new SharedMem();
|
||||
shared = new SharedMemory(sharedMemoryName, true);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
vstd::clear_pointer(shared);
|
||||
logNetwork->error("Cannot open interprocess memory.");
|
||||
handleException();
|
||||
throw;
|
||||
@ -1040,7 +1105,18 @@ void CServerHandler::callServer()
|
||||
#ifndef VCMI_ANDROID
|
||||
setThreadName("CServerHandler::callServer");
|
||||
const std::string logName = (VCMIDirs::get().userCachePath() / "server_log.txt").string();
|
||||
const std::string comm = VCMIDirs::get().serverPath().string() + " --port=" + port + " > \"" + logName + '\"';
|
||||
std::string comm = VCMIDirs::get().serverPath().string()
|
||||
+ " --port=" + getDefaultPortStr()
|
||||
+ " --run-by-client"
|
||||
+ " --uuid=" + uuid;
|
||||
if(shared)
|
||||
{
|
||||
comm += " --enable-shm";
|
||||
if(settings["session"]["enable-shm-uuid"].Bool())
|
||||
comm += " --enable-shm-uuid";
|
||||
}
|
||||
comm += " > \"" + logName + '\"';
|
||||
|
||||
int result = std::system(comm.c_str());
|
||||
if (result == 0)
|
||||
{
|
||||
@ -1056,16 +1132,8 @@ void CServerHandler::callServer()
|
||||
#endif
|
||||
}
|
||||
|
||||
CConnection * CServerHandler::justConnectToServer(const std::string &host, const std::string &port)
|
||||
CConnection * CServerHandler::justConnectToServer(const std::string &host, const ui16 port)
|
||||
{
|
||||
std::string realPort;
|
||||
if(settings["testing"]["enabled"].Bool())
|
||||
realPort = settings["testing"]["port"].String();
|
||||
else if(port.size())
|
||||
realPort = port;
|
||||
else
|
||||
realPort = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
|
||||
|
||||
CConnection *ret = nullptr;
|
||||
while(!ret)
|
||||
{
|
||||
@ -1073,8 +1141,9 @@ CConnection * CServerHandler::justConnectToServer(const std::string &host, const
|
||||
{
|
||||
logNetwork->infoStream() << "Establishing connection...";
|
||||
ret = new CConnection( host.size() ? host : settings["server"]["server"].String(),
|
||||
realPort,
|
||||
port ? port : getDefaultPort(),
|
||||
NAME);
|
||||
ret->connectionID = 1; // TODO: Refactoring for the server so IDs set outside of CConnection
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ class CGameInterface;
|
||||
class CConnection;
|
||||
class CCallback;
|
||||
struct BattleAction;
|
||||
struct SharedMem;
|
||||
struct SharedMemory;
|
||||
class CClient;
|
||||
class CScriptingModule;
|
||||
struct CPathsInfo;
|
||||
@ -46,9 +46,9 @@ public:
|
||||
|
||||
CStopWatch th;
|
||||
boost::thread *serverThread; //thread that called system to run server
|
||||
SharedMem *shared; //interprocess memory (for waiting for server)
|
||||
SharedMemory * shared;
|
||||
std::string uuid;
|
||||
bool verbose; //whether to print log msgs
|
||||
std::string port; //port number in text form
|
||||
|
||||
//functions setting up local server
|
||||
void startServer(); //creates a thread with callServer
|
||||
@ -56,7 +56,9 @@ public:
|
||||
CConnection * connectToServer(); //connects to server
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
static CConnection * justConnectToServer(const std::string &host = "", const std::string &port = ""); //connects to given host without taking any other actions (like setting up server)
|
||||
static CConnection * justConnectToServer(const std::string &host = "", const ui16 port = 0); //connects to given host without taking any other actions (like setting up server)
|
||||
static ui16 getDefaultPort();
|
||||
static std::string getDefaultPortStr();
|
||||
|
||||
CServerHandler(bool runServer = false);
|
||||
virtual ~CServerHandler();
|
||||
@ -147,14 +149,15 @@ public:
|
||||
void newGame(CConnection *con, StartInfo *si); //con - connection to server
|
||||
|
||||
void loadNeutralBattleAI();
|
||||
void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color);
|
||||
void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color, bool battlecb = false);
|
||||
void installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback = true);
|
||||
std::string aiNameForPlayer(const PlayerSettings &ps, bool battleAI); //empty means no AI -> human
|
||||
std::string aiNameForPlayer(bool battleAI);
|
||||
|
||||
void endGame(bool closeConnection = true);
|
||||
void stopConnection();
|
||||
void save(const std::string & fname);
|
||||
void loadGame(const std::string & fname, const bool server = true, const std::vector<int>& humanplayerindices = std::vector<int>(), const int loadnumplayers = 1, int player_ = -1, const std::string & ipaddr = "", const std::string & port = "");
|
||||
void loadGame(const std::string & fname, const bool server = true, const std::vector<int>& humanplayerindices = std::vector<int>(), const int loadnumplayers = 1, int player_ = -1, const std::string & ipaddr = "", const ui16 port = 0);
|
||||
void run();
|
||||
void campaignMapFinished( std::shared_ptr<CCampaignState> camp );
|
||||
void finishCampaign( std::shared_ptr<CCampaignState> camp );
|
||||
|
@ -96,6 +96,10 @@
|
||||
#define BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(function,...) \
|
||||
CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[0].color, function, __VA_ARGS__) \
|
||||
CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[1].color, function, __VA_ARGS__) \
|
||||
if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool() && LOCPLINT->battleInt) \
|
||||
{ \
|
||||
CALL_ONLY_THAT_BATTLE_INTERFACE(PlayerColor::SPECTATOR, function, __VA_ARGS__) \
|
||||
} \
|
||||
BATTLE_INTERFACE_CALL_RECEIVERS(function, __VA_ARGS__)
|
||||
/*
|
||||
* NetPacksClient.cpp, part of VCMI engine
|
||||
@ -283,13 +287,13 @@ void GiveBonus::applyCl(CClient *cl)
|
||||
void ChangeObjPos::applyFirstCl(CClient *cl)
|
||||
{
|
||||
CGObjectInstance *obj = GS(cl)->getObjInstance(objid);
|
||||
if(flags & 1)
|
||||
if(flags & 1 && CGI->mh)
|
||||
CGI->mh->hideObject(obj);
|
||||
}
|
||||
void ChangeObjPos::applyCl(CClient *cl)
|
||||
{
|
||||
CGObjectInstance *obj = GS(cl)->getObjInstance(objid);
|
||||
if(flags & 1)
|
||||
if(flags & 1 && CGI->mh)
|
||||
CGI->mh->printObject(obj);
|
||||
|
||||
cl->invalidatePaths();
|
||||
@ -298,6 +302,10 @@ void ChangeObjPos::applyCl(CClient *cl)
|
||||
void PlayerEndsGame::applyCl(CClient *cl)
|
||||
{
|
||||
CALL_IN_ALL_INTERFACES(gameOver, player, victoryLossCheckResult);
|
||||
|
||||
// In auto testing mode we always close client if red player won or lose
|
||||
if(!settings["session"]["testmap"].isNull() && player == PlayerColor(0))
|
||||
handleQuit(settings["session"]["spectate"].Bool()); // if spectator is active ask to close client or not
|
||||
}
|
||||
|
||||
void RemoveBonus::applyCl(CClient *cl)
|
||||
@ -335,7 +343,8 @@ void RemoveObject::applyFirstCl(CClient *cl)
|
||||
{
|
||||
const CGObjectInstance *o = cl->getObj(id);
|
||||
|
||||
CGI->mh->hideObject(o, true);
|
||||
if(CGI->mh)
|
||||
CGI->mh->hideObject(o, true);
|
||||
|
||||
//notify interfaces about removal
|
||||
for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++)
|
||||
@ -357,18 +366,20 @@ void TryMoveHero::applyFirstCl(CClient *cl)
|
||||
//check if playerint will have the knowledge about movement - if not, directly update maphandler
|
||||
for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++)
|
||||
{
|
||||
if(i->first >= PlayerColor::PLAYER_LIMIT)
|
||||
continue;
|
||||
TeamState *t = GS(cl)->getPlayerTeam(i->first);
|
||||
if((t->fogOfWarMap[start.x-1][start.y][start.z] || t->fogOfWarMap[end.x-1][end.y][end.z])
|
||||
&& GS(cl)->getPlayer(i->first)->human)
|
||||
humanKnows = true;
|
||||
auto ps = GS(cl)->getPlayer(i->first);
|
||||
if(ps && (GS(cl)->isVisible(start - int3(1, 0, 0), i->first) || GS(cl)->isVisible(end - int3(1, 0, 0), i->first)))
|
||||
{
|
||||
if(ps->human)
|
||||
humanKnows = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!CGI->mh)
|
||||
return;
|
||||
|
||||
if(result == TELEPORTATION || result == EMBARK || result == DISEMBARK || !humanKnows)
|
||||
CGI->mh->hideObject(h, result == EMBARK && humanKnows);
|
||||
|
||||
|
||||
if(result == DISEMBARK)
|
||||
CGI->mh->printObject(h->boat);
|
||||
}
|
||||
@ -378,13 +389,14 @@ void TryMoveHero::applyCl(CClient *cl)
|
||||
const CGHeroInstance *h = cl->getHero(id);
|
||||
cl->invalidatePaths();
|
||||
|
||||
if(result == TELEPORTATION || result == EMBARK || result == DISEMBARK)
|
||||
if(CGI->mh)
|
||||
{
|
||||
CGI->mh->printObject(h, result == DISEMBARK);
|
||||
}
|
||||
if(result == TELEPORTATION || result == EMBARK || result == DISEMBARK)
|
||||
CGI->mh->printObject(h, result == DISEMBARK);
|
||||
|
||||
if(result == EMBARK)
|
||||
CGI->mh->hideObject(h->boat);
|
||||
if(result == EMBARK)
|
||||
CGI->mh->hideObject(h->boat);
|
||||
}
|
||||
|
||||
PlayerColor player = h->tempOwner;
|
||||
|
||||
@ -395,18 +407,17 @@ void TryMoveHero::applyCl(CClient *cl)
|
||||
//notify interfaces about move
|
||||
for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++)
|
||||
{
|
||||
if(i->first >= PlayerColor::PLAYER_LIMIT) continue;
|
||||
TeamState *t = GS(cl)->getPlayerTeam(i->first);
|
||||
if(t->fogOfWarMap[start.x-1][start.y][start.z] || t->fogOfWarMap[end.x-1][end.y][end.z])
|
||||
if(GS(cl)->isVisible(start - int3(1, 0, 0), i->first)
|
||||
|| GS(cl)->isVisible(end - int3(1, 0, 0), i->first))
|
||||
{
|
||||
i->second->heroMoved(*this);
|
||||
}
|
||||
}
|
||||
|
||||
if(!humanKnows) //maphandler didn't get update from playerint, do it now
|
||||
{ //TODO: restructure nicely
|
||||
//maphandler didn't get update from playerint, do it now
|
||||
//TODO: restructure nicely
|
||||
if(!humanKnows && CGI->mh)
|
||||
CGI->mh->printObject(h);
|
||||
}
|
||||
}
|
||||
|
||||
void NewStructures::applyCl(CClient *cl)
|
||||
@ -482,22 +493,22 @@ void HeroRecruited::applyCl(CClient *cl)
|
||||
needsPrinting = false;
|
||||
}
|
||||
}
|
||||
if (needsPrinting)
|
||||
{
|
||||
if(needsPrinting && CGI->mh)
|
||||
CGI->mh->printObject(h);
|
||||
}
|
||||
}
|
||||
|
||||
void GiveHero::applyCl(CClient *cl)
|
||||
{
|
||||
CGHeroInstance *h = GS(cl)->getHero(id);
|
||||
CGI->mh->printObject(h);
|
||||
if(CGI->mh)
|
||||
CGI->mh->printObject(h);
|
||||
cl->playerint[h->tempOwner]->heroCreated(h);
|
||||
}
|
||||
|
||||
void GiveHero::applyFirstCl(CClient *cl)
|
||||
{
|
||||
CGI->mh->hideObject(GS(cl)->getHero(id));
|
||||
if(CGI->mh)
|
||||
CGI->mh->hideObject(GS(cl)->getHero(id));
|
||||
}
|
||||
|
||||
void InfoWindow::applyCl(CClient *cl)
|
||||
@ -588,6 +599,8 @@ void BattleStart::applyFirstCl(CClient *cl)
|
||||
info->tile, info->sides[0].hero, info->sides[1].hero);
|
||||
CALL_ONLY_THAT_BATTLE_INTERFACE(info->sides[1].color, battleStartBefore, info->sides[0].armyObject, info->sides[1].armyObject,
|
||||
info->tile, info->sides[0].hero, info->sides[1].hero);
|
||||
CALL_ONLY_THAT_BATTLE_INTERFACE(PlayerColor::SPECTATOR, battleStartBefore, info->sides[0].armyObject, info->sides[1].armyObject,
|
||||
info->tile, info->sides[0].hero, info->sides[1].hero);
|
||||
BATTLE_INTERFACE_CALL_RECEIVERS(battleStartBefore, info->sides[0].armyObject, info->sides[1].armyObject,
|
||||
info->tile, info->sides[0].hero, info->sides[1].hero);
|
||||
}
|
||||
@ -707,7 +720,7 @@ void BattleResultsApplied::applyCl(CClient *cl)
|
||||
{
|
||||
INTERFACE_CALL_IF_PRESENT(player1, battleResultsApplied);
|
||||
INTERFACE_CALL_IF_PRESENT(player2, battleResultsApplied);
|
||||
INTERFACE_CALL_IF_PRESENT(PlayerColor::UNFLAGGABLE, battleResultsApplied);
|
||||
INTERFACE_CALL_IF_PRESENT(PlayerColor::SPECTATOR, battleResultsApplied);
|
||||
if(GS(cl)->initialOpts->mode == StartInfo::DUEL)
|
||||
{
|
||||
handleQuit();
|
||||
@ -808,7 +821,10 @@ void PlayerMessage::applyCl(CClient *cl)
|
||||
logNetwork->debugStream() << "Player "<< player <<" sends a message: " << text;
|
||||
|
||||
std::ostringstream str;
|
||||
str << cl->getPlayer(player)->nodeName() <<": " << text;
|
||||
if(player.isSpectator())
|
||||
str << "Spectator: " << text;
|
||||
else
|
||||
str << cl->getPlayer(player)->nodeName() <<": " << text;
|
||||
if(LOCPLINT)
|
||||
LOCPLINT->cingconsole->print(str.str());
|
||||
}
|
||||
@ -904,7 +920,8 @@ void NewObject::applyCl(CClient *cl)
|
||||
cl->invalidatePaths();
|
||||
|
||||
const CGObjectInstance *obj = cl->getObj(id);
|
||||
CGI->mh->printObject(obj, true);
|
||||
if(CGI->mh)
|
||||
CGI->mh->printObject(obj, true);
|
||||
|
||||
for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++)
|
||||
{
|
||||
|
@ -292,6 +292,4 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
@ -109,17 +109,10 @@
|
||||
<Filter>gui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\CCallback.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CQuestLog.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SDLRWwrapper.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="VCMI_client.rc" />
|
||||
<ClInclude Include="CQuestLog.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="vcmi.ico" />
|
||||
@ -254,5 +247,6 @@
|
||||
<Filter>gui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\CCallback.h" />
|
||||
<ClInclude Include="SDLRWwrapper.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -95,7 +95,7 @@ void CBattleInterface::addNewAnim(CBattleAnimation *anim)
|
||||
CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet *army2,
|
||||
const CGHeroInstance *hero1, const CGHeroInstance *hero2,
|
||||
const SDL_Rect & myRect,
|
||||
std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen)
|
||||
std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt)
|
||||
: background(nullptr), queue(nullptr), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
|
||||
activeStack(nullptr), mouseHoveredStack(nullptr), stackToActivate(nullptr), selectedStack(nullptr), previouslyHoveredHex(-1),
|
||||
currentlyHoveredHex(-1), attackingHex(-1), stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellToCast(nullptr), sp(nullptr),
|
||||
@ -105,12 +105,15 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
|
||||
{
|
||||
OBJ_CONSTRUCTION;
|
||||
|
||||
if (!curInt)
|
||||
if(spectatorInt)
|
||||
curInt = spectatorInt;
|
||||
else if(!curInt)
|
||||
{
|
||||
//May happen when we are defending during network MP game -> attacker interface is just not present
|
||||
curInt = defenderInt;
|
||||
}
|
||||
|
||||
|
||||
animsAreDisplayed.setn(false);
|
||||
pos = myRect;
|
||||
strongInterest = true;
|
||||
@ -377,6 +380,8 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
|
||||
currentAction = INVALID;
|
||||
selectedAction = INVALID;
|
||||
addUsedEvents(RCLICK | MOVE | KEYBOARD);
|
||||
|
||||
blockUI(settings["session"]["spectate"].Bool());
|
||||
}
|
||||
|
||||
CBattleInterface::~CBattleInterface()
|
||||
@ -448,6 +453,7 @@ CBattleInterface::~CBattleInterface()
|
||||
int terrain = LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType;
|
||||
CCS->musich->playMusicFromSet("terrain", terrain, true);
|
||||
}
|
||||
animsAreDisplayed.setn(false);
|
||||
}
|
||||
|
||||
void CBattleInterface::setPrintCellBorders(bool set)
|
||||
@ -871,19 +877,24 @@ void CBattleInterface::bSpellf()
|
||||
if (spellDestSelectMode) //we are casting a spell
|
||||
return;
|
||||
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE,0);
|
||||
|
||||
if (!myTurn)
|
||||
return;
|
||||
|
||||
auto myHero = currentHero();
|
||||
ESpellCastProblem::ESpellCastProblem spellCastProblem;
|
||||
if (curInt->cb->battleCanCastSpell(&spellCastProblem))
|
||||
if(!myHero)
|
||||
return;
|
||||
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE,0);
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem spellCastProblem = curInt->cb->battleCanCastSpell(myHero, ECastingMode::HERO_CASTING);
|
||||
|
||||
if(spellCastProblem == ESpellCastProblem::OK)
|
||||
{
|
||||
GH.pushInt(new CSpellWindow(myHero, curInt.get()));
|
||||
}
|
||||
else if (spellCastProblem == ESpellCastProblem::MAGIC_IS_BLOCKED)
|
||||
{
|
||||
//TODO: move to spell mechanics, add more information to spell cast problem
|
||||
//Handle Orb of Inhibition-like effects -> we want to display dialog with info, why casting is impossible
|
||||
auto blockingBonus = currentHero()->getBonusLocalFirst(Selector::type(Bonus::BLOCK_ALL_MAGIC));
|
||||
if (!blockingBonus)
|
||||
@ -1241,6 +1252,11 @@ void CBattleInterface::battleFinished(const BattleResult& br)
|
||||
void CBattleInterface::displayBattleFinished()
|
||||
{
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE,0);
|
||||
if(settings["session"]["spectate"].Bool() && settings["session"]["spectate-skip-battle-result"].Bool())
|
||||
{
|
||||
GH.popIntTotally(this);
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Rect temp_rect = genRect(561, 470, (screen->w - 800)/2 + 165, (screen->h - 600)/2 + 19);
|
||||
resWindow = new CBattleResultWindow(*bresult, temp_rect, *this->curInt);
|
||||
@ -1526,6 +1542,9 @@ void CBattleInterface::setAnimSpeed(int set)
|
||||
|
||||
int CBattleInterface::getAnimSpeed() const
|
||||
{
|
||||
if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-battle-speed"].isNull())
|
||||
return vstd::round(settings["session"]["spectate-battle-speed"].Float() *100);
|
||||
|
||||
return vstd::round(settings["battle"]["animationSpeed"].Float() *100);
|
||||
}
|
||||
|
||||
@ -1658,8 +1677,7 @@ void CBattleInterface::enterCreatureCastingMode()
|
||||
{
|
||||
const ISpellCaster *caster = activeStack;
|
||||
const CSpell *spell = SpellID(creatureSpellToCast).toSpell();
|
||||
|
||||
const bool isCastingPossible = (curInt->cb->battleCanCastThisSpellHere(caster, spell, ECastingMode::CREATURE_ACTIVE_CASTING, BattleHex::INVALID) == ESpellCastProblem::OK);
|
||||
const bool isCastingPossible = (spell->canBeCastAt(curInt->cb.get(), caster, ECastingMode::CREATURE_ACTIVE_CASTING, BattleHex::INVALID) == ESpellCastProblem::OK);
|
||||
|
||||
if (isCastingPossible)
|
||||
{
|
||||
@ -1849,11 +1867,17 @@ void CBattleInterface::showQueue()
|
||||
|
||||
void CBattleInterface::blockUI(bool on)
|
||||
{
|
||||
ESpellCastProblem::ESpellCastProblem spellcastingProblem;
|
||||
bool canCastSpells = curInt->cb->battleCanCastSpell(&spellcastingProblem);
|
||||
//if magic is blocked, we leave button active, so the message can be displayed (cf bug #97)
|
||||
if (!canCastSpells)
|
||||
canCastSpells = spellcastingProblem == ESpellCastProblem::MAGIC_IS_BLOCKED;
|
||||
bool canCastSpells = false;
|
||||
auto hero = curInt->cb->battleGetMyHero();
|
||||
|
||||
if(hero)
|
||||
{
|
||||
ESpellCastProblem::ESpellCastProblem spellcastingProblem = curInt->cb->battleCanCastSpell(hero, ECastingMode::HERO_CASTING);
|
||||
|
||||
//if magic is blocked, we leave button active, so the message can be displayed after button click
|
||||
canCastSpells = spellcastingProblem == ESpellCastProblem::OK || spellcastingProblem == ESpellCastProblem::MAGIC_IS_BLOCKED;
|
||||
}
|
||||
|
||||
bool canWait = activeStack ? !activeStack->waited() : false;
|
||||
|
||||
bOptions->block(on);
|
||||
@ -2511,7 +2535,7 @@ bool CBattleInterface::isCastingPossibleHere(const CStack *sactive, const CStack
|
||||
else
|
||||
{
|
||||
const ECastingMode::ECastingMode mode = creatureCasting ? ECastingMode::CREATURE_ACTIVE_CASTING : ECastingMode::HERO_CASTING;
|
||||
isCastingPossible = (curInt->cb->battleCanCastThisSpellHere(caster, sp, mode, myNumber) == ESpellCastProblem::OK);
|
||||
isCastingPossible = (sp->canBeCastAt(curInt->cb.get(), caster, mode, myNumber) == ESpellCastProblem::OK);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -269,7 +269,7 @@ public:
|
||||
ui32 animIDhelper; //for giving IDs for animations
|
||||
static CondSh<bool> animsAreDisplayed; //for waiting with the end of battle for end of anims
|
||||
|
||||
CBattleInterface(const CCreatureSet *army1, const CCreatureSet *army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, const SDL_Rect & myRect, std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen); //c-tor
|
||||
CBattleInterface(const CCreatureSet *army1, const CCreatureSet *army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, const SDL_Rect & myRect, std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt = nullptr); //c-tor
|
||||
virtual ~CBattleInterface(); //d-tor
|
||||
|
||||
//std::vector<TimeInterested*> timeinterested; //animation handling
|
||||
|
@ -191,7 +191,7 @@ void CBattleHero::clickLeft(tribool down, bool previousState)
|
||||
if(myOwner->spellDestSelectMode) //we are casting a spell
|
||||
return;
|
||||
|
||||
if(myHero != nullptr && !down && myOwner->myTurn && myOwner->getCurrentPlayerInterface()->cb->battleCanCastSpell()) //check conditions
|
||||
if(myHero != nullptr && !down && myOwner->myTurn && myOwner->getCurrentPlayerInterface()->cb->battleCanCastSpell(myHero, ECastingMode::HERO_CASTING) == ESpellCastProblem::OK) //check conditions
|
||||
{
|
||||
for(int it=0; it<GameConstants::BFIELD_SIZE; ++it) //do nothing when any hex is hovered - hero's animation overlaps battlefield
|
||||
{
|
||||
@ -211,14 +211,10 @@ void CBattleHero::clickRight(tribool down, bool previousState)
|
||||
windowPosition.y = myOwner->pos.y + 135;
|
||||
|
||||
InfoAboutHero targetHero;
|
||||
|
||||
if (down && myOwner->myTurn)
|
||||
if(down && (myOwner->myTurn || settings["session"]["spectate"].Bool()))
|
||||
{
|
||||
if (myHero != nullptr)
|
||||
targetHero.initFromHero(myHero, InfoAboutHero::EInfoLevel::INBATTLE);
|
||||
else
|
||||
targetHero = myOwner->enemyHero();
|
||||
|
||||
auto h = flip ? myOwner->defendingHeroInstance : myOwner->attackingHeroInstance;
|
||||
targetHero.initFromHero(h, InfoAboutHero::EInfoLevel::INBATTLE);
|
||||
GH.pushInt(new CHeroInfoWindow(targetHero, &windowPosition));
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../CMT.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../battle/CBattleInterface.h"
|
||||
|
||||
extern std::queue<SDL_Event> events;
|
||||
extern boost::mutex eventsM;
|
||||
@ -59,6 +60,7 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std:
|
||||
{
|
||||
processList(CIntObject::LCLICK,activityFlag,&lclickable,cb);
|
||||
processList(CIntObject::RCLICK,activityFlag,&rclickable,cb);
|
||||
processList(CIntObject::MCLICK,activityFlag,&mclickable,cb);
|
||||
processList(CIntObject::HOVER,activityFlag,&hoverable,cb);
|
||||
processList(CIntObject::MOVE,activityFlag,&motioninterested,cb);
|
||||
processList(CIntObject::KEYBOARD,activityFlag,&keyinterested,cb);
|
||||
@ -191,6 +193,46 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
|
||||
if (sEvent->type==SDL_KEYDOWN || sEvent->type==SDL_KEYUP)
|
||||
{
|
||||
SDL_KeyboardEvent key = sEvent->key;
|
||||
if(sEvent->type == SDL_KEYDOWN && key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool())
|
||||
{
|
||||
//TODO: we need some central place for all interface-independent hotkeys
|
||||
Settings s = settings.write["session"];
|
||||
switch(key.keysym.sym)
|
||||
{
|
||||
case SDLK_F5:
|
||||
if(settings["session"]["spectate-locked-pim"].Bool())
|
||||
LOCPLINT->pim->unlock();
|
||||
else
|
||||
LOCPLINT->pim->lock();
|
||||
s["spectate-locked-pim"].Bool() = !settings["session"]["spectate-locked-pim"].Bool();
|
||||
break;
|
||||
|
||||
case SDLK_F6:
|
||||
s["spectate-ignore-hero"].Bool() = !settings["session"]["spectate-ignore-hero"].Bool();
|
||||
break;
|
||||
|
||||
case SDLK_F7:
|
||||
s["spectate-skip-battle"].Bool() = !settings["session"]["spectate-skip-battle"].Bool();
|
||||
break;
|
||||
|
||||
case SDLK_F8:
|
||||
s["spectate-skip-battle-result"].Bool() = !settings["session"]["spectate-skip-battle-result"].Bool();
|
||||
break;
|
||||
|
||||
case SDLK_F9:
|
||||
//not working yet since CClient::run remain locked after CBattleInterface removal
|
||||
if(LOCPLINT->battleInt)
|
||||
{
|
||||
GH.popIntTotally(GH.topInt());
|
||||
vstd::clear_pointer(LOCPLINT->battleInt);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//translate numpad keys
|
||||
if(key.keysym.sym == SDLK_KP_ENTER)
|
||||
@ -219,18 +261,18 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
|
||||
CCS->curh->cursorMove(sEvent->motion.x, sEvent->motion.y);
|
||||
handleMouseMotion(sEvent);
|
||||
}
|
||||
else if (sEvent->type==SDL_MOUSEBUTTONDOWN)
|
||||
else if(sEvent->type == SDL_MOUSEBUTTONDOWN)
|
||||
{
|
||||
if(sEvent->button.button == SDL_BUTTON_LEFT)
|
||||
switch(sEvent->button.button)
|
||||
{
|
||||
|
||||
if(lastClick == sEvent->motion && (SDL_GetTicks() - lastClickTime) < 300)
|
||||
case SDL_BUTTON_LEFT:
|
||||
if(lastClick == sEvent->motion && (SDL_GetTicks() - lastClickTime) < 300)
|
||||
{
|
||||
std::list<CIntObject*> hlp = doubleClickInterested;
|
||||
for(auto i=hlp.begin(); i != hlp.end() && current; i++)
|
||||
for(auto i = hlp.begin(); i != hlp.end() && current; i++)
|
||||
{
|
||||
if(!vstd::contains(doubleClickInterested,*i)) continue;
|
||||
if (isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y))
|
||||
if(!vstd::contains(doubleClickInterested, *i)) continue;
|
||||
if(isItIn(&(*i)->pos, sEvent->motion.x, sEvent->motion.y))
|
||||
{
|
||||
(*i)->onDoubleClick();
|
||||
}
|
||||
@ -241,31 +283,16 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
|
||||
lastClick = sEvent->motion;
|
||||
lastClickTime = SDL_GetTicks();
|
||||
|
||||
std::list<CIntObject*> hlp = lclickable;
|
||||
for(auto i=hlp.begin(); i != hlp.end() && current; i++)
|
||||
{
|
||||
if(!vstd::contains(lclickable,*i)) continue;
|
||||
if (isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y))
|
||||
{
|
||||
prev = (*i)->pressedL;
|
||||
(*i)->pressedL = true;
|
||||
(*i)->clickLeft(true, prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sEvent->button.button == SDL_BUTTON_RIGHT)
|
||||
{
|
||||
std::list<CIntObject*> hlp = rclickable;
|
||||
for(auto i=hlp.begin(); i != hlp.end() && current; i++)
|
||||
{
|
||||
if(!vstd::contains(rclickable,*i)) continue;
|
||||
if (isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y))
|
||||
{
|
||||
prev = (*i)->pressedR;
|
||||
(*i)->pressedR = true;
|
||||
(*i)->clickRight(true, prev);
|
||||
}
|
||||
}
|
||||
handleMouseButtonClick(lclickable, EIntObjMouseBtnType::LEFT, true);
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
handleMouseButtonClick(rclickable, EIntObjMouseBtnType::RIGHT, true);
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
handleMouseButtonClick(mclickable, EIntObjMouseBtnType::MIDDLE, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (sEvent->type == SDL_MOUSEWHEEL)
|
||||
@ -295,41 +322,45 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
|
||||
}
|
||||
}
|
||||
//todo: muiltitouch
|
||||
else if ((sEvent->type==SDL_MOUSEBUTTONUP) && (sEvent->button.button == SDL_BUTTON_LEFT))
|
||||
else if(sEvent->type == SDL_MOUSEBUTTONUP)
|
||||
{
|
||||
std::list<CIntObject*> hlp = lclickable;
|
||||
for(auto i=hlp.begin(); i != hlp.end() && current; i++)
|
||||
switch(sEvent->button.button)
|
||||
{
|
||||
if(!vstd::contains(lclickable,*i)) continue;
|
||||
prev = (*i)->pressedL;
|
||||
(*i)->pressedL = false;
|
||||
if (isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y))
|
||||
{
|
||||
(*i)->clickLeft(false, prev);
|
||||
}
|
||||
else
|
||||
(*i)->clickLeft(boost::logic::indeterminate, prev);
|
||||
}
|
||||
}
|
||||
else if ((sEvent->type==SDL_MOUSEBUTTONUP) && (sEvent->button.button == SDL_BUTTON_RIGHT))
|
||||
{
|
||||
std::list<CIntObject*> hlp = rclickable;
|
||||
for(auto i=hlp.begin(); i != hlp.end() && current; i++)
|
||||
{
|
||||
if(!vstd::contains(rclickable,*i)) continue;
|
||||
prev = (*i)->pressedR;
|
||||
(*i)->pressedR = false;
|
||||
if (isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y))
|
||||
{
|
||||
(*i)->clickRight(false, prev);
|
||||
}
|
||||
else
|
||||
(*i)->clickRight(boost::logic::indeterminate, prev);
|
||||
case SDL_BUTTON_LEFT:
|
||||
handleMouseButtonClick(lclickable, EIntObjMouseBtnType::LEFT, false);
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
handleMouseButtonClick(rclickable, EIntObjMouseBtnType::RIGHT, false);
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
handleMouseButtonClick(mclickable, EIntObjMouseBtnType::MIDDLE, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
current = nullptr;
|
||||
} //event end
|
||||
|
||||
void CGuiHandler::handleMouseButtonClick(CIntObjectList & interestedObjs, EIntObjMouseBtnType btn, bool isPressed)
|
||||
{
|
||||
auto hlp = interestedObjs;
|
||||
for(auto i = hlp.begin(); i != hlp.end() && current; i++)
|
||||
{
|
||||
if(!vstd::contains(interestedObjs, *i)) continue;
|
||||
|
||||
auto prev = (*i)->mouseState(btn);
|
||||
if(!isPressed)
|
||||
(*i)->updateMouseState(btn, isPressed);
|
||||
if(isItIn(&(*i)->pos, current->motion.x, current->motion.y))
|
||||
{
|
||||
if(isPressed)
|
||||
(*i)->updateMouseState(btn, isPressed);
|
||||
(*i)->click(btn, isPressed, prev);
|
||||
}
|
||||
else if(!isPressed)
|
||||
(*i)->click(btn, boost::logic::indeterminate, prev);
|
||||
}
|
||||
}
|
||||
|
||||
void CGuiHandler::handleMouseMotion(SDL_Event *sEvent)
|
||||
{
|
||||
//sending active, hovered hoverable objects hover() call
|
||||
@ -361,7 +392,8 @@ void CGuiHandler::simpleRedraw()
|
||||
//update only top interface and draw background
|
||||
if(objsToBlit.size() > 1)
|
||||
blitAt(screen2,0,0,screen); //blit background
|
||||
objsToBlit.back()->show(screen); //blit active interface/window
|
||||
if(!objsToBlit.empty())
|
||||
objsToBlit.back()->show(screen); //blit active interface/window
|
||||
}
|
||||
|
||||
void CGuiHandler::handleMoveInterested(const SDL_MouseMotionEvent & motion)
|
||||
|
@ -10,6 +10,7 @@ class CIntObject;
|
||||
class IUpdateable;
|
||||
class IShowActivatable;
|
||||
class IShowable;
|
||||
enum class EIntObjMouseBtnType;
|
||||
template <typename T> struct CondSh;
|
||||
|
||||
/*
|
||||
@ -53,6 +54,7 @@ private:
|
||||
//active GUI elements (listening for events
|
||||
CIntObjectList lclickable,
|
||||
rclickable,
|
||||
mclickable,
|
||||
hoverable,
|
||||
keyinterested,
|
||||
motioninterested,
|
||||
@ -62,6 +64,7 @@ private:
|
||||
textInterested;
|
||||
|
||||
|
||||
void handleMouseButtonClick(CIntObjectList & interestedObjs, EIntObjMouseBtnType btn, bool isPressed);
|
||||
void processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb);
|
||||
public:
|
||||
void handleElementActivate(CIntObject * elem, ui16 activityFlag);
|
||||
|
@ -16,7 +16,7 @@ CIntObject::CIntObject(int used_, Point pos_):
|
||||
parent(parent_m),
|
||||
active(active_m)
|
||||
{
|
||||
pressedL = pressedR = hovered = captureAllKeys = strongInterest = false;
|
||||
hovered = captureAllKeys = strongInterest = false;
|
||||
toNextTick = timerDelay = 0;
|
||||
used = used_;
|
||||
|
||||
@ -134,6 +134,23 @@ CIntObject::~CIntObject()
|
||||
parent_m->removeChild(this);
|
||||
}
|
||||
|
||||
void CIntObject::click(EIntObjMouseBtnType btn, tribool down, bool previousState)
|
||||
{
|
||||
switch(btn)
|
||||
{
|
||||
default:
|
||||
case EIntObjMouseBtnType::LEFT:
|
||||
clickLeft(down, previousState);
|
||||
break;
|
||||
case EIntObjMouseBtnType::MIDDLE:
|
||||
clickMiddle(down, previousState);
|
||||
break;
|
||||
case EIntObjMouseBtnType::RIGHT:
|
||||
clickRight(down, previousState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CIntObject::printAtLoc( const std::string & text, int x, int y, EFonts font, SDL_Color kolor/*=Colors::WHITE*/, SDL_Surface * dst/*=screen*/ )
|
||||
{
|
||||
graphics->fonts[font]->renderTextLeft(dst, text, kolor, Point(pos.x + x, pos.y + y));
|
||||
@ -340,16 +357,9 @@ void CKeyShortcut::keyPressed(const SDL_KeyboardEvent & key)
|
||||
if(vstd::contains(assignedKeys,key.keysym.sym)
|
||||
|| vstd::contains(assignedKeys, CGuiHandler::numToDigit(key.keysym.sym)))
|
||||
{
|
||||
bool prev = pressedL;
|
||||
if(key.state == SDL_PRESSED)
|
||||
{
|
||||
pressedL = true;
|
||||
clickLeft(true, prev);
|
||||
}
|
||||
else
|
||||
{
|
||||
pressedL = false;
|
||||
clickLeft(false, prev);
|
||||
}
|
||||
bool prev = mouseState(EIntObjMouseBtnType::LEFT);
|
||||
updateMouseState(EIntObjMouseBtnType::LEFT, key.state == SDL_PRESSED);
|
||||
clickLeft(key.state == SDL_PRESSED, prev);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ public:
|
||||
virtual ~IShowActivatable(){}; //d-tor
|
||||
};
|
||||
|
||||
enum class EIntObjMouseBtnType { LEFT, MIDDLE, RIGHT };
|
||||
//typedef ui16 ActivityFlag;
|
||||
|
||||
// Base UI element
|
||||
@ -73,6 +74,8 @@ class CIntObject : public IShowActivatable //interface object
|
||||
int toNextTick;
|
||||
int timerDelay;
|
||||
|
||||
std::map<EIntObjMouseBtnType, bool> currentMouseState;
|
||||
|
||||
void onTimer(int timePassed);
|
||||
|
||||
//non-const versions of fields to allow changing them in CIntObject
|
||||
@ -104,13 +107,13 @@ public:
|
||||
CIntObject(int used=0, Point offset=Point());
|
||||
virtual ~CIntObject(); //d-tor
|
||||
|
||||
//l-clicks handling
|
||||
/*const*/ bool pressedL; //for determining if object is L-pressed
|
||||
virtual void clickLeft(tribool down, bool previousState){}
|
||||
void updateMouseState(EIntObjMouseBtnType btn, bool state) { currentMouseState[btn] = state; }
|
||||
bool mouseState(EIntObjMouseBtnType btn) const { return currentMouseState.count(btn) ? currentMouseState.at(btn) : false; }
|
||||
|
||||
//r-clicks handling
|
||||
/*const*/ bool pressedR; //for determining if object is R-pressed
|
||||
virtual void clickRight(tribool down, bool previousState){}
|
||||
virtual void click(EIntObjMouseBtnType btn, tribool down, bool previousState);
|
||||
virtual void clickLeft(tribool down, bool previousState) {}
|
||||
virtual void clickRight(tribool down, bool previousState) {}
|
||||
virtual void clickMiddle(tribool down, bool previousState) {}
|
||||
|
||||
//hover handling
|
||||
/*const*/ bool hovered; //for determining if object is hovered
|
||||
@ -138,7 +141,7 @@ public:
|
||||
//double click
|
||||
virtual void onDoubleClick(){}
|
||||
|
||||
enum {LCLICK=1, RCLICK=2, HOVER=4, MOVE=8, KEYBOARD=16, TIME=32, GENERAL=64, WHEEL=128, DOUBLECLICK=256, TEXTINPUT=512, ALL=0xffff};
|
||||
enum {LCLICK=1, RCLICK=2, HOVER=4, MOVE=8, KEYBOARD=16, TIME=32, GENERAL=64, WHEEL=128, DOUBLECLICK=256, TEXTINPUT=512, MCLICK=1024, ALL=0xffff};
|
||||
const ui16 & active;
|
||||
void addUsedEvents(ui16 newActions);
|
||||
void removeUsedEvents(ui16 newActions);
|
||||
|
@ -54,7 +54,7 @@ struct NeighborTilesInfo
|
||||
if ( dx + pos.x < 0 || dx + pos.x >= sizes.x
|
||||
|| dy + pos.y < 0 || dy + pos.y >= sizes.y)
|
||||
return false;
|
||||
return visibilityMap[dx+pos.x][dy+pos.y][pos.z];
|
||||
return settings["session"]["spectate"].Bool() ? true : visibilityMap[dx+pos.x][dy+pos.y][pos.z];
|
||||
};
|
||||
d7 = getTile(-1, -1); //789
|
||||
d8 = getTile( 0, -1); //456
|
||||
@ -563,7 +563,7 @@ void CMapHandler::CMapWorldViewBlitter::drawTileOverlay(SDL_Surface * targetSurf
|
||||
const CGObjectInstance * obj = object.obj;
|
||||
|
||||
const bool sameLevel = obj->pos.z == pos.z;
|
||||
const bool isVisible = (*info->visibilityMap)[pos.x][pos.y][pos.z];
|
||||
const bool isVisible = settings["session"]["spectate"].Bool() ? true : (*info->visibilityMap)[pos.x][pos.y][pos.z];
|
||||
const bool isVisitable = obj->visitableAt(pos.x, pos.y);
|
||||
|
||||
if(sameLevel && isVisible && isVisitable)
|
||||
@ -895,7 +895,7 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
|
||||
{
|
||||
const TerrainTile2 & tile = parent->ttiles[pos.x][pos.y][pos.z];
|
||||
|
||||
if (!(*info->visibilityMap)[pos.x][pos.y][topTile.z] && !info->showAllTerrain)
|
||||
if(!settings["session"]["spectate"].Bool() && !(*info->visibilityMap)[pos.x][pos.y][topTile.z] && !info->showAllTerrain)
|
||||
drawFow(targetSurf);
|
||||
|
||||
// overlay needs to be drawn over fow, because of artifacts-aura-like spells
|
||||
@ -1099,6 +1099,9 @@ bool CMapHandler::CMapBlitter::canDrawObject(const CGObjectInstance * obj) const
|
||||
|
||||
bool CMapHandler::CMapBlitter::canDrawCurrentTile() const
|
||||
{
|
||||
if(settings["session"]["spectate"].Bool())
|
||||
return true;
|
||||
|
||||
const NeighborTilesInfo neighbors(pos, parent->sizes, *info->visibilityMap);
|
||||
return !neighbors.areAllHidden();
|
||||
}
|
||||
|
@ -587,7 +587,7 @@ void CMinimap::hover(bool on)
|
||||
|
||||
void CMinimap::mouseMoved(const SDL_MouseMotionEvent & sEvent)
|
||||
{
|
||||
if (pressedL)
|
||||
if(mouseState(EIntObjMouseBtnType::LEFT))
|
||||
moveAdvMapSelection();
|
||||
}
|
||||
|
||||
|
@ -623,7 +623,7 @@ void CSlider::clickLeft(tribool down, bool previousState)
|
||||
return;
|
||||
// if (rw>1) return;
|
||||
// if (rw<0) return;
|
||||
slider->clickLeft(true, slider->pressedL);
|
||||
slider->clickLeft(true, slider->mouseState(EIntObjMouseBtnType::LEFT));
|
||||
moveTo(rw * positions + 0.5);
|
||||
return;
|
||||
}
|
||||
|
@ -161,16 +161,18 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
const CArtifact * const cur = ourOwner->commonInfo->src.art->artType;
|
||||
|
||||
switch(cur->id)
|
||||
if(cur->id == ArtifactID::CATAPULT)
|
||||
{
|
||||
case ArtifactID::CATAPULT:
|
||||
//should not happen, catapult cannot be selected
|
||||
assert(cur->id != ArtifactID::CATAPULT);
|
||||
break;
|
||||
case ArtifactID::BALLISTA: case ArtifactID::AMMO_CART: case ArtifactID::FIRST_AID_TENT: //war machines cannot go to backpack
|
||||
logGlobal->error("Attempt to move Catapult");
|
||||
}
|
||||
else if(cur->isBig())
|
||||
{
|
||||
//war machines cannot go to backpack
|
||||
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % cur->Name()));
|
||||
break;
|
||||
default:
|
||||
}
|
||||
else
|
||||
{
|
||||
setMeAsDest();
|
||||
vstd::amin(ourOwner->commonInfo->dst.slotID, ArtifactPosition(
|
||||
ourOwner->curHero->artifactsInBackpack.size() + GameConstants::BACKPACK_START));
|
||||
@ -184,7 +186,6 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState)
|
||||
deselect();
|
||||
else
|
||||
ourOwner->realizeCurrentTransaction();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -366,7 +367,7 @@ bool CHeroArtPlace::fitsHere(const CArtifactInstance * art) const
|
||||
|
||||
// Anything but War Machines can be placed in backpack.
|
||||
if (slotID >= GameConstants::BACKPACK_START)
|
||||
return !CGI->arth->isBigArtifact(art->artType->id);
|
||||
return !art->artType->isBig();
|
||||
|
||||
return art->canBePutAt(ArtifactLocation(ourOwner->curHero, slotID), true);
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ CTerrainRect::CTerrainRect()
|
||||
pos.w=ADVOPT.advmapW;
|
||||
pos.h=ADVOPT.advmapH;
|
||||
moveX = moveY = 0;
|
||||
addUsedEvents(LCLICK | RCLICK | HOVER | MOVE);
|
||||
addUsedEvents(LCLICK | RCLICK | MCLICK | HOVER | MOVE);
|
||||
}
|
||||
|
||||
CTerrainRect::~CTerrainRect()
|
||||
@ -124,17 +124,10 @@ void CTerrainRect::clickLeft(tribool down, bool previousState)
|
||||
#ifdef VCMI_ANDROID
|
||||
if(adventureInt->swipeEnabled)
|
||||
{
|
||||
if(down == true)
|
||||
if(handleSwipeStateChange(down == true))
|
||||
{
|
||||
swipeInitialRealPos = int3(GH.current->motion.x, GH.current->motion.y, 0);
|
||||
swipeInitialMapPos = int3(adventureInt->position);
|
||||
return; // if swipe is enabled, we don't process "down" events and wait for "up" (to make sure this wasn't a swiping gesture)
|
||||
}
|
||||
else if(isSwiping) // only accept this touch if it wasn't a swipe
|
||||
{
|
||||
isSwiping = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -165,22 +158,32 @@ void CTerrainRect::clickRight(tribool down, bool previousState)
|
||||
adventureInt->tileRClicked(mp);
|
||||
}
|
||||
|
||||
void CTerrainRect::clickMiddle(tribool down, bool previousState)
|
||||
{
|
||||
handleSwipeStateChange(down == true);
|
||||
}
|
||||
|
||||
void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent)
|
||||
{
|
||||
handleHover(sEvent);
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
if(!adventureInt->swipeEnabled || sEvent.state == 0)
|
||||
if(!adventureInt->swipeEnabled)
|
||||
return;
|
||||
|
||||
handleSwipeMove(sEvent);
|
||||
#endif // !VCMI_ANDROID
|
||||
}
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
|
||||
void CTerrainRect::handleSwipeMove(const SDL_MouseMotionEvent & sEvent)
|
||||
{
|
||||
#ifdef VCMI_ANDROID
|
||||
if(sEvent.state == 0) // any "button" is enough on android
|
||||
#else //!VCMI_ANDROID
|
||||
if((sEvent.state & SDL_BUTTON_MMASK) == 0) // swipe only works with middle mouse on other platforms
|
||||
#endif //!VCMI_ANDROID
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isSwiping)
|
||||
{
|
||||
// try to distinguish if this touch was meant to be a swipe or just fat-fingering press
|
||||
@ -201,7 +204,21 @@ void CTerrainRect::handleSwipeMove(const SDL_MouseMotionEvent & sEvent)
|
||||
}
|
||||
}
|
||||
|
||||
#endif // VCMI_ANDROID
|
||||
bool CTerrainRect::handleSwipeStateChange(bool btnPressed)
|
||||
{
|
||||
if(btnPressed)
|
||||
{
|
||||
swipeInitialRealPos = int3(GH.current->motion.x, GH.current->motion.y, 0);
|
||||
swipeInitialMapPos = int3(adventureInt->position);
|
||||
return true;
|
||||
}
|
||||
else if(isSwiping) // only accept this touch if it wasn't a swipe
|
||||
{
|
||||
isSwiping = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CTerrainRect::handleHover(const SDL_MouseMotionEvent &sEvent)
|
||||
{
|
||||
@ -534,11 +551,9 @@ CAdvMapInt::CAdvMapInt():
|
||||
infoBar(Rect(ADVOPT.infoboxX, ADVOPT.infoboxY, 192, 192)), state(NA),
|
||||
spellBeingCasted(nullptr), position(int3(0, 0, 0)), selection(nullptr),
|
||||
updateScreen(false), anim(0), animValHitCount(0), heroAnim(0), heroAnimValHitCount(0),
|
||||
activeMapPanel(nullptr), duringAITurn(false), scrollingDir(0), scrollingState(false)
|
||||
#ifdef VCMI_ANDROID
|
||||
, swipeEnabled(settings["general"]["swipe"].Bool()), swipeMovementRequested(false),
|
||||
activeMapPanel(nullptr), duringAITurn(false), scrollingDir(0), scrollingState(false),
|
||||
swipeEnabled(settings["general"]["swipe"].Bool()), swipeMovementRequested(false),
|
||||
swipeTargetPosition(int3(-1, -1, -1))
|
||||
#endif
|
||||
{
|
||||
adventureInt = this;
|
||||
pos.x = pos.y = 0;
|
||||
@ -1007,21 +1022,24 @@ void CAdvMapInt::show(SDL_Surface * to)
|
||||
}
|
||||
++heroAnim;
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
if(swipeEnabled)
|
||||
{
|
||||
handleSwipeUpdate();
|
||||
}
|
||||
#ifdef VCMI_ANDROID // on android, map-moving mode is exclusive (TODO technically it might work with both enabled; to be checked)
|
||||
else
|
||||
#endif // VCMI_ANDROID
|
||||
{
|
||||
#endif // !VCMI_ANDROID
|
||||
handleMapScrollingUpdate();
|
||||
#ifdef VCMI_ANDROID
|
||||
}
|
||||
#endif
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
gems[i]->setFrame(LOCPLINT->playerID.getNum());
|
||||
{
|
||||
if(settings["session"]["spectate"].Bool())
|
||||
gems[i]->setFrame(PlayerColor(1).getNum());
|
||||
else
|
||||
gems[i]->setFrame(LOCPLINT->playerID.getNum());
|
||||
}
|
||||
if(updateScreen)
|
||||
{
|
||||
int3 betterPos = LOCPLINT->repairScreenPos(position);
|
||||
@ -1084,14 +1102,13 @@ void CAdvMapInt::handleMapScrollingUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
|
||||
void CAdvMapInt::handleSwipeUpdate()
|
||||
{
|
||||
if(swipeMovementRequested)
|
||||
{
|
||||
position.x = swipeTargetPosition.x;
|
||||
position.y = swipeTargetPosition.y;
|
||||
auto fixedPos = LOCPLINT->repairScreenPos(swipeTargetPosition);
|
||||
position.x = fixedPos.x;
|
||||
position.y = fixedPos.y;
|
||||
CCS->curh->changeGraphic(ECursor::DEFAULT, 0);
|
||||
updateScreen = true;
|
||||
minimap.redraw();
|
||||
@ -1099,8 +1116,6 @@ void CAdvMapInt::handleSwipeUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void CAdvMapInt::selectionChanged()
|
||||
{
|
||||
const CGTownInstance *to = LOCPLINT->towns[townList.getSelectedIndex()];
|
||||
@ -1481,7 +1496,8 @@ void CAdvMapInt::setPlayer(PlayerColor Player)
|
||||
void CAdvMapInt::startTurn()
|
||||
{
|
||||
state = INGAME;
|
||||
if(LOCPLINT->cb->getCurrentPlayer() == LOCPLINT->playerID)
|
||||
if(LOCPLINT->cb->getCurrentPlayer() == LOCPLINT->playerID
|
||||
|| settings["session"]["spectate"].Bool())
|
||||
{
|
||||
adjustActiveness(false);
|
||||
minimap.setAIRadar(false);
|
||||
@ -1490,6 +1506,9 @@ void CAdvMapInt::startTurn()
|
||||
|
||||
void CAdvMapInt::endingTurn()
|
||||
{
|
||||
if(settings["session"]["spectate"].Bool())
|
||||
return;
|
||||
|
||||
if(LOCPLINT->cingconsole->active)
|
||||
LOCPLINT->cingconsole->deactivate();
|
||||
LOCPLINT->makingTurn = false;
|
||||
@ -1817,6 +1836,9 @@ const IShipyard * CAdvMapInt::ourInaccessibleShipyard(const CGObjectInstance *ob
|
||||
|
||||
void CAdvMapInt::aiTurnStarted()
|
||||
{
|
||||
if(settings["session"]["spectate"].Bool())
|
||||
return;
|
||||
|
||||
adjustActiveness(true);
|
||||
CCS->musich->playMusicFromSet("enemy-turn", true);
|
||||
adventureInt->minimap.setAIRadar(true);
|
||||
|
@ -62,10 +62,10 @@ class CTerrainRect
|
||||
bool isSwiping;
|
||||
static constexpr float SwipeTouchSlop = 16.0f;
|
||||
|
||||
void handleHover(const SDL_MouseMotionEvent &sEvent);
|
||||
#ifdef VCMI_ANDROID
|
||||
void handleSwipeMove(const SDL_MouseMotionEvent &sEvent);
|
||||
#endif // VCMI_ANDROID
|
||||
void handleHover(const SDL_MouseMotionEvent & sEvent);
|
||||
void handleSwipeMove(const SDL_MouseMotionEvent & sEvent);
|
||||
/// handles start/finish of swipe (press/release of corresponding button); returns true if state change was handled
|
||||
bool handleSwipeStateChange(bool btnPressed);
|
||||
public:
|
||||
int tilesw, tilesh; //width and height of terrain to blit in tiles
|
||||
int3 curHoveredTile;
|
||||
@ -77,6 +77,7 @@ public:
|
||||
void deactivate() override;
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void clickRight(tribool down, bool previousState) override;
|
||||
void clickMiddle(tribool down, bool previousState) override;
|
||||
void hover(bool on) override;
|
||||
void mouseMoved (const SDL_MouseMotionEvent & sEvent) override;
|
||||
void show(SDL_Surface * to) override;
|
||||
@ -132,11 +133,9 @@ public:
|
||||
enum{LEFT=1, RIGHT=2, UP=4, DOWN=8};
|
||||
ui8 scrollingDir; //uses enum: LEFT RIGHT, UP, DOWN
|
||||
bool scrollingState;
|
||||
#ifdef VCMI_ANDROID
|
||||
bool swipeEnabled;
|
||||
bool swipeMovementRequested;
|
||||
int3 swipeTargetPosition;
|
||||
#endif // !VCMI_ANDROID
|
||||
|
||||
enum{NA, INGAME, WAITING} state;
|
||||
|
||||
@ -260,9 +259,7 @@ public:
|
||||
void changeMode(EAdvMapMode newMode, float newScale = 0.36f);
|
||||
|
||||
void handleMapScrollingUpdate();
|
||||
#ifdef VCMI_ANDROID
|
||||
void handleSwipeUpdate();
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
@ -754,7 +754,8 @@ void CCastleBuildings::enterBlacksmith(ArtifactID artifactID)
|
||||
}
|
||||
int price = CGI->arth->artifacts[artifactID]->price;
|
||||
bool possible = LOCPLINT->cb->getResourceAmount(Res::GOLD) >= price && !hero->hasArt(artifactID);
|
||||
GH.pushInt(new CBlacksmithDialog(possible, CArtHandler::machineIDToCreature(artifactID), artifactID, hero->id));
|
||||
CreatureID cre = artifactID.toArtifact()->warMachine;
|
||||
GH.pushInt(new CBlacksmithDialog(possible, cre, artifactID, hero->id));
|
||||
}
|
||||
|
||||
void CCastleBuildings::enterBuilding(BuildingID building)
|
||||
|
@ -337,7 +337,7 @@ void CSpellWindow::computeSpellsPerArea()
|
||||
spellsCurSite.reserve(mySpells.size());
|
||||
for(const CSpell * spell : mySpells)
|
||||
{
|
||||
if(spell->combatSpell ^ !battleSpellsOnly
|
||||
if(spell->isCombatSpell() ^ !battleSpellsOnly
|
||||
&& ((selectedTab == 4) || spell->school.at((ESpellSchool)selectedTab))
|
||||
)
|
||||
{
|
||||
@ -547,9 +547,9 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
||||
}
|
||||
|
||||
//we will cast a spell
|
||||
if(mySpell->combatSpell && owner->myInt->battleInt && owner->myInt->cb->battleCanCastSpell()) //if battle window is open
|
||||
if(mySpell->isCombatSpell() && owner->myInt->battleInt) //if battle window is open
|
||||
{
|
||||
ESpellCastProblem::ESpellCastProblem problem = owner->myInt->cb->battleCanCastThisSpell(mySpell);
|
||||
ESpellCastProblem::ESpellCastProblem problem = mySpell->canBeCast(owner->myInt->cb.get(), ECastingMode::HERO_CASTING, owner->myHero);
|
||||
switch (problem)
|
||||
{
|
||||
case ESpellCastProblem::OK:
|
||||
|
@ -225,9 +225,13 @@ void CWindowObject::setShadow(bool on)
|
||||
|
||||
void CWindowObject::showAll(SDL_Surface *to)
|
||||
{
|
||||
auto color = LOCPLINT ? LOCPLINT->playerID : PlayerColor(1);
|
||||
if(settings["session"]["spectate"].Bool())
|
||||
color = PlayerColor(1); // TODO: Spectator shouldn't need special code for UI colors
|
||||
|
||||
CIntObject::showAll(to);
|
||||
if ((options & BORDERED) && (pos.h != to->h || pos.w != to->w))
|
||||
CMessage::drawBorder(LOCPLINT ? LOCPLINT->playerID : PlayerColor(1), to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
|
||||
CMessage::drawBorder(color, to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
|
||||
}
|
||||
|
||||
void CWindowObject::close()
|
||||
|
@ -205,7 +205,7 @@ void CRecruitmentWindow::buy()
|
||||
CreatureID crid = selected->creature->idNumber;
|
||||
SlotID dstslot = dst-> getSlotFor(crid);
|
||||
|
||||
if(!dstslot.validSlot() && !vstd::contains(CGI->arth->bigArtifacts,CGI->arth->creatureToMachineID(crid))) //no available slot
|
||||
if(!dstslot.validSlot() && (selected->creature->warMachine == ArtifactID::NONE)) //no available slot
|
||||
{
|
||||
std::string txt;
|
||||
if(dst->ID == Obj::HERO)
|
||||
|
@ -335,6 +335,8 @@ void CRClickPopup::close()
|
||||
void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCompsInfo &comps)
|
||||
{
|
||||
PlayerColor player = LOCPLINT ? LOCPLINT->playerID : PlayerColor(1); //if no player, then use blue
|
||||
if(settings["session"]["spectate"].Bool())//TODO: there must be better way to implement this
|
||||
player = PlayerColor(1);
|
||||
|
||||
CSimpleWindow * temp = new CInfoWindow(txt, player, comps);
|
||||
temp->center(Point(GH.current->motion)); //center on mouse
|
||||
|
@ -17,22 +17,26 @@
|
||||
"catapult":
|
||||
{
|
||||
"index" : 3,
|
||||
"type" : ["HERO"]
|
||||
"type" : ["HERO"],
|
||||
"warMachine" : "catapult"
|
||||
},
|
||||
"ballista":
|
||||
{
|
||||
"index" : 4,
|
||||
"type" : ["HERO"]
|
||||
"type" : ["HERO"],
|
||||
"warMachine" : "ballista"
|
||||
},
|
||||
"ammoCart":
|
||||
{
|
||||
"index" : 5,
|
||||
"type" : ["HERO"]
|
||||
"type" : ["HERO"],
|
||||
"warMachine" : "ammoCart"
|
||||
},
|
||||
"firstAidTent":
|
||||
{
|
||||
"index" : 6,
|
||||
"type" : ["HERO"]
|
||||
"type" : ["HERO"],
|
||||
"warMachine" : "firstAidTent"
|
||||
},
|
||||
"centaurAxe":
|
||||
{
|
||||
@ -1333,7 +1337,7 @@
|
||||
"subtype" : 1,
|
||||
"val" : 0,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
],
|
||||
"index" : 93,
|
||||
"type" : ["HERO"]
|
||||
|
@ -4,7 +4,7 @@
|
||||
"title" : "VCMI artifact format",
|
||||
"description" : "Format used to define new artifacts in VCMI",
|
||||
"required" : [ "class", "text", "type", "value" ],
|
||||
|
||||
|
||||
"definitions" : {
|
||||
"growingBonusList" : {
|
||||
"type" : "array",
|
||||
@ -117,6 +117,12 @@
|
||||
"value": {
|
||||
"type":"number",
|
||||
"description": "Cost of this artifact, in gold"
|
||||
},
|
||||
"warMachine":
|
||||
{
|
||||
"type":"string",
|
||||
"description": "Creature id to use on battle field. If set, this artifact is war machine"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,8 @@
|
||||
"$schema": "http://json-schema.org/draft-04/schema",
|
||||
"required" : [ "general", "video", "adventure", "pathfinder", "battle", "server", "logging", "launcher" ],
|
||||
"definitions" : {
|
||||
"logLevelEnum" : {
|
||||
"type" : "string",
|
||||
"logLevelEnum" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "trace", "debug", "info", "warn", "error" ]
|
||||
}
|
||||
},
|
||||
@ -17,7 +17,7 @@
|
||||
"type" : "object",
|
||||
"default": {},
|
||||
"additionalProperties" : false,
|
||||
"required" : [ "playerName", "showfps", "music", "sound", "encoding", "swipe" ],
|
||||
"required" : [ "playerName", "showfps", "music", "sound", "encoding", "swipe", "saveRandomMaps" ],
|
||||
"properties" : {
|
||||
"playerName" : {
|
||||
"type":"string",
|
||||
@ -41,8 +41,12 @@
|
||||
},
|
||||
"swipe" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
"default" : true
|
||||
},
|
||||
"saveRandomMaps" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
}
|
||||
}
|
||||
},
|
||||
"video" : {
|
||||
|
@ -55,7 +55,7 @@ void CSettingsView::loadSettings()
|
||||
ui->comboBoxEnemyAI->setCurrentIndex(enemyAIIndex);
|
||||
ui->comboBoxPlayerAI->setCurrentIndex(playerAIIndex);
|
||||
|
||||
ui->spinBoxNetworkPort->setValue(settings["server"]["port"].Float());
|
||||
ui->spinBoxNetworkPort->setValue(settings["server"]["port"].Integer());
|
||||
|
||||
ui->comboBoxAutoCheck->setCurrentIndex(settings["launcher"]["autoCheckRepositories"].Bool());
|
||||
// all calls to plainText will trigger textChanged() signal overwriting config. Create backup before editing widget
|
||||
|
@ -432,22 +432,36 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp
|
||||
if(!creatureBank)
|
||||
{
|
||||
//Checks if hero has artifact and create appropriate stack
|
||||
auto handleWarMachine= [&](int side, ArtifactPosition artslot, CreatureID cretype, BattleHex hex)
|
||||
auto handleWarMachine= [&](int side, ArtifactPosition artslot, BattleHex hex)
|
||||
{
|
||||
if(heroes[side] && heroes[side]->getArt(artslot))
|
||||
stacks.push_back(curB->generateNewStack(CStackBasicDescriptor(cretype, 1), !side, SlotID::WAR_MACHINES_SLOT, hex));
|
||||
const CArtifactInstance * warMachineArt = heroes[side]->getArt(artslot);
|
||||
|
||||
if(nullptr != warMachineArt)
|
||||
{
|
||||
CreatureID cre = warMachineArt->artType->warMachine;
|
||||
|
||||
if(cre != CreatureID::NONE)
|
||||
stacks.push_back(curB->generateNewStack(CStackBasicDescriptor(cre, 1), !side, SlotID::WAR_MACHINES_SLOT, hex));
|
||||
}
|
||||
};
|
||||
|
||||
handleWarMachine(0, ArtifactPosition::MACH1, CreatureID::BALLISTA, 52);
|
||||
handleWarMachine(0, ArtifactPosition::MACH2, CreatureID::AMMO_CART, 18);
|
||||
handleWarMachine(0, ArtifactPosition::MACH3, CreatureID::FIRST_AID_TENT, 154);
|
||||
if(town && town->hasFort())
|
||||
handleWarMachine(0, ArtifactPosition::MACH4, CreatureID::CATAPULT, 120);
|
||||
if(heroes[0])
|
||||
{
|
||||
|
||||
if(!town) //defending hero shouldn't receive ballista (bug #551)
|
||||
handleWarMachine(1, ArtifactPosition::MACH1, CreatureID::BALLISTA, 66);
|
||||
handleWarMachine(1, ArtifactPosition::MACH2, CreatureID::AMMO_CART, 32);
|
||||
handleWarMachine(1, ArtifactPosition::MACH3, CreatureID::FIRST_AID_TENT, 168);
|
||||
handleWarMachine(0, ArtifactPosition::MACH1, 52);
|
||||
handleWarMachine(0, ArtifactPosition::MACH2, 18);
|
||||
handleWarMachine(0, ArtifactPosition::MACH3, 154);
|
||||
if(town && town->hasFort())
|
||||
handleWarMachine(0, ArtifactPosition::MACH4, 120);
|
||||
}
|
||||
|
||||
if(heroes[1])
|
||||
{
|
||||
if(!town) //defending hero shouldn't receive ballista (bug #551)
|
||||
handleWarMachine(1, ArtifactPosition::MACH1, 66);
|
||||
handleWarMachine(1, ArtifactPosition::MACH2, 32);
|
||||
handleWarMachine(1, ArtifactPosition::MACH3, 168);
|
||||
}
|
||||
}
|
||||
//war machines added
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "VCMI_Lib.h"
|
||||
#include "CModHandler.h"
|
||||
#include "CCreatureHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "mapObjects/MapObjects.h"
|
||||
#include "NetPacksBase.h"
|
||||
@ -61,14 +62,21 @@ const std::string & CArtifact::EventText() const
|
||||
return eventText;
|
||||
}
|
||||
|
||||
bool CArtifact::isBig () const
|
||||
bool CArtifact::isBig() const
|
||||
{
|
||||
return VLC->arth->isBigArtifact(id);
|
||||
return warMachine != CreatureID::NONE;
|
||||
}
|
||||
|
||||
bool CArtifact::isTradable () const
|
||||
bool CArtifact::isTradable() const
|
||||
{
|
||||
return VLC->arth->isTradableArtifact(id);
|
||||
switch(id)
|
||||
{
|
||||
case ArtifactID::SPELLBOOK:
|
||||
case ArtifactID::GRAIL:
|
||||
return false;
|
||||
default:
|
||||
return !isBig();
|
||||
}
|
||||
}
|
||||
|
||||
CArtifact::CArtifact()
|
||||
@ -120,6 +128,26 @@ void CArtifact::addNewBonus(const std::shared_ptr<Bonus>& b)
|
||||
CBonusSystemNode::addNewBonus(b);
|
||||
}
|
||||
|
||||
void CArtifact::fillWarMachine()
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case ArtifactID::CATAPULT:
|
||||
warMachine = CreatureID::CATAPULT;
|
||||
break;
|
||||
case ArtifactID::BALLISTA:
|
||||
warMachine = CreatureID::BALLISTA;
|
||||
break;
|
||||
case ArtifactID::FIRST_AID_TENT:
|
||||
warMachine = CreatureID::FIRST_AID_TENT;
|
||||
break;
|
||||
case ArtifactID::AMMO_CART:
|
||||
warMachine = CreatureID::AMMO_CART;
|
||||
break;
|
||||
}
|
||||
warMachine = CreatureID::NONE; //this artifact is not a creature
|
||||
}
|
||||
|
||||
void CGrowingArtifact::levelUpArtifact (CArtifactInstance * art)
|
||||
{
|
||||
auto b = std::make_shared<Bonus>();
|
||||
@ -146,11 +174,6 @@ void CGrowingArtifact::levelUpArtifact (CArtifactInstance * art)
|
||||
|
||||
CArtHandler::CArtHandler()
|
||||
{
|
||||
//VLC->arth = this;
|
||||
|
||||
// War machines are the default big artifacts.
|
||||
for (ArtifactID i = ArtifactID::CATAPULT; i <= ArtifactID::FIRST_AID_TENT; i.advance(1))
|
||||
bigArtifacts.insert(i);
|
||||
}
|
||||
|
||||
CArtHandler::~CArtHandler()
|
||||
@ -310,6 +333,19 @@ CArtifact * CArtHandler::loadFromJson(const JsonNode & node, const std::string &
|
||||
auto bonus = JsonUtils::parseBonus(b);
|
||||
art->addNewBonus(bonus);
|
||||
}
|
||||
|
||||
const JsonNode & warMachine = node["warMachine"];
|
||||
if(warMachine.getType() == JsonNode::DATA_STRING && warMachine.String() != "")
|
||||
{
|
||||
VLC->modh->identifiers.requestIdentifier("creature", warMachine, [=](si32 id)
|
||||
{
|
||||
art->warMachine = CreatureID(id);
|
||||
|
||||
//this assumes that creature object is stored before registration
|
||||
VLC->creh->creatures.at(id)->warMachine = art->id;
|
||||
});
|
||||
}
|
||||
|
||||
return art;
|
||||
}
|
||||
|
||||
@ -453,47 +489,6 @@ void CArtHandler::loadGrowingArt(CGrowingArtifact * art, const JsonNode & node)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: use bimap
|
||||
ArtifactID CArtHandler::creatureToMachineID(CreatureID id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case CreatureID::CATAPULT: //Catapult
|
||||
return ArtifactID::CATAPULT;
|
||||
break;
|
||||
case CreatureID::BALLISTA: //Ballista
|
||||
return ArtifactID::BALLISTA;
|
||||
break;
|
||||
case CreatureID::FIRST_AID_TENT: //First Aid tent
|
||||
return ArtifactID::FIRST_AID_TENT;
|
||||
break;
|
||||
case CreatureID::AMMO_CART: //Ammo cart
|
||||
return ArtifactID::AMMO_CART;
|
||||
break;
|
||||
}
|
||||
return ArtifactID::NONE; //this creature is not artifact
|
||||
}
|
||||
|
||||
CreatureID CArtHandler::machineIDToCreature(ArtifactID id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case ArtifactID::CATAPULT:
|
||||
return CreatureID::CATAPULT;
|
||||
break;
|
||||
case ArtifactID::BALLISTA:
|
||||
return CreatureID::BALLISTA;
|
||||
break;
|
||||
case ArtifactID::FIRST_AID_TENT:
|
||||
return CreatureID::FIRST_AID_TENT;
|
||||
break;
|
||||
case ArtifactID::AMMO_CART:
|
||||
return CreatureID::AMMO_CART;
|
||||
break;
|
||||
}
|
||||
return CreatureID::NONE; //this artifact is not a creature
|
||||
}
|
||||
|
||||
ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags, std::function<bool(ArtifactID)> accepts)
|
||||
{
|
||||
auto getAllowedArts = [&](std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, CArtifact::EartClass flag)
|
||||
@ -635,22 +630,6 @@ bool CArtHandler::legalArtifact(ArtifactID id)
|
||||
art->aClass <= CArtifact::ART_RELIC);
|
||||
}
|
||||
|
||||
bool CArtHandler::isTradableArtifact(ArtifactID id) const
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case ArtifactID::SPELLBOOK:
|
||||
case ArtifactID::GRAIL:
|
||||
case ArtifactID::CATAPULT:
|
||||
case ArtifactID::BALLISTA:
|
||||
case ArtifactID::AMMO_CART:
|
||||
case ArtifactID::FIRST_AID_TENT:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void CArtHandler::initAllowedArtifactsList(const std::vector<bool> &allowed)
|
||||
{
|
||||
allowedArtifacts.clear();
|
||||
|
@ -61,6 +61,7 @@ public:
|
||||
std::vector<CArtifact *> constituentOf; // Reverse map of constituents - combined arts that include this art
|
||||
EartClass aClass;
|
||||
ArtifactID id;
|
||||
CreatureID warMachine;
|
||||
|
||||
const std::string &Name() const; //getter
|
||||
const std::string &Description() const; //getter
|
||||
@ -84,12 +85,23 @@ public:
|
||||
{
|
||||
h & identifier;
|
||||
}
|
||||
|
||||
if(version >= 771)
|
||||
{
|
||||
h & warMachine;
|
||||
}
|
||||
else if(!h.saving)
|
||||
{
|
||||
fillWarMachine();
|
||||
}
|
||||
}
|
||||
|
||||
CArtifact();
|
||||
~CArtifact();
|
||||
|
||||
friend class CArtHandler;
|
||||
private:
|
||||
void fillWarMachine();
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CGrowingArtifact : public CArtifact //for example commander artifacts getting bonuses after battle
|
||||
@ -213,7 +225,6 @@ public:
|
||||
|
||||
std::vector< ConstTransitivePtr<CArtifact> > artifacts;
|
||||
std::vector<CArtifact *> allowedArtifacts;
|
||||
std::set<ArtifactID> bigArtifacts; // Artifacts that cannot be moved to backpack, e.g. war machines.
|
||||
std::set<ArtifactID> growingArtifacts;
|
||||
|
||||
void addBonuses(CArtifact *art, const JsonNode &bonusList);
|
||||
@ -231,13 +242,7 @@ public:
|
||||
ArtifactID pickRandomArtifact(CRandomGenerator & rand, int flags, std::function<bool(ArtifactID)> accepts);
|
||||
|
||||
bool legalArtifact(ArtifactID id);
|
||||
//void getAllowedArts(std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, int flag);
|
||||
//void getAllowed(std::vector<ConstTransitivePtr<CArtifact> > &out, int flags);
|
||||
bool isBigArtifact (ArtifactID artID) const {return bigArtifacts.find(artID) != bigArtifacts.end();}
|
||||
bool isTradableArtifact (ArtifactID id) const;
|
||||
void initAllowedArtifactsList(const std::vector<bool> &allowed); //allowed[art_id] -> 0 if not allowed, 1 if allowed
|
||||
static ArtifactID creatureToMachineID(CreatureID id);
|
||||
static CreatureID machineIDToCreature(ArtifactID id);
|
||||
void makeItCreatureArt (CArtifact * a, bool onlyCreature = true);
|
||||
void makeItCreatureArt (ArtifactID aid, bool onlyCreature = true);
|
||||
void makeItCommanderArt (CArtifact * a, bool onlyCommander = true);
|
||||
@ -264,7 +269,6 @@ public:
|
||||
{
|
||||
h & artifacts & allowedArtifacts & treasures & minors & majors & relics
|
||||
& growingArtifacts;
|
||||
//if(!h.saving) sortArts();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -237,7 +237,7 @@ const CGTownInstance * CBattleInfoEssentials::battleGetDefendedTown() const
|
||||
BattlePerspective::BattlePerspective CBattleInfoEssentials::battleGetMySide() const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(BattlePerspective::INVALID);
|
||||
if(!player)
|
||||
if(!player || player.get().isSpectator())
|
||||
return BattlePerspective::ALL_KNOWING;
|
||||
if(*player == getBattle()->sides[0].color)
|
||||
return BattlePerspective::LEFT_SIDE;
|
||||
@ -1708,59 +1708,6 @@ std::vector<BattleHex> CBattleInfoCallback::getAttackableBattleHexes() const
|
||||
return attackableBattleHexes;
|
||||
}
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
|
||||
if(caster == nullptr)
|
||||
{
|
||||
logGlobal->errorStream() << "CBattleInfoCallback::battleCanCastThisSpell: no spellcaster.";
|
||||
return ESpellCastProblem::INVALID;
|
||||
}
|
||||
const PlayerColor player = caster->getOwner();
|
||||
const si8 side = playerToSide(player);
|
||||
|
||||
if(side < 0)
|
||||
return ESpellCastProblem::INVALID;
|
||||
|
||||
if(!battleDoWeKnowAbout(side))
|
||||
return ESpellCastProblem::INVALID;
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem genProblem = battleCanCastSpell(caster, mode);
|
||||
if(genProblem != ESpellCastProblem::OK)
|
||||
return genProblem;
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case ECastingMode::HERO_CASTING:
|
||||
{
|
||||
const CGHeroInstance * castingHero = dynamic_cast<const CGHeroInstance *>(caster);//todo: unify hero|creature spell cost
|
||||
if(!castingHero)
|
||||
{
|
||||
logGlobal->error("battleCanCastThisSpell: invalid caster");
|
||||
return ESpellCastProblem::INVALID;
|
||||
}
|
||||
|
||||
if(!castingHero->getArt(ArtifactPosition::SPELLBOOK))
|
||||
return ESpellCastProblem::NO_SPELLBOOK;
|
||||
if(!castingHero->canCastThisSpell(spell))
|
||||
return ESpellCastProblem::HERO_DOESNT_KNOW_SPELL;
|
||||
if(castingHero->mana < battleGetSpellCost(spell, castingHero)) //not enough mana
|
||||
return ESpellCastProblem::NOT_ENOUGH_MANA;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(!spell->combatSpell)
|
||||
return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
|
||||
|
||||
//effect like Recanter's Cloak. Blocks also passive casting.
|
||||
//TODO: check creature abilities to block
|
||||
if(battleMaxSpellLevel(side) < spell->level)
|
||||
return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED;
|
||||
|
||||
return spell->canBeCast(this, mode, caster);
|
||||
}
|
||||
|
||||
ui32 CBattleInfoCallback::battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(-1);
|
||||
@ -1789,22 +1736,6 @@ ui32 CBattleInfoCallback::battleGetSpellCost(const CSpell * sp, const CGHeroInst
|
||||
return ret - manaReduction + manaIncrease;
|
||||
}
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpellHere(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
|
||||
if(caster == nullptr)
|
||||
{
|
||||
logGlobal->errorStream() << "CBattleInfoCallback::battleCanCastThisSpellHere: no spellcaster.";
|
||||
return ESpellCastProblem::INVALID;
|
||||
}
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem problem = battleCanCastThisSpell(caster, spell, mode);
|
||||
if(problem != ESpellCastProblem::OK)
|
||||
return problem;
|
||||
|
||||
return spell->canBeCastAt(this, caster, mode, dest);
|
||||
}
|
||||
|
||||
const CStack * CBattleInfoCallback::getStackIf(std::function<bool(const CStack*)> pred) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(nullptr);
|
||||
@ -1887,7 +1818,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c
|
||||
|
||||
if(subject->hasBonus(Selector::source(Bonus::SPELL_EFFECT, spellID), Selector::all, cachingStr.str())
|
||||
//TODO: this ability has special limitations
|
||||
|| battleCanCastThisSpellHere(subject, spellID.toSpell(), ECastingMode::CREATURE_ACTIVE_CASTING, subject->position) != ESpellCastProblem::OK)
|
||||
|| spellID.toSpell()->canBeCastAt(this, subject, ECastingMode::CREATURE_ACTIVE_CASTING, subject->position) != ESpellCastProblem::OK)
|
||||
continue;
|
||||
|
||||
switch (spellID)
|
||||
@ -2127,18 +2058,6 @@ ReachabilityInfo::Parameters::Parameters(const CStack *Stack)
|
||||
knownAccessible = stack->getHexes();
|
||||
}
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem CPlayerBattleCallback::battleCanCastThisSpell(const CSpell * spell) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);
|
||||
ASSERT_IF_CALLED_WITH_PLAYER
|
||||
|
||||
const ISpellCaster * hero = battleGetMyHero();
|
||||
if(hero == nullptr)
|
||||
return ESpellCastProblem::INVALID;
|
||||
else
|
||||
return CBattleInfoCallback::battleCanCastThisSpell(hero, spell, ECastingMode::HERO_CASTING);
|
||||
}
|
||||
|
||||
bool CPlayerBattleCallback::battleCanFlee() const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(false);
|
||||
@ -2169,26 +2088,6 @@ int CPlayerBattleCallback::battleGetSurrenderCost() const
|
||||
return CBattleInfoCallback::battleGetSurrenderCost(*player);
|
||||
}
|
||||
|
||||
bool CPlayerBattleCallback::battleCanCastSpell(ESpellCastProblem::ESpellCastProblem *outProblem /*= nullptr*/) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(false);
|
||||
ASSERT_IF_CALLED_WITH_PLAYER
|
||||
|
||||
const CGHeroInstance * hero = battleGetMyHero();
|
||||
if(!hero)
|
||||
{
|
||||
if(outProblem)
|
||||
*outProblem = ESpellCastProblem::NO_HERO_TO_CAST_SPELL;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto problem = CBattleInfoCallback::battleCanCastSpell(hero, ECastingMode::HERO_CASTING);
|
||||
if(outProblem)
|
||||
*outProblem = problem;
|
||||
|
||||
return problem == ESpellCastProblem::OK;
|
||||
}
|
||||
|
||||
const CGHeroInstance * CPlayerBattleCallback::battleGetMyHero() const
|
||||
{
|
||||
return CBattleInfoEssentials::battleGetFightingHero(battleGetMySide());
|
||||
|
@ -293,8 +293,6 @@ public:
|
||||
si8 battleMaxSpellLevel(ui8 side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
|
||||
ui32 battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
|
||||
ESpellCastProblem::ESpellCastProblem battleCanCastSpell(const ISpellCaster * caster, ECastingMode::ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell
|
||||
ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode) const; //checks if given player can cast given spell
|
||||
ESpellCastProblem::ESpellCastProblem battleCanCastThisSpellHere(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const; //checks if given player can cast given spell at given tile in given mode
|
||||
|
||||
SpellID battleGetRandomStackSpell(CRandomGenerator & rand, const CStack * stack, ERandomSpell mode) const;
|
||||
SpellID getRandomBeneficialSpell(CRandomGenerator & rand, const CStack * subject) const;
|
||||
@ -336,11 +334,9 @@ class DLL_LINKAGE CPlayerBattleCallback : public CBattleInfoCallback
|
||||
public:
|
||||
bool battleCanFlee() const; //returns true if caller can flee from the battle
|
||||
TStacks battleGetStacks(EStackOwnership whose = MINE_AND_ENEMY, bool onlyAlive = true) const; //returns stacks on battlefield
|
||||
ESpellCastProblem::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell) const; //determines if given spell can be cast (and returns problem description)
|
||||
|
||||
int battleGetSurrenderCost() const; //returns cost of surrendering battle, -1 if surrendering is not possible
|
||||
|
||||
bool battleCanCastSpell(ESpellCastProblem::ESpellCastProblem *outProblem = nullptr) const; //returns true, if caller can cast a spell. If not, if pointer is given via arg, the reason will be written.
|
||||
const CGHeroInstance * battleGetMyHero() const;
|
||||
InfoAboutHero battleGetEnemyHero() const;
|
||||
};
|
||||
|
@ -152,6 +152,26 @@ void CCreature::setId(CreatureID ID)
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
|
||||
void CCreature::fillWarMachine()
|
||||
{
|
||||
switch (idNumber)
|
||||
{
|
||||
case CreatureID::CATAPULT: //Catapult
|
||||
warMachine = ArtifactID::CATAPULT;
|
||||
break;
|
||||
case CreatureID::BALLISTA: //Ballista
|
||||
warMachine = ArtifactID::BALLISTA;
|
||||
break;
|
||||
case CreatureID::FIRST_AID_TENT: //First Aid tent
|
||||
warMachine = ArtifactID::FIRST_AID_TENT;
|
||||
break;
|
||||
case CreatureID::AMMO_CART: //Ammo cart
|
||||
warMachine = ArtifactID::AMMO_CART;
|
||||
break;
|
||||
}
|
||||
warMachine = ArtifactID::NONE; //this creature is not artifact
|
||||
}
|
||||
|
||||
static void AddAbility(CCreature *cre, const JsonVector &ability_vec)
|
||||
{
|
||||
auto nsf = std::make_shared<Bonus>();
|
||||
|
@ -98,6 +98,8 @@ public:
|
||||
}
|
||||
} sounds;
|
||||
|
||||
ArtifactID warMachine;
|
||||
|
||||
bool isItNativeTerrain(int terrain) const;
|
||||
bool isDoubleWide() const; //returns true if unit is double wide on battlefield
|
||||
bool isFlying() const; //returns true if it is a flying unit
|
||||
@ -142,9 +144,20 @@ public:
|
||||
{
|
||||
h & identifier;
|
||||
}
|
||||
if(version >= 771)
|
||||
{
|
||||
h & warMachine;
|
||||
}
|
||||
else if(!h.saving)
|
||||
{
|
||||
fillWarMachine();
|
||||
}
|
||||
}
|
||||
|
||||
CCreature();
|
||||
|
||||
private:
|
||||
void fillWarMachine();
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CCreatureHandler : public IHandlerBase
|
||||
|
@ -595,7 +595,7 @@ const CMapHeader * CGameInfoCallback::getMapHeader() const
|
||||
|
||||
bool CGameInfoCallback::hasAccess(boost::optional<PlayerColor> playerId) const
|
||||
{
|
||||
return !player || gs->getPlayerRelations( *playerId, *player ) != PlayerRelations::ENEMIES;
|
||||
return !player || player.get().isSpectator() || gs->getPlayerRelations( *playerId, *player ) != PlayerRelations::ENEMIES;
|
||||
}
|
||||
|
||||
EPlayerStatus::EStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bool verbose) const
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include "StartInfo.h"
|
||||
#include "NetPacks.h"
|
||||
#include "registerTypes/RegisterTypes.h"
|
||||
#include "mapping/CMapInfo.h"
|
||||
#include "BattleInfo.h"
|
||||
#include "JsonNode.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
@ -24,8 +23,10 @@
|
||||
#include "rmg/CMapGenerator.h"
|
||||
#include "CStopWatch.h"
|
||||
#include "mapping/CMapEditManager.h"
|
||||
#include "mapping/CMapService.h"
|
||||
#include "serializer/CTypeList.h"
|
||||
#include "serializer/CMemorySerializer.h"
|
||||
#include "VCMIDirs.h"
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
@ -698,7 +699,7 @@ CGameState::~CGameState()
|
||||
ptr.second.dellNull();
|
||||
}
|
||||
|
||||
void CGameState::init(StartInfo * si)
|
||||
void CGameState::init(StartInfo * si, bool allowSavingRandomMap)
|
||||
{
|
||||
logGlobal->infoStream() << "\tUsing random seed: "<< si->seedToBeUsed;
|
||||
getRandomGenerator().setSeed(si->seedToBeUsed);
|
||||
@ -709,7 +710,7 @@ void CGameState::init(StartInfo * si)
|
||||
switch(scenarioOps->mode)
|
||||
{
|
||||
case StartInfo::NEW_GAME:
|
||||
initNewGame();
|
||||
initNewGame(allowSavingRandomMap);
|
||||
break;
|
||||
case StartInfo::CAMPAIGN:
|
||||
initCampaign();
|
||||
@ -771,7 +772,7 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
}
|
||||
|
||||
void CGameState::initNewGame()
|
||||
void CGameState::initNewGame(bool allowSavingRandomMap)
|
||||
{
|
||||
if(scenarioOps->createRandomMap())
|
||||
{
|
||||
@ -780,8 +781,37 @@ void CGameState::initNewGame()
|
||||
|
||||
// Gen map
|
||||
CMapGenerator mapGenerator;
|
||||
map = mapGenerator.generate(scenarioOps->mapGenOptions.get(), scenarioOps->seedToBeUsed).release();
|
||||
|
||||
std::unique_ptr<CMap> randomMap = mapGenerator.generate(scenarioOps->mapGenOptions.get(), scenarioOps->seedToBeUsed);
|
||||
|
||||
if(allowSavingRandomMap)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto path = VCMIDirs::get().userCachePath() / "RandomMaps";
|
||||
boost::filesystem::create_directories(path);
|
||||
|
||||
std::shared_ptr<CMapGenOptions> options = scenarioOps->mapGenOptions;
|
||||
|
||||
const std::string templateName = options->getMapTemplate()->getName();
|
||||
const ui32 seed = scenarioOps->seedToBeUsed;
|
||||
|
||||
const std::string fileName = boost::str(boost::format("%s_%d.vmap") % templateName % seed );
|
||||
const auto fullPath = path / fileName;
|
||||
|
||||
CMapService::saveMap(randomMap, fullPath);
|
||||
|
||||
logGlobal->info("Random map has been saved to:");
|
||||
logGlobal->info(fullPath.string());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
logGlobal->error("Saving random map failed with exception");
|
||||
handleException();
|
||||
}
|
||||
}
|
||||
|
||||
map = randomMap.release();
|
||||
// Update starting options
|
||||
for(int i = 0; i < map->players.size(); ++i)
|
||||
{
|
||||
@ -809,7 +839,8 @@ void CGameState::initNewGame()
|
||||
else
|
||||
{
|
||||
logGlobal->infoStream() << "Open map file: " << scenarioOps->mapname;
|
||||
map = CMapService::loadMap(scenarioOps->mapname).release();
|
||||
const ResourceID mapURI(scenarioOps->mapname, EResType::MAP);
|
||||
map = CMapService::loadMap(mapURI).release();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2177,6 +2208,9 @@ bool CGameState::isVisible(int3 pos, PlayerColor player)
|
||||
{
|
||||
if(player == PlayerColor::NEUTRAL)
|
||||
return false;
|
||||
if(player.isSpectator())
|
||||
return true;
|
||||
|
||||
return getPlayerTeam(player)->fogOfWarMap[pos.x][pos.y][pos.z];
|
||||
}
|
||||
|
||||
|
@ -201,7 +201,7 @@ public:
|
||||
CGameState();
|
||||
virtual ~CGameState();
|
||||
|
||||
void init(StartInfo * si);
|
||||
void init(StartInfo * si, bool allowSavingRandomMap = false);
|
||||
|
||||
ConstTransitivePtr<StartInfo> scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized)
|
||||
PlayerColor currentPlayer; //ID of player currently having turn
|
||||
@ -283,7 +283,7 @@ private:
|
||||
|
||||
// ----- initialization -----
|
||||
|
||||
void initNewGame();
|
||||
void initNewGame(bool allowSavingRandomMap);
|
||||
void initCampaign();
|
||||
void initDuel();
|
||||
void checkMapChecksum();
|
||||
|
@ -584,7 +584,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
|
||||
VLC->modh->identifiers.requestIdentifier("creature", source["warMachine"],
|
||||
[&town](si32 creature)
|
||||
{
|
||||
town.warMachine = CArtHandler::creatureToMachineID(CreatureID(creature));
|
||||
town.warMachine = CreatureID(creature).toCreature()->warMachine;
|
||||
});
|
||||
|
||||
town.moatDamage = source["moatDamage"].Float();
|
||||
|
@ -29,6 +29,7 @@ const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(-3);
|
||||
const SlotID SlotID::WAR_MACHINES_SLOT = SlotID(-4);
|
||||
const SlotID SlotID::ARROW_TOWERS_SLOT = SlotID(-5);
|
||||
|
||||
const PlayerColor PlayerColor::SPECTATOR = PlayerColor(252);
|
||||
const PlayerColor PlayerColor::CANNOT_DETERMINE = PlayerColor(253);
|
||||
const PlayerColor PlayerColor::UNFLAGGABLE = PlayerColor(254);
|
||||
const PlayerColor PlayerColor::NEUTRAL = PlayerColor(255);
|
||||
@ -72,6 +73,11 @@ bool PlayerColor::isValidPlayer() const
|
||||
return num < PLAYER_LIMIT_I;
|
||||
}
|
||||
|
||||
bool PlayerColor::isSpectator() const
|
||||
{
|
||||
return num == 252;
|
||||
}
|
||||
|
||||
std::string PlayerColor::getStr(bool L10n) const
|
||||
{
|
||||
std::string ret = "unnamed";
|
||||
|
@ -257,12 +257,14 @@ class PlayerColor : public BaseForID<PlayerColor, ui8>
|
||||
PLAYER_LIMIT_I = 8
|
||||
};
|
||||
|
||||
DLL_LINKAGE static const PlayerColor SPECTATOR; //252
|
||||
DLL_LINKAGE static const PlayerColor CANNOT_DETERMINE; //253
|
||||
DLL_LINKAGE static const PlayerColor UNFLAGGABLE; //254 - neutral objects (pandora, banks)
|
||||
DLL_LINKAGE static const PlayerColor NEUTRAL; //255
|
||||
DLL_LINKAGE static const PlayerColor PLAYER_LIMIT; //player limit per map
|
||||
|
||||
DLL_LINKAGE bool isValidPlayer() const; //valid means < PLAYER_LIMIT (especially non-neutral)
|
||||
DLL_LINKAGE bool isSpectator() const;
|
||||
|
||||
DLL_LINKAGE std::string getStr(bool L10n = false) const;
|
||||
DLL_LINKAGE std::string getStrCap(bool L10n = false) const;
|
||||
|
@ -19,40 +19,63 @@
|
||||
struct ServerReady
|
||||
{
|
||||
bool ready;
|
||||
boost::interprocess::interprocess_mutex mutex;
|
||||
boost::interprocess::interprocess_condition cond;
|
||||
uint16_t port; //ui16?
|
||||
boost::interprocess::interprocess_mutex mutex;
|
||||
boost::interprocess::interprocess_condition cond;
|
||||
|
||||
ServerReady()
|
||||
{
|
||||
ready = false;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
void setToTrueAndNotify()
|
||||
void waitTillReady()
|
||||
{
|
||||
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> slock(mutex);
|
||||
while(!ready)
|
||||
{
|
||||
cond.wait(slock);
|
||||
}
|
||||
}
|
||||
|
||||
void setToReadyAndNotify(const uint16_t Port)
|
||||
{
|
||||
{
|
||||
boost::unique_lock<boost::interprocess::interprocess_mutex> lock(mutex);
|
||||
boost::unique_lock<boost::interprocess::interprocess_mutex> lock(mutex);
|
||||
ready = true;
|
||||
port = Port;
|
||||
}
|
||||
cond.notify_all();
|
||||
}
|
||||
};
|
||||
|
||||
struct SharedMem
|
||||
struct SharedMemory
|
||||
{
|
||||
const char * name;
|
||||
boost::interprocess::shared_memory_object smo;
|
||||
boost::interprocess::mapped_region *mr;
|
||||
ServerReady *sr;
|
||||
boost::interprocess::mapped_region * mr;
|
||||
ServerReady * sr;
|
||||
|
||||
SharedMem() //c-tor
|
||||
:smo(boost::interprocess::open_or_create,"vcmi_memory",boost::interprocess::read_write)
|
||||
SharedMemory(std::string Name, bool initialize = false)
|
||||
: name(Name.c_str())
|
||||
{
|
||||
if(initialize)
|
||||
{
|
||||
//if the application has previously crashed, the memory may not have been removed. to avoid problems - try to destroy it
|
||||
boost::interprocess::shared_memory_object::remove(name);
|
||||
}
|
||||
smo = boost::interprocess::shared_memory_object(boost::interprocess::open_or_create, name, boost::interprocess::read_write);
|
||||
smo.truncate(sizeof(ServerReady));
|
||||
mr = new boost::interprocess::mapped_region(smo,boost::interprocess::read_write);
|
||||
sr = new(mr->get_address())ServerReady();
|
||||
if(initialize)
|
||||
sr = new(mr->get_address())ServerReady();
|
||||
else
|
||||
sr = reinterpret_cast<ServerReady*>(mr->get_address());
|
||||
};
|
||||
~SharedMem() //d-tor
|
||||
|
||||
~SharedMemory()
|
||||
{
|
||||
delete mr;
|
||||
boost::interprocess::shared_memory_object::remove("vcmi_memory");
|
||||
boost::interprocess::shared_memory_object::remove(name);
|
||||
}
|
||||
};
|
||||
|
@ -152,6 +152,21 @@ struct PlayerBlocked : public CPackForClient
|
||||
}
|
||||
};
|
||||
|
||||
struct PlayerCheated : public CPackForClient
|
||||
{
|
||||
PlayerCheated() : losingCheatCode(false), winningCheatCode(false) {}
|
||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||
|
||||
PlayerColor player;
|
||||
bool losingCheatCode;
|
||||
bool winningCheatCode;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & player & losingCheatCode & winningCheatCode;
|
||||
}
|
||||
};
|
||||
|
||||
struct YourTurn : public CPackForClient
|
||||
{
|
||||
YourTurn(){}
|
||||
@ -1774,6 +1789,13 @@ struct CloseServer : public CPackForServer
|
||||
{}
|
||||
};
|
||||
|
||||
struct LeaveGame : public CPackForServer
|
||||
{
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{}
|
||||
};
|
||||
|
||||
struct EndTurn : public CPackForServer
|
||||
{
|
||||
bool applyGh(CGameHandler *gh);
|
||||
|
@ -1840,6 +1840,15 @@ DLL_LINKAGE void BattleSetStackProperty::applyGs(CGameState *gs)
|
||||
}
|
||||
}
|
||||
|
||||
DLL_LINKAGE void PlayerCheated::applyGs(CGameState *gs)
|
||||
{
|
||||
if(!player.isValidPlayer())
|
||||
return;
|
||||
|
||||
gs->getPlayer(player)->enteredLosingCheatCode = losingCheatCode;
|
||||
gs->getPlayer(player)->enteredWinningCheatCode = winningCheatCode;
|
||||
}
|
||||
|
||||
DLL_LINKAGE void YourTurn::applyGs(CGameState *gs)
|
||||
{
|
||||
gs->currentPlayer = player;
|
||||
|
@ -424,4 +424,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -669,4 +669,4 @@
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -352,34 +352,43 @@ void CGHeroInstance::initArmy(CRandomGenerator & rand, IArmyDescriptor *dst /*=
|
||||
|
||||
int count = rand.nextInt(stack.minAmount, stack.maxAmount);
|
||||
|
||||
if(stack.creature >= CreatureID::CATAPULT &&
|
||||
stack.creature <= CreatureID::ARROW_TOWERS) //war machine
|
||||
const CCreature * creature = stack.creature.toCreature();
|
||||
|
||||
if(creature == nullptr)
|
||||
{
|
||||
logGlobal->error("Hero %s has invalid creature with id %d in initial army", name, stack.creature.toEnum());
|
||||
continue;
|
||||
}
|
||||
|
||||
if(creature->warMachine != ArtifactID::NONE) //war machine
|
||||
{
|
||||
warMachinesGiven++;
|
||||
if(dst != this)
|
||||
continue;
|
||||
|
||||
int slot = -1;
|
||||
ArtifactID aid = ArtifactID::NONE;
|
||||
switch (stack.creature)
|
||||
ArtifactID aid = creature->warMachine;
|
||||
const CArtifact * art = aid.toArtifact();
|
||||
|
||||
if(art != nullptr && !art->possibleSlots.at(ArtBearer::HERO).empty())
|
||||
{
|
||||
case CreatureID::CATAPULT:
|
||||
slot = ArtifactPosition::MACH4;
|
||||
aid = ArtifactID::CATAPULT;
|
||||
break;
|
||||
default:
|
||||
aid = CArtHandler::creatureToMachineID(stack.creature);
|
||||
slot = 9 + aid;
|
||||
break;
|
||||
//TODO: should we try another possible slots?
|
||||
ArtifactPosition slot = art->possibleSlots.at(ArtBearer::HERO).front();
|
||||
|
||||
if(!getArt(slot))
|
||||
putArtifact(slot, CArtifactInstance::createNewArtifactInstance(aid));
|
||||
else
|
||||
logGlobal->warnStream() << "Hero " << name << " already has artifact at " << slot << ", omitting giving " << aid;
|
||||
}
|
||||
auto convSlot = ArtifactPosition(slot);
|
||||
if(!getArt(convSlot))
|
||||
putArtifact(convSlot, CArtifactInstance::createNewArtifactInstance(aid));
|
||||
else
|
||||
logGlobal->warnStream() << "Hero " << name << " already has artifact at " << slot << ", omitting giving " << aid;
|
||||
{
|
||||
logGlobal->error("Hero %s has invalid war machine in initial army", name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dst->setCreature(SlotID(stackNo-warMachinesGiven), stack.creature, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1610,42 +1619,34 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
|
||||
}
|
||||
}
|
||||
|
||||
//primary skills
|
||||
if(handler.saving)
|
||||
{
|
||||
if(handler.saving)
|
||||
{
|
||||
bool haveSkills = false;
|
||||
const bool haveSkills = hasBonus(Selector::type(Bonus::PRIMARY_SKILL).And(Selector::sourceType(Bonus::HERO_BASE_SKILL)));
|
||||
|
||||
for(int i = 0; i < GameConstants::PRIMARY_SKILLS; ++i)
|
||||
{
|
||||
if(valOfBonuses(Selector::typeSubtype(Bonus::PRIMARY_SKILL, i).And(Selector::sourceType(Bonus::HERO_BASE_SKILL))) != 0)
|
||||
{
|
||||
haveSkills = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(haveSkills)
|
||||
{
|
||||
auto primarySkills = handler.enterStruct("primarySkills");
|
||||
|
||||
for(int i = 0; i < GameConstants::PRIMARY_SKILLS; ++i)
|
||||
{
|
||||
int value = valOfBonuses(Selector::typeSubtype(Bonus::PRIMARY_SKILL, i).And(Selector::sourceType(Bonus::HERO_BASE_SKILL)));
|
||||
|
||||
handler.serializeInt(PrimarySkill::names[i], value, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if(haveSkills)
|
||||
{
|
||||
auto primarySkills = handler.enterStruct("primarySkills");
|
||||
|
||||
for(int i = 0; i < GameConstants::PRIMARY_SKILLS; ++i)
|
||||
{
|
||||
int value = 0;
|
||||
int value = valOfBonuses(Selector::typeSubtype(Bonus::PRIMARY_SKILL, i).And(Selector::sourceType(Bonus::HERO_BASE_SKILL)));
|
||||
|
||||
handler.serializeInt(PrimarySkill::names[i], value, 0);
|
||||
if(value != 0)
|
||||
pushPrimSkill(static_cast<PrimarySkill::PrimarySkill>(i), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto primarySkills = handler.enterStruct("primarySkills");
|
||||
|
||||
if(primarySkills.get().getType() == JsonNode::DATA_STRUCT)
|
||||
{
|
||||
for(int i = 0; i < GameConstants::PRIMARY_SKILLS; ++i)
|
||||
{
|
||||
int value = 0;
|
||||
primarySkills->serializeInt(PrimarySkill::names[i], value, 0);
|
||||
pushPrimSkill(static_cast<PrimarySkill::PrimarySkill>(i), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1477,7 +1477,7 @@ std::string CGWitchHut::getHoverText(PlayerColor player) const
|
||||
std::string CGWitchHut::getHoverText(const CGHeroInstance * hero) const
|
||||
{
|
||||
std::string hoverName = getHoverText(hero->tempOwner);
|
||||
if(hero->getSecSkillLevel(SecondarySkill(ability))) //hero knows that ability
|
||||
if(wasVisited(hero->tempOwner) && hero->getSecSkillLevel(SecondarySkill(ability))) //hero knows that ability
|
||||
hoverName += "\n\n" + VLC->generaltexth->allTexts[357]; // (Already learned)
|
||||
return hoverName;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CMapInfo.h"
|
||||
|
||||
#include "../filesystem/ResourceID.h"
|
||||
#include "../StartInfo.h"
|
||||
#include "../GameConstants.h"
|
||||
#include "CMapService.h"
|
||||
@ -58,7 +59,7 @@ CMapInfo::~CMapInfo()
|
||||
void CMapInfo::mapInit(const std::string & fname)
|
||||
{
|
||||
fileURI = fname;
|
||||
mapHeader = CMapService::loadMapHeader(fname);
|
||||
mapHeader = CMapService::loadMapHeader(ResourceID(fname, EResType::MAP));
|
||||
countPlayers();
|
||||
}
|
||||
|
||||
@ -81,3 +82,4 @@ CMapInfo & CMapInfo::operator=(CMapInfo &&tmp)
|
||||
return *this;
|
||||
}
|
||||
|
||||
#undef STEAL
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "../filesystem/CBinaryReader.h"
|
||||
#include "../filesystem/CCompressedStream.h"
|
||||
#include "../filesystem/CMemoryStream.h"
|
||||
#include "../filesystem/CMemoryBuffer.h"
|
||||
|
||||
#include "CMap.h"
|
||||
|
||||
@ -12,24 +13,16 @@
|
||||
#include "MapFormatJson.h"
|
||||
|
||||
|
||||
std::unique_ptr<CMap> CMapService::loadMap(const std::string & name)
|
||||
std::unique_ptr<CMap> CMapService::loadMap(const ResourceID & name)
|
||||
{
|
||||
auto stream = getStreamFromFS(name);
|
||||
std::unique_ptr<CMap> map(getMapLoader(stream)->loadMap());
|
||||
std::unique_ptr<CMapHeader> header(map.get());
|
||||
|
||||
getMapPatcher(name)->patchMapHeader(header);
|
||||
header.release();
|
||||
|
||||
return map;
|
||||
return getMapLoader(stream)->loadMap();
|
||||
}
|
||||
|
||||
std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const std::string & name)
|
||||
std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const ResourceID & name)
|
||||
{
|
||||
auto stream = getStreamFromFS(name);
|
||||
std::unique_ptr<CMapHeader> header = getMapLoader(stream)->loadMapHeader();
|
||||
getMapPatcher(name)->patchMapHeader(header);
|
||||
return header;
|
||||
return getMapLoader(stream)->loadMapHeader();
|
||||
}
|
||||
|
||||
std::unique_ptr<CMap> CMapService::loadMap(const ui8 * buffer, int size, const std::string & name)
|
||||
@ -38,6 +31,7 @@ std::unique_ptr<CMap> CMapService::loadMap(const ui8 * buffer, int size, const s
|
||||
std::unique_ptr<CMap> map(getMapLoader(stream)->loadMap());
|
||||
std::unique_ptr<CMapHeader> header(map.get());
|
||||
|
||||
//might be original campaign and require patch
|
||||
getMapPatcher(name)->patchMapHeader(header);
|
||||
header.release();
|
||||
|
||||
@ -48,13 +42,32 @@ std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const ui8 * buffer, int s
|
||||
{
|
||||
auto stream = getStreamFromMem(buffer, size);
|
||||
std::unique_ptr<CMapHeader> header = getMapLoader(stream)->loadMapHeader();
|
||||
|
||||
//might be original campaign and require patch
|
||||
getMapPatcher(name)->patchMapHeader(header);
|
||||
return header;
|
||||
}
|
||||
|
||||
std::unique_ptr<CInputStream> CMapService::getStreamFromFS(const std::string & name)
|
||||
void CMapService::saveMap(const std::unique_ptr<CMap> & map, boost::filesystem::path fullPath)
|
||||
{
|
||||
return CResourceHandler::get()->load(ResourceID(name, EResType::MAP));
|
||||
CMemoryBuffer serializeBuffer;
|
||||
{
|
||||
CMapSaverJson saver(&serializeBuffer);
|
||||
saver.saveMap(map);
|
||||
}
|
||||
{
|
||||
boost::filesystem::remove(fullPath);
|
||||
boost::filesystem::ofstream tmp(fullPath, boost::filesystem::ofstream::binary);
|
||||
|
||||
tmp.write((const char *)serializeBuffer.getBuffer().data(),serializeBuffer.getSize());
|
||||
tmp.flush();
|
||||
tmp.close();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<CInputStream> CMapService::getStreamFromFS(const ResourceID & name)
|
||||
{
|
||||
return CResourceHandler::get()->load(name);
|
||||
}
|
||||
|
||||
std::unique_ptr<CInputStream> CMapService::getStreamFromMem(const ui8 * buffer, int size)
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
class ResourceID;
|
||||
|
||||
class CMap;
|
||||
class CMapHeader;
|
||||
class CInputStream;
|
||||
@ -31,7 +33,7 @@ public:
|
||||
* @param name the name of the map
|
||||
* @return a unique ptr to the loaded map class
|
||||
*/
|
||||
static std::unique_ptr<CMap> loadMap(const std::string & name);
|
||||
static std::unique_ptr<CMap> loadMap(const ResourceID & name);
|
||||
|
||||
/**
|
||||
* Loads the VCMI/H3 map header specified by the name.
|
||||
@ -39,7 +41,7 @@ public:
|
||||
* @param name the name of the map
|
||||
* @return a unique ptr to the loaded map header class
|
||||
*/
|
||||
static std::unique_ptr<CMapHeader> loadMapHeader(const std::string & name);
|
||||
static std::unique_ptr<CMapHeader> loadMapHeader(const ResourceID & name);
|
||||
|
||||
/**
|
||||
* Loads the VCMI/H3 map file from a buffer. This method is temporarily
|
||||
@ -69,6 +71,7 @@ public:
|
||||
*/
|
||||
static std::unique_ptr<CMapHeader> loadMapHeader(const ui8 * buffer, int size, const std::string & name);
|
||||
|
||||
static void saveMap(const std::unique_ptr<CMap> & map, boost::filesystem::path fullPath);
|
||||
private:
|
||||
/**
|
||||
* Gets a map input stream object specified by a map name.
|
||||
@ -76,7 +79,7 @@ private:
|
||||
* @param name the name of the map
|
||||
* @return a unique ptr to the input stream class
|
||||
*/
|
||||
static std::unique_ptr<CInputStream> getStreamFromFS(const std::string & name);
|
||||
static std::unique_ptr<CInputStream> getStreamFromFS(const ResourceID & name);
|
||||
|
||||
/**
|
||||
* Gets a map input stream from a buffer.
|
||||
|
@ -872,9 +872,17 @@ bool CMapLoaderH3M::loadArtifactToSlot(CGHeroInstance * hero, int slot)
|
||||
bool isArt = aid != artmask;
|
||||
if(isArt)
|
||||
{
|
||||
if(vstd::contains(VLC->arth->bigArtifacts, aid) && slot >= GameConstants::BACKPACK_START)
|
||||
const CArtifact * art = ArtifactID(aid).toArtifact();
|
||||
|
||||
if(nullptr == art)
|
||||
{
|
||||
logGlobal->warnStream() << "Warning: A big artifact (war machine) in hero's backpack, ignoring...";
|
||||
logGlobal->warnStream() << "Invalid artifact in hero's backpack, ignoring...";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(art->isBig() && slot >= GameConstants::BACKPACK_START)
|
||||
{
|
||||
logGlobal->warnStream() << "A big artifact (war machine) in hero's backpack, ignoring...";
|
||||
return false;
|
||||
}
|
||||
if(aid == 0 && slot == ArtifactPosition::MISC5)
|
||||
|
@ -1212,6 +1212,8 @@ void CMapSaverJson::saveMap(const std::unique_ptr<CMap>& map)
|
||||
|
||||
void CMapSaverJson::writeHeader()
|
||||
{
|
||||
logGlobal->trace("Saving header");
|
||||
|
||||
JsonNode header;
|
||||
JsonSerializer handler(mapObjectResolver.get(), header);
|
||||
|
||||
@ -1283,6 +1285,7 @@ JsonNode CMapSaverJson::writeTerrainLevel(const int index)
|
||||
|
||||
void CMapSaverJson::writeTerrain()
|
||||
{
|
||||
logGlobal->trace("Saving terrain");
|
||||
//todo: multilevel map save support
|
||||
|
||||
JsonNode surface = writeTerrainLevel(0);
|
||||
@ -1297,12 +1300,14 @@ void CMapSaverJson::writeTerrain()
|
||||
|
||||
void CMapSaverJson::writeObjects()
|
||||
{
|
||||
logGlobal->trace("Saving objects");
|
||||
JsonNode data(JsonNode::DATA_STRUCT);
|
||||
|
||||
JsonSerializer handler(mapObjectResolver.get(), data);
|
||||
|
||||
for(CGObjectInstance * obj : map->objects)
|
||||
{
|
||||
logGlobal->trace("\t%s", obj->instanceName);
|
||||
auto temp = handler.enterStruct(obj->instanceName);
|
||||
|
||||
obj->serializeJson(handler);
|
||||
|
@ -210,6 +210,7 @@ void registerTypesClientPacks1(Serializer &s)
|
||||
s.template registerType<CPackForClient, PackageApplied>();
|
||||
s.template registerType<CPackForClient, SystemMessage>();
|
||||
s.template registerType<CPackForClient, PlayerBlocked>();
|
||||
s.template registerType<CPackForClient, PlayerCheated>();
|
||||
s.template registerType<CPackForClient, YourTurn>();
|
||||
s.template registerType<CPackForClient, SetResources>();
|
||||
s.template registerType<CPackForClient, SetPrimSkill>();
|
||||
@ -314,6 +315,7 @@ void registerTypesServerPacks(Serializer &s)
|
||||
{
|
||||
s.template registerType<CPack, CPackForServer>();
|
||||
s.template registerType<CPackForServer, CloseServer>();
|
||||
s.template registerType<CPackForServer, LeaveGame>();
|
||||
s.template registerType<CPackForServer, EndTurn>();
|
||||
s.template registerType<CPackForServer, DismissHero>();
|
||||
s.template registerType<CPackForServer, MoveHero>();
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "StdInc.h"
|
||||
#include "RegisterTypes.h"
|
||||
|
||||
#include "../mapping/CMapInfo.h"
|
||||
#include "../StartInfo.h"
|
||||
#include "../CGameState.h"
|
||||
#include "../mapping/CMap.h"
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "StdInc.h"
|
||||
#include "RegisterTypes.h"
|
||||
|
||||
#include "../mapping/CMapInfo.h"
|
||||
#include "../StartInfo.h"
|
||||
#include "../CStack.h"
|
||||
#include "../BattleInfo.h"
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "StdInc.h"
|
||||
#include "RegisterTypes.h"
|
||||
|
||||
#include "../mapping/CMapInfo.h"
|
||||
#include "../StartInfo.h"
|
||||
#include "../CGameState.h"
|
||||
#include "../mapping/CMap.h"
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "StdInc.h"
|
||||
#include "RegisterTypes.h"
|
||||
|
||||
#include "../mapping/CMapInfo.h"
|
||||
#include "../StartInfo.h"
|
||||
#include "../CStack.h"
|
||||
#include "../BattleInfo.h"
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "StdInc.h"
|
||||
#include "RegisterTypes.h"
|
||||
|
||||
#include "../mapping/CMapInfo.h"
|
||||
#include "../StartInfo.h"
|
||||
#include "../CGameState.h"
|
||||
#include "../mapping/CMap.h"
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "StdInc.h"
|
||||
#include "RegisterTypes.h"
|
||||
|
||||
#include "../mapping/CMapInfo.h"
|
||||
#include "../StartInfo.h"
|
||||
#include "../CGameState.h"
|
||||
#include "../mapping/CMap.h"
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "../ConstTransitivePtr.h"
|
||||
#include "../GameConstants.h"
|
||||
|
||||
const ui32 SERIALIZATION_VERSION = 770;
|
||||
const ui32 SERIALIZATION_VERSION = 771;
|
||||
const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
|
||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
||||
|
||||
|
@ -61,14 +61,14 @@ void CConnection::init()
|
||||
iser.fileVersion = SERIALIZATION_VERSION;
|
||||
}
|
||||
|
||||
CConnection::CConnection(std::string host, std::string port, std::string Name)
|
||||
CConnection::CConnection(std::string host, ui16 port, std::string Name)
|
||||
:iser(this), oser(this), io_service(new asio::io_service), name(Name)
|
||||
{
|
||||
int i;
|
||||
boost::system::error_code error = asio::error::host_not_found;
|
||||
socket = new tcp::socket(*io_service);
|
||||
tcp::resolver resolver(*io_service);
|
||||
tcp::resolver::iterator end, pom, endpoint_iterator = resolver.resolve(tcp::resolver::query(host,port),error);
|
||||
tcp::resolver::iterator end, pom, endpoint_iterator = resolver.resolve(tcp::resolver::query(host, std::to_string(port)),error);
|
||||
if(error)
|
||||
{
|
||||
logNetwork->errorStream() << "Problem with resolving: \n" << error;
|
||||
@ -191,8 +191,7 @@ void CConnection::close()
|
||||
if(socket)
|
||||
{
|
||||
socket->close();
|
||||
delete socket;
|
||||
socket = nullptr;
|
||||
vstd::clear_pointer(socket);
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,6 +200,11 @@ bool CConnection::isOpen() const
|
||||
return socket && connected;
|
||||
}
|
||||
|
||||
bool CConnection::isHost() const
|
||||
{
|
||||
return connectionID == 1;
|
||||
}
|
||||
|
||||
void CConnection::reportState(CLogger * out)
|
||||
{
|
||||
out->debugStream() << "CConnection";
|
||||
|
@ -68,12 +68,13 @@ public:
|
||||
|
||||
bool receivedStop, sendStop;
|
||||
|
||||
CConnection(std::string host, std::string port, std::string Name);
|
||||
CConnection(std::string host, ui16 port, std::string Name);
|
||||
CConnection(TAcceptor * acceptor, boost::asio::io_service *Io_service, std::string Name);
|
||||
CConnection(TSocket * Socket, std::string Name); //use immediately after accepting connection into socket
|
||||
|
||||
void close();
|
||||
bool isOpen() const;
|
||||
bool isHost() const;
|
||||
template<class T>
|
||||
CConnection &operator&(const T&);
|
||||
virtual ~CConnection(void);
|
||||
|
@ -157,10 +157,50 @@ ui32 CSpell::calculateDamage(const ISpellCaster * caster, const CStack * affecte
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, const ISpellCaster * caster) const
|
||||
{
|
||||
const ESpellCastProblem::ESpellCastProblem generalProblem = mechanics->canBeCast(cb, mode, caster);
|
||||
ESpellCastProblem::ESpellCastProblem genProblem = cb->battleCanCastSpell(caster, mode);
|
||||
if(genProblem != ESpellCastProblem::OK)
|
||||
return genProblem;
|
||||
|
||||
if(generalProblem != ESpellCastProblem::OK)
|
||||
return generalProblem;
|
||||
switch(mode)
|
||||
{
|
||||
case ECastingMode::HERO_CASTING:
|
||||
{
|
||||
const CGHeroInstance * castingHero = dynamic_cast<const CGHeroInstance *>(caster);//todo: unify hero|creature spell cost
|
||||
if(!castingHero)
|
||||
{
|
||||
logGlobal->debug("CSpell::canBeCast: invalid caster");
|
||||
return ESpellCastProblem::NO_HERO_TO_CAST_SPELL;
|
||||
}
|
||||
|
||||
if(!castingHero->getArt(ArtifactPosition::SPELLBOOK))
|
||||
return ESpellCastProblem::NO_SPELLBOOK;
|
||||
if(!castingHero->canCastThisSpell(this))
|
||||
return ESpellCastProblem::HERO_DOESNT_KNOW_SPELL;
|
||||
if(castingHero->mana < cb->battleGetSpellCost(this, castingHero)) //not enough mana
|
||||
return ESpellCastProblem::NOT_ENOUGH_MANA;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(!isCombatSpell())
|
||||
return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
|
||||
|
||||
const PlayerColor player = caster->getOwner();
|
||||
const si8 side = cb->playerToSide(player);
|
||||
|
||||
if(side < 0)
|
||||
return ESpellCastProblem::INVALID;
|
||||
|
||||
//effect like Recanter's Cloak. Blocks also passive casting.
|
||||
//TODO: check creature abilities to block
|
||||
//TODO: check any possible caster
|
||||
if(cb->battleMaxSpellLevel(side) < level)
|
||||
return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED;
|
||||
|
||||
const ESpellCastProblem::ESpellCastProblem specificProblem = mechanics->canBeCast(cb, mode, caster);
|
||||
|
||||
if(specificProblem != ESpellCastProblem::OK)
|
||||
return specificProblem;
|
||||
|
||||
//check for creature target existence
|
||||
//allow to cast spell if there is at least one smart target
|
||||
@ -368,6 +408,10 @@ void CSpell::getEffects(std::vector<Bonus> & lst, const int level) const
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem CSpell::canBeCastAt(const CBattleInfoCallback * cb, const ISpellCaster * caster, ECastingMode::ECastingMode mode, BattleHex destination) const
|
||||
{
|
||||
ESpellCastProblem::ESpellCastProblem problem = canBeCast(cb, mode, caster);
|
||||
if(problem != ESpellCastProblem::OK)
|
||||
return problem;
|
||||
|
||||
SpellTargetingContext ctx(this, mode, caster, caster->getSpellSchoolLevel(this), destination);
|
||||
|
||||
return mechanics->canBeCast(cb, ctx);
|
||||
|
@ -87,10 +87,14 @@ void BattleSpellCastParameters::cast(const SpellCastEnvironment * env)
|
||||
spell->battleCast(env, *this);
|
||||
}
|
||||
|
||||
void BattleSpellCastParameters::castIfPossible(const SpellCastEnvironment * env)
|
||||
bool BattleSpellCastParameters::castIfPossible(const SpellCastEnvironment * env)
|
||||
{
|
||||
if(ESpellCastProblem::OK == cb->battleCanCastThisSpell(caster, spell, mode))
|
||||
if(ESpellCastProblem::OK == spell->canBeCast(cb, mode, caster))
|
||||
{
|
||||
cast(env);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BattleHex BattleSpellCastParameters::getFirstDestinationHex() const
|
||||
|
@ -57,7 +57,8 @@ public:
|
||||
void cast(const SpellCastEnvironment * env);
|
||||
|
||||
///cast with silent check for permitted cast
|
||||
void castIfPossible(const SpellCastEnvironment * env);
|
||||
///returns true if cast was permitted
|
||||
bool castIfPossible(const SpellCastEnvironment * env);
|
||||
|
||||
BattleHex getFirstDestinationHex() const;
|
||||
|
||||
|
@ -48,7 +48,7 @@
|
||||
#ifndef _MSC_VER
|
||||
#include <boost/thread/xtime.hpp>
|
||||
#endif
|
||||
extern bool end2;
|
||||
extern std::atomic<bool> serverShuttingDown;
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
@ -1031,6 +1031,25 @@ void CGameHandler::handleConnection(std::set<PlayerColor> players, CConnection &
|
||||
{
|
||||
setThreadName("CGameHandler::handleConnection");
|
||||
|
||||
auto handleDisconnection = [&](const std::exception & e)
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(*c.wmx);
|
||||
assert(!c.connected); //make sure that connection has been marked as broken
|
||||
logGlobal->error(e.what());
|
||||
conns -= &c;
|
||||
for(auto playerConn : connections)
|
||||
{
|
||||
if(!serverShuttingDown && playerConn.second == &c)
|
||||
{
|
||||
PlayerCheated pc;
|
||||
pc.player = playerConn.first;
|
||||
pc.losingCheatCode = true;
|
||||
sendAndApply(&pc);
|
||||
checkVictoryLossConditionsForPlayer(playerConn.first);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
while(1)//server should never shut connection first //was: while(!end2)
|
||||
@ -1042,6 +1061,8 @@ void CGameHandler::handleConnection(std::set<PlayerColor> players, CConnection &
|
||||
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(*c.rmx);
|
||||
if(!c.connected)
|
||||
throw clientDisconnectedException();
|
||||
c >> player >> requestID >> pack; //get the package
|
||||
|
||||
if (!pack)
|
||||
@ -1060,6 +1081,11 @@ void CGameHandler::handleConnection(std::set<PlayerColor> players, CConnection &
|
||||
//prepare struct informing that action was applied
|
||||
auto sendPackageResponse = [&](bool succesfullyApplied)
|
||||
{
|
||||
//dont reply to disconnected client
|
||||
//TODO: this must be implemented as option of CPackForServer
|
||||
if(dynamic_cast<LeaveGame *>(pack) || dynamic_cast<CloseServer *>(pack))
|
||||
return;
|
||||
|
||||
PackageApplied applied;
|
||||
applied.player = player;
|
||||
applied.result = succesfullyApplied;
|
||||
@ -1095,13 +1121,15 @@ void CGameHandler::handleConnection(std::set<PlayerColor> players, CConnection &
|
||||
}
|
||||
catch(boost::system::system_error &e) //for boost errors just log, not crash - probably client shut down connection
|
||||
{
|
||||
assert(!c.connected); //make sure that connection has been marked as broken
|
||||
logGlobal->error(e.what());
|
||||
end2 = true;
|
||||
handleDisconnection(e);
|
||||
}
|
||||
catch(clientDisconnectedException & e)
|
||||
{
|
||||
handleDisconnection(e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
end2 = true;
|
||||
serverShuttingDown = true;
|
||||
handleException();
|
||||
throw;
|
||||
}
|
||||
@ -1851,7 +1879,8 @@ void CGameHandler::run(bool resume)
|
||||
sbuffer << color << " ";
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(gsm);
|
||||
connections[color] = cc;
|
||||
if(!color.isSpectator()) // there can be more than one spectator
|
||||
connections[color] = cc;
|
||||
}
|
||||
}
|
||||
logGlobal->info(sbuffer.str());
|
||||
@ -1874,7 +1903,7 @@ void CGameHandler::run(bool resume)
|
||||
if (gs->scenarioOps->mode == StartInfo::DUEL)
|
||||
{
|
||||
runBattle();
|
||||
end2 = true;
|
||||
serverShuttingDown = true;
|
||||
|
||||
|
||||
while(conns.size() && (*conns.begin())->isOpen())
|
||||
@ -1885,7 +1914,7 @@ void CGameHandler::run(bool resume)
|
||||
|
||||
auto playerTurnOrder = generatePlayerTurnOrder();
|
||||
|
||||
while(!end2)
|
||||
while(!serverShuttingDown)
|
||||
{
|
||||
if (!resume) newTurn();
|
||||
|
||||
@ -1926,7 +1955,7 @@ void CGameHandler::run(bool resume)
|
||||
|
||||
//wait till turn is done
|
||||
boost::unique_lock<boost::mutex> lock(states.mx);
|
||||
while (states.players.at(playerColor).makingTurn && !end2)
|
||||
while(states.players.at(playerColor).makingTurn && !serverShuttingDown)
|
||||
{
|
||||
static time_duration p = milliseconds(100);
|
||||
states.cv.timed_wait(lock, p);
|
||||
@ -1942,7 +1971,7 @@ void CGameHandler::run(bool resume)
|
||||
activePlayer = true;
|
||||
}
|
||||
if (!activePlayer)
|
||||
end2 = true;
|
||||
serverShuttingDown = true;
|
||||
}
|
||||
while(conns.size() && (*conns.begin())->isOpen())
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(5)); //give time client to close socket
|
||||
@ -2631,6 +2660,9 @@ void CGameHandler::sendToAllClients(CPackForClient * info)
|
||||
logNetwork->trace("Sending to all clients a package of type %s", typeid(*info).name());
|
||||
for (auto & elem : conns)
|
||||
{
|
||||
if(!elem->isOpen())
|
||||
continue;
|
||||
|
||||
boost::unique_lock<boost::mutex> lock(*(elem)->wmx);
|
||||
*elem << info;
|
||||
}
|
||||
@ -2703,11 +2735,31 @@ void CGameHandler::close()
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
serverShuttingDown = true;
|
||||
|
||||
//for (CConnection *cc : conns)
|
||||
// if (cc && cc->socket && cc->socket->is_open())
|
||||
// cc->socket->close();
|
||||
//exit(0);
|
||||
for (auto & elem : conns)
|
||||
{
|
||||
if(!elem->isOpen())
|
||||
continue;
|
||||
|
||||
boost::unique_lock<boost::mutex> lock(*(elem)->wmx);
|
||||
elem->close();
|
||||
elem->connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CGameHandler::playerLeftGame(int cid)
|
||||
{
|
||||
for (auto & elem : conns)
|
||||
{
|
||||
if(elem->isOpen() && elem->connectionID == cid)
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(*(elem)->wmx);
|
||||
elem->close();
|
||||
elem->connected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CGameHandler::arrangeStacks(ObjectInstanceID id1, ObjectInstanceID id2, ui8 what, SlotID p1, SlotID p2, si32 val, PlayerColor player)
|
||||
@ -3083,7 +3135,7 @@ bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst
|
||||
const CGDwelling * dw = static_cast<const CGDwelling *>(getObj(objid));
|
||||
const CArmedInstance *dst = nullptr;
|
||||
const CCreature *c = VLC->creh->creatures.at(crid);
|
||||
bool warMachine = c->hasBonusOfType(Bonus::SIEGE_WEAPON);
|
||||
const bool warMachine = c->warMachine != ArtifactID::NONE;
|
||||
|
||||
//TODO: test for owning
|
||||
//TODO: check if dst can recruit objects (e.g. hero is actually visiting object, town and source are same, etc)
|
||||
@ -3134,24 +3186,18 @@ bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst
|
||||
if (warMachine)
|
||||
{
|
||||
const CGHeroInstance *h = dynamic_cast<const CGHeroInstance*>(dst);
|
||||
if (!h)
|
||||
COMPLAIN_RET("Only hero can buy war machines");
|
||||
|
||||
switch(crid)
|
||||
{
|
||||
case CreatureID::BALLISTA:
|
||||
giveHeroNewArtifact(h, VLC->arth->artifacts[ArtifactID::BALLISTA], ArtifactPosition::MACH1);
|
||||
break;
|
||||
case CreatureID::FIRST_AID_TENT:
|
||||
giveHeroNewArtifact(h, VLC->arth->artifacts[ArtifactID::FIRST_AID_TENT], ArtifactPosition::MACH3);
|
||||
break;
|
||||
case CreatureID::AMMO_CART:
|
||||
giveHeroNewArtifact(h, VLC->arth->artifacts[ArtifactID::AMMO_CART], ArtifactPosition::MACH2);
|
||||
break;
|
||||
default:
|
||||
complain("This war machine cannot be recruited!");
|
||||
return false;
|
||||
}
|
||||
COMPLAIN_RET_FALSE_IF(!h, "Only hero can buy war machines");
|
||||
|
||||
ArtifactID artId = c->warMachine;
|
||||
|
||||
COMPLAIN_RET_FALSE_IF(artId == ArtifactID::CATAPULT, "Catapult cannot be recruited!");
|
||||
|
||||
const CArtifact * art = artId.toArtifact();
|
||||
|
||||
COMPLAIN_RET_FALSE_IF(nullptr == art, "Invalid war machine artifact");
|
||||
|
||||
return giveHeroNewArtifact(h, art);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3392,7 +3438,10 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition
|
||||
bool CGameHandler::buyArtifact(ObjectInstanceID hid, ArtifactID aid)
|
||||
{
|
||||
const CGHeroInstance * hero = getHero(hid);
|
||||
COMPLAIN_RET_FALSE_IF(nullptr == hero, "Invalid hero index");
|
||||
const CGTownInstance * town = hero->visitedTown;
|
||||
COMPLAIN_RET_FALSE_IF(nullptr == town, "Hero not in town");
|
||||
|
||||
if (aid==ArtifactID::SPELLBOOK)
|
||||
{
|
||||
if ((!town->hasBuilt(BuildingID::MAGES_GUILD_1) && complain("Cannot buy a spellbook, no mage guild in the town!"))
|
||||
@ -3407,26 +3456,24 @@ bool CGameHandler::buyArtifact(ObjectInstanceID hid, ArtifactID aid)
|
||||
giveSpells(town,hero);
|
||||
return true;
|
||||
}
|
||||
else if (aid < 7 && aid > 3) //war machine
|
||||
else
|
||||
{
|
||||
int price = VLC->arth->artifacts[aid]->price;
|
||||
const CArtifact * art = aid.toArtifact();
|
||||
COMPLAIN_RET_FALSE_IF(nullptr == art, "Invalid artifact index to buy");
|
||||
COMPLAIN_RET_FALSE_IF(art->warMachine == CreatureID::NONE, "War machine artifact required");
|
||||
COMPLAIN_RET_FALSE_IF(hero->hasArt(aid),"Hero already has this machine!");
|
||||
const int price = art->price;
|
||||
COMPLAIN_RET_FALSE_IF(getPlayer(hero->getOwner())->resources.at(Res::GOLD) < price, "Not enough gold!");
|
||||
|
||||
if ((hero->getArt(ArtifactPosition(9+aid)) && complain("Hero already has this machine!"))
|
||||
|| (getPlayer(hero->getOwner())->resources.at(Res::GOLD) < price && complain("Not enough gold!")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ((town->hasBuilt(BuildingID::BLACKSMITH) && town->town->warMachine == aid)
|
||||
|| ((town->hasBuilt(BuildingID::BALLISTA_YARD, ETownType::STRONGHOLD)) && aid == ArtifactID::BALLISTA))
|
||||
{
|
||||
giveResource(hero->getOwner(),Res::GOLD,-price);
|
||||
giveHeroNewArtifact(hero, VLC->arth->artifacts[aid], ArtifactPosition(9+aid));
|
||||
return true;
|
||||
return giveHeroNewArtifact(hero, art);
|
||||
}
|
||||
else
|
||||
COMPLAIN_RET("This machine is unavailable here!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CGameHandler::buyArtifact(const IMarket *m, const CGHeroInstance *h, Res::ERes rid, ArtifactID aid)
|
||||
@ -4379,7 +4426,9 @@ void CGameHandler::playerMessage(PlayerColor player, const std::string &message,
|
||||
{
|
||||
SystemMessage temp_message(VLC->generaltexth->allTexts.at(260));
|
||||
sendAndApply(&temp_message);
|
||||
checkVictoryLossConditionsForPlayer(player);//Player enter win code or got required art\creature
|
||||
|
||||
if(!player.isSpectator())
|
||||
checkVictoryLossConditionsForPlayer(player);//Player enter win code or got required art\creature
|
||||
}
|
||||
}
|
||||
|
||||
@ -4407,7 +4456,7 @@ bool CGameHandler::makeCustomAction(BattleAction &ba)
|
||||
if (ba.selectedStack >= 0)
|
||||
parameters.aimToStack(gs->curB->battleGetStackByID(ba.selectedStack, false));
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem escp = gs->curB->battleCanCastThisSpell(h, s, ECastingMode::HERO_CASTING);//todo: should we check aimed cast(battleCanCastThisSpellHere)?
|
||||
ESpellCastProblem::ESpellCastProblem escp = s->canBeCast(gs->curB, ECastingMode::HERO_CASTING, h);//todo: should we check aimed cast?
|
||||
if (escp != ESpellCastProblem::OK)
|
||||
{
|
||||
logGlobal->warn("Spell cannot be cast! Problem: %d", escp);
|
||||
@ -4580,14 +4629,14 @@ void CGameHandler::stackTurnTrigger(const CStack *st)
|
||||
const CSpell * spell = SpellID(spellID).toSpell();
|
||||
bl.remove_if([&bonus](const Bonus* b){return b==bonus.get();});
|
||||
|
||||
if (gs->curB->battleCanCastThisSpell(st, spell, ECastingMode::ENCHANTER_CASTING) == ESpellCastProblem::OK)
|
||||
{
|
||||
BattleSpellCastParameters parameters(gs->curB, st, spell);
|
||||
parameters.spellLvl = bonus->val;
|
||||
parameters.effectLevel = bonus->val;//todo: recheck
|
||||
parameters.mode = ECastingMode::ENCHANTER_CASTING;
|
||||
parameters.cast(spellEnv);
|
||||
BattleSpellCastParameters parameters(gs->curB, st, spell);
|
||||
parameters.spellLvl = bonus->val;
|
||||
parameters.effectLevel = bonus->val;//todo: recheck
|
||||
parameters.mode = ECastingMode::ENCHANTER_CASTING;
|
||||
|
||||
cast = parameters.castIfPossible(spellEnv);
|
||||
if(cast)
|
||||
{
|
||||
//todo: move to mechanics
|
||||
BattleSetStackProperty ssp;
|
||||
ssp.which = BattleSetStackProperty::ENCHANTER_COUNTER;
|
||||
@ -4595,8 +4644,6 @@ void CGameHandler::stackTurnTrigger(const CStack *st)
|
||||
ssp.val = bonus->additionalInfo; //increase cooldown counter
|
||||
ssp.stackID = st->ID;
|
||||
sendAndApply(&ssp);
|
||||
|
||||
cast = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5051,7 +5098,7 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
|
||||
|
||||
if (p->human)
|
||||
{
|
||||
end2 = true;
|
||||
serverShuttingDown = true;
|
||||
|
||||
if (gs->scenarioOps->campState)
|
||||
{
|
||||
@ -5251,7 +5298,7 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta
|
||||
vstd::amin(chance, 100);
|
||||
|
||||
const CSpell * spell = SpellID(spellID).toSpell();
|
||||
if (gs->curB->battleCanCastThisSpellHere(attacker, spell, ECastingMode::AFTER_ATTACK_CASTING, oneOfAttacked->position) != ESpellCastProblem::OK)
|
||||
if(spell->canBeCastAt(gs->curB, attacker, ECastingMode::AFTER_ATTACK_CASTING, oneOfAttacked->position) != ESpellCastProblem::OK)
|
||||
continue;
|
||||
|
||||
//check if spell should be cast (probability handling)
|
||||
@ -6001,6 +6048,18 @@ void CGameHandler::putArtifact(const ArtifactLocation &al, const CArtifactInstan
|
||||
sendAndApply(&pa);
|
||||
}
|
||||
|
||||
bool CGameHandler::giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact * art)
|
||||
{
|
||||
COMPLAIN_RET_FALSE_IF(art->possibleSlots.at(ArtBearer::HERO).empty(), "Not a hero artifact!");
|
||||
|
||||
ArtifactPosition slot = art->possibleSlots.at(ArtBearer::HERO).front();
|
||||
|
||||
COMPLAIN_RET_FALSE_IF(nullptr != h->getArt(slot, false), "Hero already has artifact in slot");
|
||||
|
||||
giveHeroNewArtifact(h, art, slot);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGameHandler::giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact *artType, ArtifactPosition pos)
|
||||
{
|
||||
CArtifactInstance *a = nullptr;
|
||||
@ -6162,12 +6221,18 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
||||
else if (cheat == "vcmisilmaril")
|
||||
{
|
||||
///Player wins
|
||||
gs->getPlayer(player)->enteredWinningCheatCode = 1;
|
||||
PlayerCheated pc;
|
||||
pc.player = player;
|
||||
pc.winningCheatCode = true;
|
||||
sendAndApply(&pc);
|
||||
}
|
||||
else if (cheat == "vcmimelkor")
|
||||
{
|
||||
///Player looses
|
||||
gs->getPlayer(player)->enteredLosingCheatCode = 1;
|
||||
PlayerCheated pc;
|
||||
pc.player = player;
|
||||
pc.losingCheatCode = true;
|
||||
sendAndApply(&pc);
|
||||
}
|
||||
else if (cheat == "vcmieagles" || cheat == "vcmiungoliant")
|
||||
{
|
||||
@ -6357,10 +6422,12 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance * _army, Battl
|
||||
}
|
||||
else if (st->slot == SlotID::WAR_MACHINES_SLOT)
|
||||
{
|
||||
auto warMachine = VLC->arth->creatureToMachineID(st->type->idNumber);
|
||||
auto warMachine = st->type->warMachine;
|
||||
|
||||
if (warMachine == ArtifactID::NONE)
|
||||
{
|
||||
logGlobal->error("Invalid creature in war machine virtual slot. Stack: %s", st->nodeName());
|
||||
}
|
||||
//catapult artifact remain even if "creature" killed in siege
|
||||
else if (warMachine != ArtifactID::CATAPULT && !st->count)
|
||||
{
|
||||
|
@ -143,6 +143,7 @@ public:
|
||||
|
||||
void removeAfterVisit(const CGObjectInstance *object) override;
|
||||
|
||||
bool giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact * art);
|
||||
void giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact *artType, ArtifactPosition pos) override;
|
||||
void giveHeroArtifact(const CGHeroInstance *h, const CArtifactInstance *a, ArtifactPosition pos) override;
|
||||
void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) override;
|
||||
@ -223,6 +224,7 @@ public:
|
||||
bool arrangeStacks( ObjectInstanceID id1, ObjectInstanceID id2, ui8 what, SlotID p1, SlotID p2, si32 val, PlayerColor player);
|
||||
void save(const std::string &fname);
|
||||
void close();
|
||||
void playerLeftGame(int cid);
|
||||
void handleTimeEvents();
|
||||
void handleTownEvents(CGTownInstance *town, NewTurn &n);
|
||||
bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true
|
||||
@ -298,4 +300,9 @@ private:
|
||||
void checkVictoryLossConditionsForAll();
|
||||
};
|
||||
|
||||
class clientDisconnectedException : public std::exception
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void makeStackDoNothing();
|
||||
|
@ -41,11 +41,7 @@
|
||||
|
||||
std::string NAME_AFFIX = "server";
|
||||
std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name
|
||||
#ifndef VCMI_ANDROID
|
||||
namespace intpr = boost::interprocess;
|
||||
#endif
|
||||
bool end2 = false;
|
||||
int port = 3030;
|
||||
std::atomic<bool> serverShuttingDown(false);
|
||||
|
||||
boost::program_options::variables_map cmdLineOptions;
|
||||
|
||||
@ -106,6 +102,10 @@ void CPregameServer::handleConnection(CConnection *cpc)
|
||||
auto unlock = vstd::makeUnlockGuard(mx);
|
||||
while(state == RUNNING) boost::this_thread::sleep(boost::posix_time::milliseconds(50));
|
||||
}
|
||||
else if(quitting) // Server must be stopped if host is leaving from lobby to avoid crash
|
||||
{
|
||||
serverShuttingDown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
@ -206,20 +206,29 @@ void CPregameServer::connectionAccepted(const boost::system::error_code& ec)
|
||||
return;
|
||||
}
|
||||
|
||||
logNetwork->info("We got a new connection! :)");
|
||||
CConnection *pc = new CConnection(upcomingConnection, NAME);
|
||||
initConnection(pc);
|
||||
upcomingConnection = nullptr;
|
||||
try
|
||||
{
|
||||
logNetwork->info("We got a new connection! :)");
|
||||
std::string name = NAME;
|
||||
CConnection *pc = new CConnection(upcomingConnection, name.append(" STATE_PREGAME"));
|
||||
initConnection(pc);
|
||||
upcomingConnection = nullptr;
|
||||
|
||||
startListeningThread(pc);
|
||||
startListeningThread(pc);
|
||||
|
||||
*pc << (ui8)pc->connectionID << curmap;
|
||||
*pc << (ui8)pc->connectionID << curmap;
|
||||
|
||||
announceTxt(pc->name + " joins the game");
|
||||
auto pj = new PlayerJoined();
|
||||
pj->playerName = pc->name;
|
||||
pj->connectionID = pc->connectionID;
|
||||
toAnnounce.push_back(pj);
|
||||
announceTxt(pc->name + " joins the game");
|
||||
auto pj = new PlayerJoined();
|
||||
pj->playerName = pc->name;
|
||||
pj->connectionID = pc->connectionID;
|
||||
toAnnounce.push_back(pj);
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
upcomingConnection = nullptr;
|
||||
logNetwork->info("I guess it was just my imagination!");
|
||||
}
|
||||
|
||||
start_async_accept();
|
||||
}
|
||||
@ -314,9 +323,28 @@ void CPregameServer::startListeningThread(CConnection * pc)
|
||||
}
|
||||
|
||||
CVCMIServer::CVCMIServer()
|
||||
: io(new boost::asio::io_service()), acceptor(new TAcceptor(*io, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))), firstConnection(nullptr)
|
||||
: port(3030), io(new boost::asio::io_service()), firstConnection(nullptr), shared(nullptr)
|
||||
{
|
||||
logNetwork->trace("CVCMIServer created!");
|
||||
if(cmdLineOptions.count("port"))
|
||||
port = cmdLineOptions["port"].as<ui16>();
|
||||
logNetwork->info("Port %d will be used", port);
|
||||
try
|
||||
{
|
||||
acceptor = new TAcceptor(*io, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
logNetwork->info("Port %d is busy, trying to use random port instead", port);
|
||||
if(cmdLineOptions.count("run-by-client") && !cmdLineOptions.count("enable-shm"))
|
||||
{
|
||||
logNetwork->error("Cant pass port number to client without shared memory!", port);
|
||||
exit(0);
|
||||
}
|
||||
acceptor = new TAcceptor(*io, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 0));
|
||||
port = acceptor->local_endpoint().port();
|
||||
}
|
||||
logNetwork->info("Listening for connections at port %d", port);
|
||||
}
|
||||
CVCMIServer::~CVCMIServer()
|
||||
{
|
||||
@ -394,69 +422,76 @@ void CVCMIServer::newPregame()
|
||||
void CVCMIServer::start()
|
||||
{
|
||||
#ifndef VCMI_ANDROID
|
||||
ServerReady *sr = nullptr;
|
||||
intpr::mapped_region *mr;
|
||||
try
|
||||
if(cmdLineOptions.count("enable-shm"))
|
||||
{
|
||||
intpr::shared_memory_object smo(intpr::open_only,"vcmi_memory",intpr::read_write);
|
||||
smo.truncate(sizeof(ServerReady));
|
||||
mr = new intpr::mapped_region(smo,intpr::read_write);
|
||||
sr = reinterpret_cast<ServerReady*>(mr->get_address());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
intpr::shared_memory_object smo(intpr::create_only,"vcmi_memory",intpr::read_write);
|
||||
smo.truncate(sizeof(ServerReady));
|
||||
mr = new intpr::mapped_region(smo,intpr::read_write);
|
||||
sr = new(mr->get_address())ServerReady();
|
||||
std::string sharedMemoryName = "vcmi_memory";
|
||||
if(cmdLineOptions.count("enable-shm-uuid") && cmdLineOptions.count("uuid"))
|
||||
{
|
||||
sharedMemoryName += "_" + cmdLineOptions["uuid"].as<std::string>();
|
||||
}
|
||||
shared = new SharedMemory(sharedMemoryName);
|
||||
}
|
||||
#endif
|
||||
|
||||
boost::system::error_code error;
|
||||
logNetwork->info("Listening for connections at port %d", acceptor->local_endpoint().port());
|
||||
auto s = new boost::asio::ip::tcp::socket(acceptor->get_io_service());
|
||||
boost::thread acc(std::bind(vaccept,acceptor,s,&error));
|
||||
#ifndef VCMI_ANDROID
|
||||
sr->setToTrueAndNotify();
|
||||
delete mr;
|
||||
for (;;)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto s = new boost::asio::ip::tcp::socket(acceptor->get_io_service());
|
||||
boost::thread acc(std::bind(vaccept,acceptor,s,&error));
|
||||
#ifdef VCMI_ANDROID
|
||||
{ // in block to clean-up vm helper after use, because we don't need to keep this thread attached to vm
|
||||
CAndroidVMHelper envHelper;
|
||||
envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "onServerReady");
|
||||
logNetwork->info("Sending server ready message to client");
|
||||
}
|
||||
#else
|
||||
{ // in block to clean-up vm helper after use, because we don't need to keep this thread attached to vm
|
||||
CAndroidVMHelper envHelper;
|
||||
envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "onServerReady");
|
||||
logNetwork->info("Sending server ready message to client");
|
||||
}
|
||||
if(shared)
|
||||
{
|
||||
shared->sr->setToReadyAndNotify(port);
|
||||
}
|
||||
#endif
|
||||
|
||||
acc.join();
|
||||
if (error)
|
||||
{
|
||||
logNetwork->warnStream() << "Got connection but there is an error " << error;
|
||||
return;
|
||||
}
|
||||
logNetwork->info("We've accepted someone... ");
|
||||
firstConnection = new CConnection(s, NAME);
|
||||
logNetwork->info("Got connection!");
|
||||
while (!end2)
|
||||
{
|
||||
ui8 mode;
|
||||
*firstConnection >> mode;
|
||||
switch (mode)
|
||||
acc.join();
|
||||
if (error)
|
||||
{
|
||||
logNetwork->warnStream()<<"Got connection but there is an error " << error;
|
||||
return;
|
||||
}
|
||||
logNetwork->info("We've accepted someone... ");
|
||||
std::string name = NAME;
|
||||
firstConnection = new CConnection(s, name.append(" STATE_WAITING"));
|
||||
logNetwork->info("Got connection!");
|
||||
while(!serverShuttingDown)
|
||||
{
|
||||
ui8 mode;
|
||||
*firstConnection >> mode;
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
firstConnection->close();
|
||||
exit(0);
|
||||
case 1:
|
||||
firstConnection->close();
|
||||
return;
|
||||
case 2:
|
||||
newGame();
|
||||
break;
|
||||
case 3:
|
||||
loadGame();
|
||||
break;
|
||||
case 4:
|
||||
newPregame();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
case 0:
|
||||
firstConnection->close();
|
||||
exit(0);
|
||||
case 1:
|
||||
firstConnection->close();
|
||||
return;
|
||||
case 2:
|
||||
newGame();
|
||||
break;
|
||||
case 3:
|
||||
loadGame();
|
||||
break;
|
||||
case 4:
|
||||
newPregame();
|
||||
break;
|
||||
vstd::clear_pointer(firstConnection);
|
||||
logNetwork->info("I guess it was just my imagination!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -507,7 +542,11 @@ static void handleCommandOptions(int argc, char *argv[])
|
||||
opts.add_options()
|
||||
("help,h", "display help and exit")
|
||||
("version,v", "display version information and exit")
|
||||
("port", po::value<int>()->default_value(3030), "port at which server will listen to connections from client")
|
||||
("run-by-client", "indicate that server launched by client on same machine")
|
||||
("uuid", po::value<std::string>(), "")
|
||||
("enable-shm-uuid", "use UUID for shared memory identifier")
|
||||
("enable-shm", "enable usage of shared memory")
|
||||
("port", po::value<ui16>(), "port at which server will listen to connections from client")
|
||||
("resultsFile", po::value<std::string>()->default_value("./results.txt"), "file to which the battle result will be appended. Used only in the DUEL mode.");
|
||||
|
||||
if(argc > 1)
|
||||
@ -584,12 +623,7 @@ int main(int argc, char** argv)
|
||||
logConfig.configureDefault();
|
||||
logGlobal->info(NAME);
|
||||
|
||||
|
||||
handleCommandOptions(argc, argv);
|
||||
if (cmdLineOptions.count("port"))
|
||||
port = cmdLineOptions["port"].as<int>();
|
||||
logNetwork->info("Port %d will be used.", port);
|
||||
|
||||
preinitDLL(console);
|
||||
settings.init();
|
||||
logConfig.configure();
|
||||
@ -603,7 +637,7 @@ int main(int argc, char** argv)
|
||||
|
||||
try
|
||||
{
|
||||
while (!end2)
|
||||
while(!serverShuttingDown)
|
||||
{
|
||||
server.start();
|
||||
}
|
||||
@ -612,7 +646,7 @@ int main(int argc, char** argv)
|
||||
catch (boost::system::system_error &e) //for boost errors just log, not crash - probably client shut down connection
|
||||
{
|
||||
logNetwork->error(e.what());
|
||||
end2 = true;
|
||||
serverShuttingDown = true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -630,11 +664,9 @@ int main(int argc, char** argv)
|
||||
CAndroidVMHelper envHelper;
|
||||
envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "killServer");
|
||||
#endif
|
||||
delete VLC;
|
||||
VLC = nullptr;
|
||||
vstd::clear_pointer(VLC);
|
||||
CResourceHandler::clear();
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
|
@ -17,6 +17,7 @@ class CMapInfo;
|
||||
class CConnection;
|
||||
struct CPackForSelectionScreen;
|
||||
class CGameHandler;
|
||||
struct SharedMemory;
|
||||
|
||||
namespace boost
|
||||
{
|
||||
@ -43,8 +44,10 @@ typedef boost::asio::basic_stream_socket < boost::asio::ip::tcp , boost::asio::s
|
||||
|
||||
class CVCMIServer
|
||||
{
|
||||
ui16 port;
|
||||
boost::asio::io_service *io;
|
||||
TAcceptor * acceptor;
|
||||
SharedMemory * shared;
|
||||
|
||||
CConnection *firstConnection;
|
||||
public:
|
||||
@ -73,7 +76,6 @@ public:
|
||||
std::list<CPackForSelectionScreen*> toAnnounce;
|
||||
boost::recursive_mutex mx;
|
||||
|
||||
//std::vector<CMapInfo> maps;
|
||||
TAcceptor *acceptor;
|
||||
TSocket *upcomingConnection;
|
||||
|
||||
|
@ -65,6 +65,12 @@ bool CloseServer::applyGh( CGameHandler *gh )
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LeaveGame::applyGh( CGameHandler *gh )
|
||||
{
|
||||
gh->playerLeftGame(c->connectionID);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EndTurn::applyGh( CGameHandler *gh )
|
||||
{
|
||||
PlayerColor player = GS(gh)->currentPlayer;
|
||||
@ -277,8 +283,11 @@ bool CastAdvSpell::applyGh( CGameHandler *gh )
|
||||
|
||||
bool PlayerMessage::applyGh( CGameHandler *gh )
|
||||
{
|
||||
ERROR_IF_NOT(player);
|
||||
if(gh->getPlayerAt(c) != player) ERROR_AND_RETURN;
|
||||
if(!player.isSpectator()) // TODO: clearly not a great way to verify permissions
|
||||
{
|
||||
ERROR_IF_NOT(player);
|
||||
if(gh->getPlayerAt(c) != player) ERROR_AND_RETURN;
|
||||
}
|
||||
gh->playerMessage(player,text, currObj);
|
||||
return true;
|
||||
}
|
||||
|
@ -115,9 +115,10 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View)
|
||||
logGlobal->info("CMapEditManager_DrawTerrain_View start");
|
||||
try
|
||||
{
|
||||
const ResourceID testMap("test/TerrainViewTest", EResType::MAP);
|
||||
// Load maps and json config
|
||||
const auto originalMap = CMapService::loadMap("test/TerrainViewTest");
|
||||
auto map = CMapService::loadMap("test/TerrainViewTest");
|
||||
const auto originalMap = CMapService::loadMap(testMap);
|
||||
auto map = CMapService::loadMap(testMap);
|
||||
logGlobal->info("Loaded test map successfully.");
|
||||
|
||||
// Validate edit manager
|
||||
|
Loading…
Reference in New Issue
Block a user