mirror of
https://github.com/vcmi/vcmi.git
synced 2025-04-11 11:31:52 +02:00
Merge pull request #402 from vcmi/feature/multiplayer
Refactoring of all pre-gameplay UI and networking code. This will break some things, but I'll be able to fix them without constant rebasing.
This commit is contained in:
commit
1a6f456ac0
@ -95,7 +95,7 @@ void CCallback::endTurn()
|
||||
{
|
||||
logGlobal->trace("Player %d ended his turn.", player.get().getNum());
|
||||
EndTurn pack;
|
||||
sendRequest(&pack); //report that we ended turn
|
||||
sendRequest(&pack);
|
||||
}
|
||||
int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
|
||||
{
|
||||
@ -174,7 +174,7 @@ int CBattleCallback::battleMakeAction(BattleAction* action)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CBattleCallback::sendRequest(const CPack *request)
|
||||
int CBattleCallback::sendRequest(const CPackForServer * request)
|
||||
{
|
||||
int requestID = cl->sendRequest(request, *player);
|
||||
if(waitTillRealize)
|
||||
@ -262,8 +262,8 @@ void CCallback::save( const std::string &fname )
|
||||
void CCallback::sendMessage(const std::string &mess, const CGObjectInstance * currentObject)
|
||||
{
|
||||
ASSERT_IF_CALLED_WITH_PLAYER
|
||||
PlayerMessage pm(*player, mess, currentObject? currentObject->id : ObjectInstanceID(-1));
|
||||
sendRequest(&(CPackForClient&)pm);
|
||||
PlayerMessage pm(mess, currentObject? currentObject->id : ObjectInstanceID(-1));
|
||||
sendRequest(&pm);
|
||||
}
|
||||
|
||||
void CCallback::buildBoat( const IShipyard *obj )
|
||||
@ -327,14 +327,6 @@ void CCallback::castSpell(const CGHeroInstance *hero, SpellID spellID, const int
|
||||
sendRequest(&cas);
|
||||
}
|
||||
|
||||
void CCallback::unregisterAllInterfaces()
|
||||
{
|
||||
for (auto& pi : cl->playerint)
|
||||
pi.second->finish();
|
||||
cl->playerint.clear();
|
||||
cl->battleints.clear();
|
||||
}
|
||||
|
||||
int CCallback::mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
|
||||
{
|
||||
if(s1->getCreature(p1) == s2->getCreature(p2))
|
||||
|
@ -78,12 +78,12 @@ public:
|
||||
virtual void buildBoat(const IShipyard *obj) = 0;
|
||||
};
|
||||
|
||||
struct CPack;
|
||||
struct CPackForServer;
|
||||
|
||||
class CBattleCallback : public IBattleCallback, public CPlayerBattleCallback
|
||||
{
|
||||
protected:
|
||||
int sendRequest(const CPack *request); //returns requestID (that'll be matched to requestID in PackageApplied)
|
||||
int sendRequest(const CPackForServer * request); //returns requestID (that'll be matched to requestID in PackageApplied)
|
||||
CClient *cl;
|
||||
|
||||
public:
|
||||
@ -95,6 +95,7 @@ public:
|
||||
friend class CClient;
|
||||
};
|
||||
|
||||
class CPlayerInterface;
|
||||
class CCallback : public CPlayerSpecificInfoCallback, public IGameActionCallback, public CBattleCallback
|
||||
{
|
||||
public:
|
||||
@ -114,8 +115,6 @@ public:
|
||||
void unregisterGameInterface(std::shared_ptr<IGameEventsReceiver> gameEvents);
|
||||
void unregisterBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents);
|
||||
|
||||
void unregisterAllInterfaces(); //stops delivering information about game events to player interfaces -> can be called ONLY after victory/loss
|
||||
|
||||
//commands
|
||||
bool moveHero(const CGHeroInstance *h, int3 dst, bool transit = false) override; //dst must be free, neighbouring tile (this function can move hero only by one tile)
|
||||
bool teleportHero(const CGHeroInstance *who, const CGTownInstance *where);
|
||||
|
@ -20,6 +20,11 @@ GENERAL:
|
||||
- BLOCK_MAGIC_BELOW - allows blocking spells below particular spell level. HotA cape artifact can be implemented with this
|
||||
- DESTRUCTION - creature ability for killing extra units after hit, configurable
|
||||
|
||||
MULTIPLAYER:
|
||||
* Loading support. Save from single client could be used to load all clients.
|
||||
* Restart support. All clients will restart together on same server.
|
||||
* Hotseat mixed with network game. Multiple colors can be controlled by each client.
|
||||
|
||||
SPELLS:
|
||||
* Implemented cumulative effects for spells
|
||||
|
||||
|
3
Global.h
3
Global.h
@ -172,6 +172,9 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
|
||||
#if defined(_MSC_VER) && (_MSC_VER == 1900 || _MSC_VER == 1910 || _MSC_VER == 1911)
|
||||
#define BOOST_NO_CXX11_VARIADIC_TEMPLATES //Variadic templates are buggy in VS2015 and VS2017, so turn this off to avoid compile errors
|
||||
#endif
|
||||
#if BOOST_VERSION >= 106600
|
||||
#define BOOST_ASIO_ENABLE_OLD_SERVICES
|
||||
#endif
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/any.hpp>
|
||||
|
@ -14,8 +14,10 @@
|
||||
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
|
||||
const CGameInfo * CGI; //game info for general use
|
||||
const CGameInfo * CGI;
|
||||
CClientState * CCS = nullptr;
|
||||
CServerHandler * CSH;
|
||||
|
||||
|
||||
CGameInfo::CGameInfo()
|
||||
{
|
||||
|
@ -9,7 +9,6 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../lib/ConstTransitivePtr.h"
|
||||
|
||||
class CModHandler;
|
||||
@ -30,6 +29,7 @@ class CConsoleHandler;
|
||||
class CCursorHandler;
|
||||
class CGameState;
|
||||
class IMainVideoPlayer;
|
||||
class CServerHandler;
|
||||
|
||||
class CMap;
|
||||
|
||||
|
302
client/CMT.cpp
302
client/CMT.cpp
@ -20,7 +20,8 @@
|
||||
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/FileStream.h"
|
||||
#include "CPreGame.h"
|
||||
#include "mainmenu/CMainMenu.h"
|
||||
#include "lobby/CSelectionBase.h"
|
||||
#include "windows/CCastleInterface.h"
|
||||
#include "../lib/CConsoleHandler.h"
|
||||
#include "gui/CCursorHandler.h"
|
||||
@ -54,6 +55,12 @@
|
||||
#include "../lib/StringConstants.h"
|
||||
#include "../lib/CPlayerState.h"
|
||||
#include "gui/CAnimation.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "CServerHandler.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include "mainmenu/CPrologEpilogVideo.h"
|
||||
|
||||
#ifdef VCMI_WINDOWS
|
||||
#include "SDL_syswm.h"
|
||||
@ -75,7 +82,6 @@ namespace bfs = boost::filesystem;
|
||||
std::string NAME_AFFIX = "client";
|
||||
std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name
|
||||
CGuiHandler GH;
|
||||
static CClient *client = nullptr;
|
||||
|
||||
int preferredDriverIndex = -1;
|
||||
SDL_Window * mainWindow = nullptr;
|
||||
@ -91,7 +97,6 @@ SDL_Surface *screen = nullptr, //main screen surface
|
||||
std::queue<SDL_Event> events;
|
||||
boost::mutex eventsM;
|
||||
|
||||
CondSh<bool> serverAlive(false);
|
||||
static po::variables_map vm;
|
||||
|
||||
//static bool setResolution = false; //set by event handling thread after resolution is adjusted
|
||||
@ -102,9 +107,6 @@ static void setScreenRes(int w, int h, int bpp, bool fullscreen, int displayInde
|
||||
void dispose();
|
||||
void playIntro();
|
||||
static void mainLoop();
|
||||
//void requestChangingResolution();
|
||||
void startGame(StartInfo * options, CConnection *serv = nullptr);
|
||||
void endGame();
|
||||
|
||||
#ifndef VCMI_WINDOWS
|
||||
#ifndef _GNU_SOURCE
|
||||
@ -113,54 +115,6 @@ 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;
|
||||
try //attempt retrieving start info from given file
|
||||
{
|
||||
if(fname.empty() || !bfs::exists(fname))
|
||||
throw std::runtime_error("Startfile \"" + fname.string() + "\" does not exist!");
|
||||
|
||||
CLoadFile out(fname);
|
||||
if (!out.sfile || !*out.sfile)
|
||||
throw std::runtime_error("Cannot read from startfile \"" + fname.string() +"\"!");
|
||||
out >> si;
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
logGlobal->error("Failed to start from the file: %s. Error: %s. Falling back to main menu.", fname, e.what());
|
||||
GH.curInt = CGPreGame::create();
|
||||
return;
|
||||
}
|
||||
|
||||
while(GH.topInt())
|
||||
GH.popIntTotally(GH.topInt());
|
||||
startGame(&si);
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
CStopWatch tmh, pomtime;
|
||||
@ -243,15 +197,15 @@ int main(int argc, char * argv[])
|
||||
("version,v", "display version information and exit")
|
||||
("disable-shm", "force disable shared memory usage")
|
||||
("enable-shm-uuid", "use UUID for shared memory identifier")
|
||||
("start", po::value<bfs::path>(), "starts game from saved StartInfo file")
|
||||
("testmap", po::value<std::string>(), "")
|
||||
("testsave", 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")
|
||||
("onlyAI", "allow to run without human player, all players will be default AI")
|
||||
("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")
|
||||
@ -259,12 +213,6 @@ int main(int argc, char * argv[])
|
||||
("disable-video", "disable video player")
|
||||
("nointro,i", "skips intro movies")
|
||||
("donotstartserver,d","do not attempt to start server and just connect to it instead server")
|
||||
("loadserver","specifies we are the multiplayer server for loaded games")
|
||||
("loadnumplayers",po::value<int>(),"specifies the number of players connecting to a multiplayer game")
|
||||
("loadhumanplayerindices",po::value<std::vector<int>>(),"Indexes of human players (0=Red, etc.)")
|
||||
("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")
|
||||
("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");
|
||||
@ -317,6 +265,17 @@ int main(int argc, char * argv[])
|
||||
session["headless"].Bool() = true;
|
||||
session["onlyai"].Bool() = true;
|
||||
}
|
||||
else if(vm.count("spectate"))
|
||||
{
|
||||
session["spectate"].Bool() = true;
|
||||
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>();
|
||||
}
|
||||
// Server settings
|
||||
session["donotstartserver"].Bool() = vm.count("donotstartserver");
|
||||
|
||||
@ -441,6 +400,7 @@ int main(int argc, char * argv[])
|
||||
|
||||
CCS = new CClientState();
|
||||
CGI = new CGameInfo(); //contains all global informations about game (texts, lodHandlers, map handler etc.)
|
||||
CSH = new CServerHandler();
|
||||
// Initialize video
|
||||
#ifdef DISABLE_VIDEO
|
||||
CCS->videoh = new CEmptyVideoPlayer();
|
||||
@ -453,15 +413,17 @@ int main(int argc, char * argv[])
|
||||
|
||||
logGlobal->info("\tInitializing video: %d ms", pomtime.getDiff());
|
||||
|
||||
//initializing audio
|
||||
CCS->soundh = new CSoundHandler();
|
||||
CCS->soundh->init();
|
||||
CCS->soundh->setVolume(settings["general"]["sound"].Float());
|
||||
CCS->musich = new CMusicHandler();
|
||||
CCS->musich->init();
|
||||
CCS->musich->setVolume(settings["general"]["music"].Float());
|
||||
logGlobal->info("Initializing screen and sound handling: %d ms", pomtime.getDiff());
|
||||
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
//initializing audio
|
||||
CCS->soundh = new CSoundHandler();
|
||||
CCS->soundh->init();
|
||||
CCS->soundh->setVolume(settings["general"]["sound"].Float());
|
||||
CCS->musich = new CMusicHandler();
|
||||
CCS->musich->init();
|
||||
CCS->musich->setVolume(settings["general"]["music"].Float());
|
||||
logGlobal->info("Initializing screen and sound handling: %d ms", pomtime.getDiff());
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
// Ctrl+click should be treated as a right click on Mac OS X
|
||||
SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1");
|
||||
@ -500,41 +462,21 @@ int main(int argc, char * argv[])
|
||||
session["oneGoodAI"].Bool() = vm.count("oneGoodAI");
|
||||
session["aiSolo"].Bool() = false;
|
||||
|
||||
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>();
|
||||
session["onlyai"].Bool() = true;
|
||||
boost::thread(&CServerHandler::debugStartTest, CSH, session["testmap"].String(), false);
|
||||
}
|
||||
|
||||
session["spectate"].Bool() = vm.count("spectate");
|
||||
if(session["spectate"].Bool())
|
||||
else if(vm.count("testsave"))
|
||||
{
|
||||
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());
|
||||
session["testsave"].String() = vm["testsave"].as<std::string>();
|
||||
session["onlyai"].Bool() = true;
|
||||
boost::thread(&CServerHandler::debugStartTest, CSH, session["testsave"].String(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!fileToStartFrom.empty() && bfs::exists(fileToStartFrom))
|
||||
startGameFromFile(fileToStartFrom); //ommit pregame and start the game using settings from file
|
||||
else
|
||||
{
|
||||
if(!fileToStartFrom.empty())
|
||||
{
|
||||
logGlobal->warn("Warning: cannot find given file to start from (%s). Falling back to main menu.", fileToStartFrom.string());
|
||||
}
|
||||
GH.curInt = CGPreGame::create(); //will set CGP pointer to itself
|
||||
}
|
||||
GH.curInt = CMainMenu::create();
|
||||
}
|
||||
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
@ -614,8 +556,8 @@ void processCommand(const std::string &message)
|
||||
}
|
||||
else
|
||||
{
|
||||
if(client && client->erm)
|
||||
client->erm->executeUserCommand(message);
|
||||
if(CSH->client && CSH->client->erm)
|
||||
CSH->client->erm->executeUserCommand(message);
|
||||
std::cout << "erm>";
|
||||
}
|
||||
}
|
||||
@ -665,21 +607,21 @@ void processCommand(const std::string &message)
|
||||
}
|
||||
else if(cn=="save")
|
||||
{
|
||||
if(!client)
|
||||
if(!CSH->client)
|
||||
{
|
||||
std::cout << "Game in not active";
|
||||
return;
|
||||
}
|
||||
std::string fname;
|
||||
readed >> fname;
|
||||
client->save(fname);
|
||||
CSH->client->save(fname);
|
||||
}
|
||||
// else if(cn=="load")
|
||||
// {
|
||||
// // TODO: this code should end the running game and manage to call startGame instead
|
||||
// std::string fname;
|
||||
// readed >> fname;
|
||||
// client->loadGame(fname);
|
||||
// CSH->client->loadGame(fname);
|
||||
// }
|
||||
else if(message=="convert txt")
|
||||
{
|
||||
@ -861,22 +803,6 @@ void processCommand(const std::string &message)
|
||||
logGlobal->info("Option %s disabled!", what);
|
||||
}
|
||||
}
|
||||
else if(cn == "sinfo")
|
||||
{
|
||||
std::string fname;
|
||||
readed >> fname;
|
||||
if(fname.size() && SEL)
|
||||
{
|
||||
CSaveFile out(fname);
|
||||
out << SEL->sInfo;
|
||||
}
|
||||
}
|
||||
else if(cn == "start")
|
||||
{
|
||||
std::string fname;
|
||||
readed >> fname;
|
||||
startGameFromFile(fname);
|
||||
}
|
||||
else if(cn == "unlock")
|
||||
{
|
||||
std::string mxname;
|
||||
@ -935,8 +861,8 @@ void processCommand(const std::string &message)
|
||||
{
|
||||
YourTurn yt;
|
||||
yt.player = player;
|
||||
yt.daysWithoutCastle = client->getPlayer(player)->daysWithoutCastle;
|
||||
yt.applyCl(client);
|
||||
yt.daysWithoutCastle = CSH->client->getPlayer(player)->daysWithoutCastle;
|
||||
yt.applyCl(CSH->client);
|
||||
};
|
||||
|
||||
Settings session = settings.write["session"];
|
||||
@ -947,7 +873,7 @@ void processCommand(const std::string &message)
|
||||
else if(cn == "gosolo")
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
if(!client)
|
||||
if(!CSH->client)
|
||||
{
|
||||
std::cout << "Game in not active";
|
||||
return;
|
||||
@ -955,23 +881,23 @@ void processCommand(const std::string &message)
|
||||
PlayerColor color;
|
||||
if(session["aiSolo"].Bool())
|
||||
{
|
||||
for(auto & elem : client->gameState()->players)
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human)
|
||||
client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
color = LOCPLINT->playerID;
|
||||
removeGUI();
|
||||
for(auto & elem : client->gameState()->players)
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human)
|
||||
{
|
||||
auto AiToGive = client->aiNameForPlayer(*client->getPlayerSettings(elem.first), false);
|
||||
auto AiToGive = CSH->client->aiNameForPlayer(*CSH->client->getPlayerSettings(elem.first), false);
|
||||
logNetwork->info("Player %s will be lead by %s", elem.first, AiToGive);
|
||||
client->installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), elem.first);
|
||||
CSH->client->installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), elem.first);
|
||||
}
|
||||
}
|
||||
GH.totalRedraw();
|
||||
@ -986,7 +912,7 @@ void processCommand(const std::string &message)
|
||||
boost::to_lower(colorName);
|
||||
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
if(!client)
|
||||
if(!CSH->client)
|
||||
{
|
||||
std::cout << "Game in not active";
|
||||
return;
|
||||
@ -994,7 +920,7 @@ void processCommand(const std::string &message)
|
||||
PlayerColor color;
|
||||
if(LOCPLINT)
|
||||
color = LOCPLINT->playerID;
|
||||
for(auto & elem : client->gameState()->players)
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human || (colorName.length() &&
|
||||
elem.first.getNum() != vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, colorName)))
|
||||
@ -1003,7 +929,7 @@ void processCommand(const std::string &message)
|
||||
}
|
||||
|
||||
removeGUI();
|
||||
client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
}
|
||||
GH.totalRedraw();
|
||||
if(color != PlayerColor::NEUTRAL)
|
||||
@ -1290,46 +1216,59 @@ static void handleEvent(SDL_Event & ev)
|
||||
{
|
||||
switch(ev.user.code)
|
||||
{
|
||||
case FORCE_QUIT:
|
||||
case EUserEvent::FORCE_QUIT:
|
||||
{
|
||||
handleQuit(false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case RETURN_TO_MAIN_MENU:
|
||||
case EUserEvent::RETURN_TO_MAIN_MENU:
|
||||
{
|
||||
endGame();
|
||||
GH.curInt = CGPreGame::create();
|
||||
CSH->endGameplay();
|
||||
GH.curInt = CMainMenu::create();
|
||||
GH.defActionsDef = 63;
|
||||
}
|
||||
break;
|
||||
case RESTART_GAME:
|
||||
case EUserEvent::RESTART_GAME:
|
||||
{
|
||||
StartInfo si = *client->getStartInfo(true);
|
||||
si.seedToBeUsed = 0; //server gives new random generator seed if 0
|
||||
endGame();
|
||||
startGame(&si);
|
||||
CSH->sendStartGame();
|
||||
}
|
||||
break;
|
||||
case PREPARE_RESTART_CAMPAIGN:
|
||||
case EUserEvent::CAMPAIGN_START_SCENARIO:
|
||||
{
|
||||
auto si = reinterpret_cast<StartInfo *>(ev.user.data1);
|
||||
endGame();
|
||||
startGame(si);
|
||||
CSH->endGameplay();
|
||||
GH.curInt = CMainMenu::create();
|
||||
auto ourCampaign = std::shared_ptr<CCampaignState>(reinterpret_cast<CCampaignState *>(ev.user.data1));
|
||||
auto & epilogue = ourCampaign->camp->scenarios[ourCampaign->mapsConquered.back()].epilog;
|
||||
auto finisher = [=]()
|
||||
{
|
||||
if(ourCampaign->mapsRemaining.size())
|
||||
{
|
||||
CMM->openCampaignLobby(ourCampaign);
|
||||
}
|
||||
};
|
||||
if(epilogue.hasPrologEpilog)
|
||||
{
|
||||
GH.pushInt(new CPrologEpilogVideo(epilogue, finisher));
|
||||
}
|
||||
else
|
||||
{
|
||||
finisher();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RETURN_TO_MENU_LOAD:
|
||||
endGame();
|
||||
CGPreGame::create();
|
||||
case EUserEvent::RETURN_TO_MENU_LOAD:
|
||||
CSH->endGameplay();
|
||||
CMainMenu::create();
|
||||
GH.defActionsDef = 63;
|
||||
CGP->update();
|
||||
CGP->menu->switchToTab(vstd::find_pos(CGP->menu->menuNameToEntry, "load"));
|
||||
GH.curInt = CGP;
|
||||
CMM->update();
|
||||
CMM->menu->switchToTab(vstd::find_pos(CMM->menu->menuNameToEntry, "load"));
|
||||
GH.curInt = CMM;
|
||||
break;
|
||||
case FULLSCREEN_TOGGLED:
|
||||
case EUserEvent::FULLSCREEN_TOGGLED:
|
||||
fullScreenChanged();
|
||||
break;
|
||||
case INTERFACE_CHANGED:
|
||||
case EUserEvent::INTERFACE_CHANGED:
|
||||
if(LOCPLINT)
|
||||
LOCPLINT->updateAmbientSounds();
|
||||
break;
|
||||
@ -1360,7 +1299,7 @@ static void handleEvent(SDL_Event & ev)
|
||||
static void mainLoop()
|
||||
{
|
||||
SettingsListener resChanged = settings.listen["video"]["fullscreen"];
|
||||
resChanged([](const JsonNode &newState){ CGuiHandler::pushSDLEvent(SDL_USEREVENT, FULLSCREEN_TOGGLED); });
|
||||
resChanged([](const JsonNode &newState){ CGuiHandler::pushSDLEvent(SDL_USEREVENT, EUserEvent::FULLSCREEN_TOGGLED); });
|
||||
|
||||
inGuiThread.reset(new bool(true));
|
||||
GH.mainFPSmng->init();
|
||||
@ -1374,69 +1313,18 @@ static void mainLoop()
|
||||
handleEvent(ev);
|
||||
}
|
||||
|
||||
CSH->applyPacksOnLobbyScreen();
|
||||
GH.renderFrame();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void startGame(StartInfo * options, CConnection *serv)
|
||||
{
|
||||
if(!settings["session"]["donotstartserver"].Bool())
|
||||
{
|
||||
serverAlive.waitWhileTrue();
|
||||
serverAlive.setn(true);
|
||||
}
|
||||
|
||||
if(settings["session"]["onlyai"].Bool())
|
||||
{
|
||||
auto ais = vm.count("ai") ? vm["ai"].as<std::vector<std::string>>() : std::vector<std::string>();
|
||||
|
||||
int i = 0;
|
||||
|
||||
|
||||
for(auto & elem : options->playerInfos)
|
||||
{
|
||||
elem.second.playerID = PlayerSettings::PLAYER_AI;
|
||||
if(i < ais.size())
|
||||
elem.second.name = ais[i++];
|
||||
}
|
||||
}
|
||||
|
||||
client = new CClient();
|
||||
CPlayerInterface::howManyPeople = 0;
|
||||
switch(options->mode) //new game
|
||||
{
|
||||
case StartInfo::NEW_GAME:
|
||||
case StartInfo::CAMPAIGN:
|
||||
client->newGame(serv, options);
|
||||
break;
|
||||
case StartInfo::LOAD_GAME:
|
||||
std::string fname = options->mapname;
|
||||
boost::algorithm::erase_last(fname,".vlgm1");
|
||||
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<ui16>() : CServerHandler::getDefaultPort());
|
||||
break;
|
||||
}
|
||||
{
|
||||
TLockGuard _(client->connectionHandlerMutex);
|
||||
client->connectionHandler = make_unique<boost::thread>(&CClient::run, client);
|
||||
}
|
||||
}
|
||||
|
||||
void endGame()
|
||||
{
|
||||
client->endGame();
|
||||
vstd::clear_pointer(client);
|
||||
}
|
||||
|
||||
void handleQuit(bool ask)
|
||||
{
|
||||
auto quitApplication = []()
|
||||
{
|
||||
if(client)
|
||||
endGame();
|
||||
if(CSH->client)
|
||||
CSH->endGameplay();
|
||||
dispose();
|
||||
vstd::clear_pointer(console);
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(750));
|
||||
@ -1450,7 +1338,7 @@ void handleQuit(bool ask)
|
||||
exit(0);
|
||||
};
|
||||
|
||||
if(client && LOCPLINT && ask)
|
||||
if(CSH->client && LOCPLINT && ask)
|
||||
{
|
||||
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[69], quitApplication, 0);
|
||||
|
@ -9,7 +9,6 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include <SDL_render.h>
|
||||
#include "../lib/CondSh.h"
|
||||
|
||||
extern SDL_Texture * screenTexture;
|
||||
|
||||
@ -20,7 +19,5 @@ 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 CondSh<bool> serverAlive; //used to prevent game start from executing if server is already running
|
||||
|
||||
void removeGUI();
|
||||
void handleQuit(bool ask = true);
|
||||
|
@ -43,6 +43,20 @@ set(client_SRCS
|
||||
windows/InfoWindows.cpp
|
||||
windows/QuickRecruitmentWindow.cpp
|
||||
|
||||
mainmenu/CMainMenu.cpp
|
||||
mainmenu/CCampaignScreen.cpp
|
||||
mainmenu/CreditsScreen.cpp
|
||||
mainmenu/CPrologEpilogVideo.cpp
|
||||
|
||||
lobby/CBonusSelection.cpp
|
||||
lobby/CSelectionBase.cpp
|
||||
lobby/CLobbyScreen.cpp
|
||||
lobby/CSavingScreen.cpp
|
||||
lobby/CScenarioInfoScreen.cpp
|
||||
lobby/OptionsTab.cpp
|
||||
lobby/RandomMapTab.cpp
|
||||
lobby/SelectionTab.cpp
|
||||
|
||||
CBitmapHandler.cpp
|
||||
CreatureCostBox.cpp
|
||||
CGameInfo.cpp
|
||||
@ -51,11 +65,12 @@ set(client_SRCS
|
||||
CMT.cpp
|
||||
CMusicHandler.cpp
|
||||
CPlayerInterface.cpp
|
||||
CPreGame.cpp
|
||||
CVideoHandler.cpp
|
||||
CServerHandler.cpp
|
||||
Graphics.cpp
|
||||
mapHandler.cpp
|
||||
NetPacksClient.cpp
|
||||
NetPacksLobbyClient.cpp
|
||||
SDLRWwrapper.cpp
|
||||
)
|
||||
|
||||
@ -100,6 +115,20 @@ set(client_HEADERS
|
||||
windows/InfoWindows.h
|
||||
windows/QuickRecruitmentWindow.h
|
||||
|
||||
mainmenu/CMainMenu.h
|
||||
mainmenu/CCampaignScreen.h
|
||||
mainmenu/CreditsScreen.h
|
||||
mainmenu/CPrologEpilogVideo.h
|
||||
|
||||
lobby/CBonusSelection.h
|
||||
lobby/CSelectionBase.h
|
||||
lobby/CLobbyScreen.h
|
||||
lobby/CSavingScreen.h
|
||||
lobby/CScenarioInfoScreen.h
|
||||
lobby/OptionsTab.h
|
||||
lobby/RandomMapTab.h
|
||||
lobby/SelectionTab.h
|
||||
|
||||
CBitmapHandler.h
|
||||
CreatureCostBox.h
|
||||
CGameInfo.h
|
||||
@ -108,8 +137,8 @@ set(client_HEADERS
|
||||
CMT.h
|
||||
CMusicHandler.h
|
||||
CPlayerInterface.h
|
||||
CPreGame.h
|
||||
CVideoHandler.h
|
||||
CServerHandler.h
|
||||
Graphics.h
|
||||
mapHandler.h
|
||||
resource.h
|
||||
|
@ -49,13 +49,16 @@
|
||||
#include "mapHandler.h"
|
||||
#include "../lib/CStopWatch.h"
|
||||
#include "../lib/StartInfo.h"
|
||||
#include "../lib/CGameState.h"
|
||||
#include "../lib/CPlayerState.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "windows/InfoWindows.h"
|
||||
#include "../lib/UnlockGuard.h"
|
||||
#include "../lib/CPathfinder.h"
|
||||
#include <SDL.h>
|
||||
#include "CServerHandler.h"
|
||||
// FIXME: only needed for CGameState::mutex
|
||||
#include "../lib/CGameState.h"
|
||||
|
||||
|
||||
// The macro below is used to mark functions that are called by client when game state changes.
|
||||
@ -90,8 +93,6 @@ CBattleInterface * CPlayerInterface::battleInt;
|
||||
enum EMoveState {STOP_MOVE, WAITING_MOVE, CONTINUE_MOVE, DURING_MOVE};
|
||||
CondSh<EMoveState> stillMoveHero(STOP_MOVE); //used during hero movement
|
||||
|
||||
int CPlayerInterface::howManyPeople = 0;
|
||||
|
||||
static bool objectBlitOrderSorter(const TerrainTileObject & a, const TerrainTileObject & b)
|
||||
{
|
||||
return CMapHandler::compareObjectBlitOrder(a.obj, b.obj);
|
||||
@ -114,7 +115,6 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player)
|
||||
logGlobal->trace("\tHuman player interface for player %s being constructed", Player.getStr());
|
||||
destinationTeleport = ObjectInstanceID();
|
||||
destinationTeleportPos = int3(-1);
|
||||
howManyPeople++;
|
||||
GH.defActionsDef = 0;
|
||||
LOCPLINT = this;
|
||||
curAction = nullptr;
|
||||
@ -139,7 +139,6 @@ CPlayerInterface::~CPlayerInterface()
|
||||
{
|
||||
CCS->soundh->ambientStopAllChannels();
|
||||
logGlobal->trace("\tHuman player interface for player %s being destructed", playerID.getStr());
|
||||
//howManyPeople--;
|
||||
delete showingDialog;
|
||||
delete cingconsole;
|
||||
if (LOCPLINT == this)
|
||||
@ -148,9 +147,7 @@ CPlayerInterface::~CPlayerInterface()
|
||||
void CPlayerInterface::init(std::shared_ptr<CCallback> CB)
|
||||
{
|
||||
cb = CB;
|
||||
|
||||
if (!towns.size() && !wanderingHeroes.size())
|
||||
initializeHeroTownList();
|
||||
initializeHeroTownList();
|
||||
|
||||
// always recreate advmap interface to avoid possible memory-corruption bugs
|
||||
if (adventureInt)
|
||||
@ -170,7 +167,7 @@ void CPlayerInterface::yourTurn()
|
||||
std::string prefix = settings["session"]["saveprefix"].String();
|
||||
if (firstCall)
|
||||
{
|
||||
if (howManyPeople == 1)
|
||||
if(CSH->howManyPlayerInterfaces() == 1)
|
||||
adventureInt->setPlayer(playerID);
|
||||
|
||||
autosaveCount = getLastIndex(prefix + "Autosave_");
|
||||
@ -192,7 +189,7 @@ void CPlayerInterface::yourTurn()
|
||||
if (adventureInt->player != playerID)
|
||||
adventureInt->setPlayer(playerID);
|
||||
|
||||
if (howManyPeople > 1) //hot seat message
|
||||
if (CSH->howManyPlayerInterfaces() > 1) //hot seat message
|
||||
{
|
||||
adventureInt->startHotSeatWait(playerID);
|
||||
|
||||
@ -1581,45 +1578,20 @@ void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
|
||||
|
||||
void CPlayerInterface::initializeHeroTownList()
|
||||
{
|
||||
std::vector<const CGHeroInstance*> allHeroes = cb->getHeroesInfo();
|
||||
/*
|
||||
std::vector <const CGHeroInstance *> newWanderingHeroes;
|
||||
|
||||
//applying current heroes order to new heroes info
|
||||
int j;
|
||||
for (int i = 0; i < wanderingHeroes.size(); i++)
|
||||
if ((j = vstd::find_pos(allHeroes, wanderingHeroes[i])) >= 0)
|
||||
if (!allHeroes[j]->inTownGarrison)
|
||||
{
|
||||
newWanderingHeroes += allHeroes[j];
|
||||
allHeroes -= allHeroes[j];
|
||||
}
|
||||
//all the rest of new heroes go the end of the list
|
||||
wanderingHeroes.clear();
|
||||
wanderingHeroes = newWanderingHeroes;
|
||||
newWanderingHeroes.clear();*/
|
||||
|
||||
for (auto & allHeroe : allHeroes)
|
||||
if (!allHeroe->inTownGarrison)
|
||||
wanderingHeroes.push_back(allHeroe);
|
||||
|
||||
std::vector<const CGTownInstance*> allTowns = cb->getTownsInfo();
|
||||
/*
|
||||
std::vector<const CGTownInstance*> newTowns;
|
||||
for (int i = 0; i < towns.size(); i++)
|
||||
if ((j = vstd::find_pos(allTowns, towns[i])) >= 0)
|
||||
if(!wanderingHeroes.size())
|
||||
{
|
||||
std::vector<const CGHeroInstance*> heroes = cb->getHeroesInfo();
|
||||
for(auto & hero : heroes)
|
||||
{
|
||||
newTowns += allTowns[j];
|
||||
allTowns -= allTowns[j];
|
||||
if(!hero->inTownGarrison)
|
||||
wanderingHeroes.push_back(hero);
|
||||
}
|
||||
}
|
||||
|
||||
towns.clear();
|
||||
towns = newTowns;
|
||||
newTowns.clear();*/
|
||||
for (auto & allTown : allTowns)
|
||||
towns.push_back(allTown);
|
||||
if(!towns.size())
|
||||
towns = cb->getTownsInfo();
|
||||
|
||||
if (adventureInt)
|
||||
if(adventureInt)
|
||||
adventureInt->updateNextHero(nullptr);
|
||||
}
|
||||
|
||||
@ -1728,7 +1700,7 @@ void CPlayerInterface::update()
|
||||
return;
|
||||
|
||||
//if there are any waiting dialogs, show them
|
||||
if ((howManyPeople <= 1 || makingTurn) && !dialogs.empty() && !showingDialog->get())
|
||||
if ((CSH->howManyPlayerInterfaces() <= 1 || makingTurn) && !dialogs.empty() && !showingDialog->get())
|
||||
{
|
||||
showingDialog->set(true);
|
||||
GH.pushInt(dialogs.front());
|
||||
@ -2227,37 +2199,27 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
|
||||
waitForAllDialogs(); //wait till all dialogs are displayed and closed
|
||||
}
|
||||
|
||||
--howManyPeople;
|
||||
|
||||
if(howManyPeople == 0 && !settings["session"]["spectate"].Bool()) //all human players eliminated
|
||||
if(CSH->howManyPlayerInterfaces() == 1 && !settings["session"]["spectate"].Bool()) //all human players eliminated
|
||||
{
|
||||
if (adventureInt)
|
||||
if(adventureInt)
|
||||
{
|
||||
GH.terminate_cond->setn(true);
|
||||
adventureInt->deactivate();
|
||||
if (GH.topInt() == adventureInt)
|
||||
GH.popInt(adventureInt);
|
||||
delete adventureInt;
|
||||
adventureInt = nullptr;
|
||||
vstd::clear_pointer(adventureInt);
|
||||
}
|
||||
}
|
||||
|
||||
if (cb->getStartInfo()->mode == StartInfo::CAMPAIGN)
|
||||
if (victoryLossCheckResult.victory() && LOCPLINT == this)
|
||||
{
|
||||
// if you lose the campaign go back to the main menu
|
||||
// campaign wins are handled in proposeNextMission
|
||||
if (victoryLossCheckResult.loss()) requestReturningToMainMenu();
|
||||
// end game if current human player has won
|
||||
requestReturningToMainMenu(true);
|
||||
}
|
||||
else
|
||||
else if(CSH->howManyPlayerInterfaces() == 1 && !settings["session"]["spectate"].Bool())
|
||||
{
|
||||
if(howManyPeople == 0 && !settings["session"]["spectate"].Bool()) //all human players eliminated
|
||||
{
|
||||
requestReturningToMainMenu();
|
||||
}
|
||||
else if (victoryLossCheckResult.victory() && LOCPLINT == this) // end game if current human player has won
|
||||
{
|
||||
requestReturningToMainMenu();
|
||||
}
|
||||
//all human players eliminated
|
||||
requestReturningToMainMenu(false);
|
||||
}
|
||||
|
||||
if (GH.curInt == this) GH.curInt = nullptr;
|
||||
@ -2378,7 +2340,7 @@ void CPlayerInterface::acceptTurn()
|
||||
}
|
||||
waitWhileDialog();
|
||||
|
||||
if (howManyPeople > 1)
|
||||
if(CSH->howManyPlayerInterfaces() > 1)
|
||||
adventureInt->startTurn();
|
||||
|
||||
adventureInt->heroList.update();
|
||||
@ -2573,11 +2535,14 @@ void CPlayerInterface::showShipyardDialogOrProblemPopup(const IShipyard *obj)
|
||||
showShipyardDialog(obj);
|
||||
}
|
||||
|
||||
void CPlayerInterface::requestReturningToMainMenu()
|
||||
void CPlayerInterface::requestReturningToMainMenu(bool won)
|
||||
{
|
||||
sendCustomEvent(RETURN_TO_MAIN_MENU);
|
||||
CSH->state = EClientState::DISCONNECTING;
|
||||
CCS->soundh->ambientStopAllChannels();
|
||||
cb->unregisterAllInterfaces();
|
||||
if(won && cb->getStartInfo()->campState)
|
||||
CSH->startCampaignScenario(cb->getStartInfo()->campState);
|
||||
else
|
||||
sendCustomEvent(EUserEvent::RETURN_TO_MAIN_MENU);
|
||||
}
|
||||
|
||||
void CPlayerInterface::sendCustomEvent( int code )
|
||||
@ -2672,7 +2637,7 @@ void CPlayerInterface::playerStartsTurn(PlayerColor player)
|
||||
GH.popInts(1);
|
||||
}
|
||||
|
||||
if (howManyPeople == 1)
|
||||
if(CSH->howManyPlayerInterfaces() == 1)
|
||||
{
|
||||
GH.curInt = this;
|
||||
adventureInt->startTurn();
|
||||
@ -2696,7 +2661,7 @@ void CPlayerInterface::waitForAllDialogs(bool unlockPim)
|
||||
|
||||
void CPlayerInterface::proposeLoadingGame()
|
||||
{
|
||||
showYesNoDialog(CGI->generaltexth->allTexts[68], [this](){ sendCustomEvent(RETURN_TO_MENU_LOAD); }, 0, false);
|
||||
showYesNoDialog(CGI->generaltexth->allTexts[68], [this](){ sendCustomEvent(EUserEvent::RETURN_TO_MENU_LOAD); }, 0, false);
|
||||
}
|
||||
|
||||
CPlayerInterface::SpellbookLastSetting::SpellbookLastSetting()
|
||||
|
@ -59,19 +59,6 @@ namespace boost
|
||||
class recursive_mutex;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
/*CHANGE_SCREEN_RESOLUTION = 1,*/
|
||||
RETURN_TO_MAIN_MENU = 2,
|
||||
//STOP_CLIENT = 3,
|
||||
RESTART_GAME = 4,
|
||||
RETURN_TO_MENU_LOAD,
|
||||
FULLSCREEN_TOGGLED,
|
||||
PREPARE_RESTART_CAMPAIGN,
|
||||
FORCE_QUIT, //quit client without question
|
||||
INTERFACE_CHANGED
|
||||
};
|
||||
|
||||
/// Central class for managing user interface logic
|
||||
class CPlayerInterface : public CGameInterface, public IUpdateable
|
||||
{
|
||||
@ -88,7 +75,6 @@ public:
|
||||
int firstCall; // -1 - just loaded game; 1 - just started game; 0 otherwise
|
||||
int autosaveCount;
|
||||
static const int SAVES_COUNT = 5;
|
||||
static int howManyPeople;
|
||||
|
||||
CCastleInterface * castleInt; //nullptr if castle window isn't opened
|
||||
static CBattleInterface * battleInt; //nullptr if no battle
|
||||
@ -247,7 +233,7 @@ public:
|
||||
void acceptTurn(); //used during hot seat after your turn message is close
|
||||
void tryDiggging(const CGHeroInstance *h);
|
||||
void showShipyardDialogOrProblemPopup(const IShipyard *obj); //obj may be town or shipyard;
|
||||
void requestReturningToMainMenu();
|
||||
void requestReturningToMainMenu(bool won);
|
||||
void sendCustomEvent(int code);
|
||||
void proposeLoadingGame();
|
||||
|
||||
|
4374
client/CPreGame.cpp
4374
client/CPreGame.cpp
File diff suppressed because it is too large
Load Diff
@ -1,650 +0,0 @@
|
||||
/*
|
||||
* CPreGame.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../lib/StartInfo.h"
|
||||
#include "../lib/FunctionList.h"
|
||||
#include "../lib/mapping/CMapInfo.h"
|
||||
#include "../lib/rmg/CMapGenerator.h"
|
||||
#include "windows/CWindowObject.h"
|
||||
|
||||
class CMapInfo;
|
||||
class CMusicHandler;
|
||||
class CMapHeader;
|
||||
class CCampaignHeader;
|
||||
class CTextInput;
|
||||
class CCampaign;
|
||||
class CGStatusBar;
|
||||
class CTextBox;
|
||||
class CCampaignState;
|
||||
class CConnection;
|
||||
class JsonNode;
|
||||
class CMapGenOptions;
|
||||
class CRandomMapTab;
|
||||
struct CPackForSelectionScreen;
|
||||
struct PlayerInfo;
|
||||
class CMultiLineLabel;
|
||||
class CToggleButton;
|
||||
class CToggleGroup;
|
||||
class CTabbedInt;
|
||||
class IImage;
|
||||
class CAnimation;
|
||||
class CAnimImage;
|
||||
class CButton;
|
||||
class CLabel;
|
||||
class CSlider;
|
||||
|
||||
namespace boost{ class thread; class recursive_mutex;}
|
||||
|
||||
enum ESortBy{_playerAm, _size, _format, _name, _viccon, _loscon, _numOfMaps, _fileName}; //_numOfMaps is for campaigns
|
||||
|
||||
/// Class which handles map sorting by different criteria
|
||||
class mapSorter
|
||||
{
|
||||
public:
|
||||
ESortBy sortBy;
|
||||
bool operator()(const CMapInfo *aaa, const CMapInfo *bbb);
|
||||
mapSorter(ESortBy es):sortBy(es){};
|
||||
};
|
||||
|
||||
/// The main menu screens listed in the EState enum
|
||||
class CMenuScreen : public CIntObject
|
||||
{
|
||||
const JsonNode& config;
|
||||
|
||||
CTabbedInt *tabs;
|
||||
|
||||
CPicture * background;
|
||||
std::vector<CPicture*> images;
|
||||
|
||||
CIntObject *createTab(size_t index);
|
||||
public:
|
||||
std::vector<std::string> menuNameToEntry;
|
||||
|
||||
enum EState { //where are we?
|
||||
mainMenu, newGame, loadGame, campaignMain, saveGame, scenarioInfo, campaignList
|
||||
};
|
||||
|
||||
enum EGameMode {
|
||||
SINGLE_PLAYER = 0, MULTI_HOT_SEAT, MULTI_NETWORK_HOST, MULTI_NETWORK_GUEST, SINGLE_CAMPAIGN
|
||||
};
|
||||
CMenuScreen(const JsonNode& configNode);
|
||||
|
||||
void showAll(SDL_Surface * to) override;
|
||||
void show(SDL_Surface * to) override;
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
void switchToTab(size_t index);
|
||||
};
|
||||
|
||||
class CMenuEntry : public CIntObject
|
||||
{
|
||||
std::vector<CPicture*> images;
|
||||
std::vector<CButton*> buttons;
|
||||
|
||||
CButton* createButton(CMenuScreen* parent, const JsonNode& button);
|
||||
public:
|
||||
CMenuEntry(CMenuScreen* parent, const JsonNode &config);
|
||||
};
|
||||
|
||||
class CreditsScreen : public CIntObject
|
||||
{
|
||||
int positionCounter;
|
||||
CMultiLineLabel* credits;
|
||||
public:
|
||||
CreditsScreen();
|
||||
|
||||
void show(SDL_Surface * to) override;
|
||||
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void clickRight(tribool down, bool previousState) override;
|
||||
};
|
||||
|
||||
/// Implementation of the chat box
|
||||
class CChatBox : public CIntObject
|
||||
{
|
||||
public:
|
||||
CTextBox *chatHistory;
|
||||
CTextInput *inputBox;
|
||||
|
||||
CChatBox(const Rect &rect);
|
||||
|
||||
void keyPressed(const SDL_KeyboardEvent & key) override;
|
||||
|
||||
void addNewMessage(const std::string &text);
|
||||
};
|
||||
|
||||
class InfoCard : public CIntObject
|
||||
{
|
||||
CAnimImage * victory, * loss, *sizes;
|
||||
std::shared_ptr<CAnimation> sFlags;
|
||||
public:
|
||||
CPicture *bg;
|
||||
|
||||
bool network;
|
||||
bool chatOn; //if chat is shown, then description is hidden
|
||||
CTextBox *mapDescription;
|
||||
CChatBox *chat;
|
||||
CPicture *playerListBg;
|
||||
|
||||
CToggleGroup *difficulty;
|
||||
|
||||
void changeSelection(const CMapInfo *to);
|
||||
void showAll(SDL_Surface * to) override;
|
||||
void clickRight(tribool down, bool previousState) override;
|
||||
void showTeamsPopup();
|
||||
void toggleChat();
|
||||
void setChat(bool activateChat);
|
||||
InfoCard(bool Network = false);
|
||||
~InfoCard();
|
||||
};
|
||||
|
||||
/// The selection tab which is shown at the map selection screen
|
||||
class SelectionTab : public CIntObject
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<CAnimation> formatIcons;
|
||||
|
||||
void parseMaps(const std::unordered_set<ResourceID> &files);
|
||||
void parseGames(const std::unordered_set<ResourceID> &files, CMenuScreen::EGameMode gameMode);
|
||||
std::unordered_set<ResourceID> getFiles(std::string dirURI, int resType);
|
||||
void parseCampaigns(const std::unordered_set<ResourceID> & files );
|
||||
CMenuScreen::EState tabType;
|
||||
public:
|
||||
int positions; //how many entries (games/maps) can be shown
|
||||
CPicture *bg; //general bg image
|
||||
CSlider *slider;
|
||||
std::vector<CMapInfo> allItems;
|
||||
std::vector<CMapInfo*> curItems;
|
||||
size_t selectionPos;
|
||||
std::function<void(CMapInfo *)> onSelect;
|
||||
|
||||
ESortBy sortingBy;
|
||||
ESortBy generalSortingBy;
|
||||
bool ascending;
|
||||
|
||||
CTextInput *txt;
|
||||
|
||||
|
||||
void filter(int size, bool selectFirst = false); //0 - all
|
||||
void select(int position); //position: <0 - positions> position on the screen
|
||||
void selectAbs(int position); //position: absolute position in curItems vector
|
||||
int getPosition(int x, int y); //convert mouse coords to entry position; -1 means none
|
||||
void sliderMove(int slidPos);
|
||||
void sortBy(int criteria);
|
||||
void sort();
|
||||
void printMaps(SDL_Surface *to);
|
||||
int getLine();
|
||||
void selectFName(std::string fname);
|
||||
const CMapInfo * getSelectedMapInfo() const;
|
||||
|
||||
void showAll(SDL_Surface * to) override;
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void keyPressed(const SDL_KeyboardEvent & key) override;
|
||||
void onDoubleClick() override;
|
||||
SelectionTab(CMenuScreen::EState Type, const std::function<void(CMapInfo *)> &OnSelect, CMenuScreen::EGameMode GameMode = CMenuScreen::SINGLE_PLAYER);
|
||||
~SelectionTab();
|
||||
};
|
||||
|
||||
/// The options tab which is shown at the map selection phase.
|
||||
class OptionsTab : public CIntObject
|
||||
{
|
||||
CPicture *bg;
|
||||
public:
|
||||
enum SelType {TOWN, HERO, BONUS};
|
||||
|
||||
struct CPlayerSettingsHelper
|
||||
{
|
||||
const PlayerSettings & settings;
|
||||
const SelType type;
|
||||
|
||||
CPlayerSettingsHelper(const PlayerSettings & settings, SelType type):
|
||||
settings(settings),
|
||||
type(type)
|
||||
{}
|
||||
|
||||
/// visible image settings
|
||||
size_t getImageIndex();
|
||||
std::string getImageName();
|
||||
|
||||
std::string getName(); /// name visible in options dialog
|
||||
std::string getTitle(); /// title in popup box
|
||||
std::string getSubtitle(); /// popup box subtitle
|
||||
std::string getDescription();/// popup box description, not always present
|
||||
};
|
||||
|
||||
class CPregameTooltipBox : public CWindowObject, public CPlayerSettingsHelper
|
||||
{
|
||||
void genHeader();
|
||||
void genTownWindow();
|
||||
void genHeroWindow();
|
||||
void genBonusWindow();
|
||||
public:
|
||||
CPregameTooltipBox(CPlayerSettingsHelper & helper);
|
||||
};
|
||||
|
||||
struct SelectedBox : public CIntObject, public CPlayerSettingsHelper //img with current town/hero/bonus
|
||||
{
|
||||
CAnimImage * image;
|
||||
CLabel *subtitle;
|
||||
|
||||
SelectedBox(Point position, PlayerSettings & settings, SelType type);
|
||||
void clickRight(tribool down, bool previousState) override;
|
||||
|
||||
void update();
|
||||
};
|
||||
|
||||
struct PlayerOptionsEntry : public CIntObject
|
||||
{
|
||||
PlayerInfo π
|
||||
PlayerSettings &s;
|
||||
CPicture *bg;
|
||||
CButton *btns[6]; //left and right for town, hero, bonus
|
||||
CButton *flag;
|
||||
SelectedBox *town;
|
||||
SelectedBox *hero;
|
||||
SelectedBox *bonus;
|
||||
enum {HUMAN_OR_CPU, HUMAN, CPU} whoCanPlay;
|
||||
|
||||
PlayerOptionsEntry(OptionsTab *owner, PlayerSettings &S);
|
||||
void selectButtons(); //hides unavailable buttons
|
||||
void showAll(SDL_Surface * to) override;
|
||||
void update();
|
||||
};
|
||||
|
||||
CSlider *turnDuration;
|
||||
|
||||
std::set<int> usedHeroes;
|
||||
|
||||
struct PlayerToRestore
|
||||
{
|
||||
PlayerColor color;
|
||||
int id;
|
||||
void reset() { id = -1; color = PlayerColor::CANNOT_DETERMINE; }
|
||||
PlayerToRestore(){ reset(); }
|
||||
} playerToRestore;
|
||||
|
||||
|
||||
std::map<PlayerColor, PlayerOptionsEntry *> entries; //indexed by color
|
||||
|
||||
void nextCastle(PlayerColor player, int dir); //dir == -1 or +1
|
||||
void nextHero(PlayerColor player, int dir); //dir == -1 or +1
|
||||
void nextBonus(PlayerColor player, int dir); //dir == -1 or +1
|
||||
void setTurnLength(int npos);
|
||||
void flagPressed(PlayerColor player);
|
||||
|
||||
void recreate();
|
||||
OptionsTab();
|
||||
~OptionsTab();
|
||||
void showAll(SDL_Surface * to) override;
|
||||
|
||||
int nextAllowedHero(PlayerColor player, int min, int max, int incl, int dir );
|
||||
|
||||
bool canUseThisHero(PlayerColor player, int ID );
|
||||
};
|
||||
|
||||
/// The random map tab shows options for generating a random map.
|
||||
class CRandomMapTab : public CIntObject
|
||||
{
|
||||
public:
|
||||
CRandomMapTab();
|
||||
|
||||
void showAll(SDL_Surface * to) override;
|
||||
void updateMapInfo();
|
||||
CFunctionList<void (const CMapInfo *)> & getMapInfoChanged();
|
||||
const CMapInfo * getMapInfo() const;
|
||||
const CMapGenOptions & getMapGenOptions() const;
|
||||
void setMapGenOptions(std::shared_ptr<CMapGenOptions> opts);
|
||||
|
||||
private:
|
||||
void addButtonsToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex) const;
|
||||
void addButtonsWithRandToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex, int helpRandIndex) const;
|
||||
void deactivateButtonsFrom(CToggleGroup * group, int startId);
|
||||
void validatePlayersCnt(int playersCnt);
|
||||
void validateCompOnlyPlayersCnt(int compOnlyPlayersCnt);
|
||||
std::vector<int> getPossibleMapSizes();
|
||||
|
||||
CPicture * bg;
|
||||
CToggleButton * twoLevelsBtn;
|
||||
CToggleGroup * mapSizeBtnGroup, * playersCntGroup, * teamsCntGroup, * compOnlyPlayersCntGroup,
|
||||
* compOnlyTeamsCntGroup, * waterContentGroup, * monsterStrengthGroup;
|
||||
CButton * showRandMaps;
|
||||
CMapGenOptions mapGenOptions;
|
||||
std::unique_ptr<CMapInfo> mapInfo;
|
||||
CFunctionList<void(const CMapInfo *)> mapInfoChanged;
|
||||
};
|
||||
|
||||
/// Interface for selecting a map.
|
||||
class ISelectionScreenInfo
|
||||
{
|
||||
public:
|
||||
CMenuScreen::EGameMode gameMode;
|
||||
CMenuScreen::EState screenType; //new/save/load#Game
|
||||
const CMapInfo *current;
|
||||
StartInfo sInfo;
|
||||
std::map<ui8, std::string> playerNames; // id of player <-> player name; 0 is reserved as ID of AI "players"
|
||||
|
||||
ISelectionScreenInfo(const std::map<ui8, std::string> *Names = nullptr);
|
||||
virtual ~ISelectionScreenInfo();
|
||||
virtual void update(){};
|
||||
virtual void propagateOptions() {};
|
||||
virtual void postRequest(ui8 what, ui8 dir) {};
|
||||
virtual void postChatMessage(const std::string &txt){};
|
||||
|
||||
void setPlayer(PlayerSettings &pset, ui8 player);
|
||||
void updateStartInfo(std::string filename, StartInfo & sInfo, const std::unique_ptr<CMapHeader> & mapHeader);
|
||||
|
||||
ui8 getIdOfFirstUnallocatedPlayer(); //returns 0 if none
|
||||
bool isGuest() const;
|
||||
bool isHost() const;
|
||||
|
||||
};
|
||||
|
||||
/// The actual map selection screen which consists of the options and selection tab
|
||||
class CSelectionScreen : public CIntObject, public ISelectionScreenInfo
|
||||
{
|
||||
bool bordered;
|
||||
public:
|
||||
CPicture *bg; //general bg image
|
||||
InfoCard *card;
|
||||
OptionsTab *opt;
|
||||
CRandomMapTab * randMapTab;
|
||||
CButton *start, *back;
|
||||
|
||||
SelectionTab *sel;
|
||||
CIntObject *curTab;
|
||||
|
||||
boost::thread *serverHandlingThread;
|
||||
boost::recursive_mutex *mx;
|
||||
std::list<CPackForSelectionScreen *> upcomingPacks; //protected by mx
|
||||
|
||||
CConnection *serv; //connection to server, used in MP mode
|
||||
bool ongoingClosing;
|
||||
ui8 myNameID; //used when networking - otherwise all player are "mine"
|
||||
|
||||
CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EGameMode GameMode = 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);
|
||||
void startCampaign();
|
||||
void startScenario();
|
||||
void difficultyChange(int to);
|
||||
|
||||
void handleConnection();
|
||||
|
||||
void processPacks();
|
||||
void setSInfo(const StartInfo &si);
|
||||
void update() override;
|
||||
void propagateOptions() override;
|
||||
void postRequest(ui8 what, ui8 dir) override;
|
||||
void postChatMessage(const std::string &txt) override;
|
||||
void propagateNames();
|
||||
void showAll(SDL_Surface *to) override;
|
||||
};
|
||||
|
||||
/// Save game screen
|
||||
class CSavingScreen : public CSelectionScreen
|
||||
{
|
||||
public:
|
||||
const CMapInfo *ourGame;
|
||||
|
||||
|
||||
CSavingScreen(bool hotseat = false);
|
||||
~CSavingScreen();
|
||||
};
|
||||
|
||||
/// Scenario information screen shown during the game (thus not really a "pre-game" but fits here anyway)
|
||||
class CScenarioInfo : public CIntObject, public ISelectionScreenInfo
|
||||
{
|
||||
public:
|
||||
CButton *back;
|
||||
InfoCard *card;
|
||||
OptionsTab *opt;
|
||||
|
||||
CScenarioInfo(const CMapHeader *mapHeader, const StartInfo *startInfo);
|
||||
~CScenarioInfo();
|
||||
};
|
||||
|
||||
/// Multiplayer mode
|
||||
class CMultiMode : public CIntObject
|
||||
{
|
||||
public:
|
||||
CPicture *bg;
|
||||
CTextInput *txt;
|
||||
CButton *btns[7]; //0 - hotseat, 6 - cancel
|
||||
CGStatusBar *bar;
|
||||
|
||||
CMultiMode();
|
||||
void openHotseat();
|
||||
void hostTCP();
|
||||
void joinTCP();
|
||||
};
|
||||
|
||||
/// Hot seat player window
|
||||
class CHotSeatPlayers : public CIntObject
|
||||
{
|
||||
CPicture *bg;
|
||||
CTextBox *title;
|
||||
CTextInput* txt[8];
|
||||
CButton *ok, *cancel;
|
||||
CGStatusBar *bar;
|
||||
|
||||
void onChange(std::string newText);
|
||||
void enterSelectionScreen();
|
||||
|
||||
public:
|
||||
CHotSeatPlayers(const std::string &firstPlayer);
|
||||
};
|
||||
|
||||
|
||||
class CPrologEpilogVideo : public CWindowObject
|
||||
{
|
||||
CCampaignScenario::SScenarioPrologEpilog spe;
|
||||
int positionCounter;
|
||||
int voiceSoundHandle;
|
||||
std::function<void()> exitCb;
|
||||
|
||||
CMultiLineLabel * text;
|
||||
public:
|
||||
CPrologEpilogVideo(CCampaignScenario::SScenarioPrologEpilog _spe, std::function<void()> callback);
|
||||
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void show(SDL_Surface * to) override;
|
||||
};
|
||||
|
||||
/// Campaign screen where you can choose one out of three starting bonuses
|
||||
class CBonusSelection : public CIntObject
|
||||
{
|
||||
public:
|
||||
CBonusSelection(const std::string & campaignFName);
|
||||
CBonusSelection(std::shared_ptr<CCampaignState> _ourCampaign);
|
||||
~CBonusSelection();
|
||||
|
||||
void showAll(SDL_Surface * to) override;
|
||||
void show(SDL_Surface * to) override;
|
||||
|
||||
private:
|
||||
struct SCampPositions
|
||||
{
|
||||
std::string campPrefix;
|
||||
int colorSuffixLength;
|
||||
|
||||
struct SRegionDesc
|
||||
{
|
||||
std::string infix;
|
||||
int xpos, ypos;
|
||||
};
|
||||
|
||||
std::vector<SRegionDesc> regions;
|
||||
|
||||
};
|
||||
|
||||
class CRegion : public CIntObject
|
||||
{
|
||||
CBonusSelection * owner;
|
||||
SDL_Surface* graphics[3]; //[0] - not selected, [1] - selected, [2] - striped
|
||||
bool accessible; //false if region should be striped
|
||||
bool selectable; //true if region should be selectable
|
||||
int myNumber; //number of region
|
||||
|
||||
public:
|
||||
std::string rclickText;
|
||||
CRegion(CBonusSelection * _owner, bool _accessible, bool _selectable, int _myNumber);
|
||||
~CRegion();
|
||||
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void clickRight(tribool down, bool previousState) override;
|
||||
void show(SDL_Surface * to) override;
|
||||
};
|
||||
|
||||
void init();
|
||||
void loadPositionsOfGraphics();
|
||||
void updateStartButtonState(int selected = -1); //-1 -- no bonus is selected
|
||||
void updateBonusSelection();
|
||||
void updateCampaignState();
|
||||
|
||||
// Event handlers
|
||||
void goBack();
|
||||
void startMap();
|
||||
void restartMap();
|
||||
void selectMap(int mapNr, bool initialSelect);
|
||||
void selectBonus(int id);
|
||||
void increaseDifficulty();
|
||||
void decreaseDifficulty();
|
||||
|
||||
// GUI components
|
||||
SDL_Surface * background;
|
||||
CButton * startB, * restartB, * backB;
|
||||
CTextBox * campaignDescription, * mapDescription;
|
||||
std::vector<SCampPositions> campDescriptions;
|
||||
std::vector<CRegion *> regions;
|
||||
CRegion * highlightedRegion;
|
||||
CToggleGroup * bonuses;
|
||||
std::array<CAnimImage *, 5> diffPics; //pictures of difficulties, user-selectable (or not if campaign locks this)
|
||||
CButton * diffLb, * diffRb; //buttons for changing difficulty
|
||||
CAnimImage * sizes;//icons of map sizes
|
||||
std::shared_ptr<CAnimation> sFlags;
|
||||
|
||||
// Data
|
||||
std::shared_ptr<CCampaignState> ourCampaign;
|
||||
int selectedMap;
|
||||
boost::optional<int> selectedBonus;
|
||||
StartInfo startInfo;
|
||||
std::unique_ptr<CMapHeader> ourHeader;
|
||||
};
|
||||
|
||||
/// Campaign selection screen
|
||||
class CCampaignScreen : public CIntObject
|
||||
{
|
||||
public:
|
||||
enum CampaignStatus {DEFAULT = 0, ENABLED, DISABLED, COMPLETED}; // the status of the campaign
|
||||
|
||||
private:
|
||||
/// A button which plays a video when you move the mouse cursor over it
|
||||
class CCampaignButton : public CIntObject
|
||||
{
|
||||
private:
|
||||
CLabel *hoverLabel;
|
||||
CampaignStatus status;
|
||||
|
||||
std::string campFile; // the filename/resourcename of the campaign
|
||||
std::string video; // the resource name of the video
|
||||
std::string hoverText;
|
||||
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void hover(bool on) override;
|
||||
|
||||
public:
|
||||
CCampaignButton(const JsonNode &config );
|
||||
void show(SDL_Surface * to) override;
|
||||
};
|
||||
|
||||
std::vector<CCampaignButton*> campButtons;
|
||||
std::vector<CPicture*> images;
|
||||
|
||||
CButton* createExitButton(const JsonNode& button);
|
||||
|
||||
public:
|
||||
enum CampaignSet {ROE, AB, SOD, WOG};
|
||||
|
||||
CCampaignScreen(const JsonNode &config);
|
||||
void showAll(SDL_Surface *to) override;
|
||||
};
|
||||
|
||||
/// Manages the configuration of pregame GUI elements like campaign screen, main menu, loading screen,...
|
||||
class CGPreGameConfig
|
||||
{
|
||||
public:
|
||||
static CGPreGameConfig & get();
|
||||
const JsonNode & getConfig() const;
|
||||
const JsonNode & getCampaigns() const;
|
||||
|
||||
private:
|
||||
CGPreGameConfig();
|
||||
|
||||
const JsonNode campaignSets;
|
||||
const JsonNode config;
|
||||
};
|
||||
|
||||
/// Handles background screen, loads graphics for victory/loss condition and random town or hero selection
|
||||
class CGPreGame : public CIntObject, public IUpdateable
|
||||
{
|
||||
void loadGraphics();
|
||||
void disposeGraphics();
|
||||
|
||||
CGPreGame(); //Use createIfNotPresent
|
||||
|
||||
public:
|
||||
CMenuScreen * menu;
|
||||
|
||||
std::shared_ptr<CAnimation> victoryIcons, lossIcons;
|
||||
|
||||
~CGPreGame();
|
||||
void update() override;
|
||||
void openSel(CMenuScreen::EState type, CMenuScreen::EGameMode gameMode = CMenuScreen::SINGLE_PLAYER);
|
||||
|
||||
void openCampaignScreen(std::string name);
|
||||
|
||||
static CGPreGame * create();
|
||||
void removeFromGui();
|
||||
static void showLoadingScreen(std::function<void()> loader);
|
||||
};
|
||||
|
||||
class CLoadingScreen : public CWindowObject
|
||||
{
|
||||
boost::thread loadingThread;
|
||||
|
||||
std::string getBackground();
|
||||
public:
|
||||
CLoadingScreen(std::function<void()> loader);
|
||||
~CLoadingScreen();
|
||||
|
||||
void showAll(SDL_Surface *to) override;
|
||||
};
|
||||
|
||||
/// Simple window to enter the server's address.
|
||||
class CSimpleJoinScreen : public CIntObject
|
||||
{
|
||||
CPicture * bg;
|
||||
CTextBox * title;
|
||||
CButton * ok, * cancel;
|
||||
CGStatusBar * bar;
|
||||
CTextInput * address;
|
||||
CTextInput * port;
|
||||
|
||||
void enterSelectionScreen(CMenuScreen::EGameMode mode);
|
||||
void onChange(const std::string & newText);
|
||||
public:
|
||||
CSimpleJoinScreen(CMenuScreen::EGameMode mode);
|
||||
};
|
||||
|
||||
extern ISelectionScreenInfo *SEL;
|
||||
extern CGPreGame *CGP;
|
674
client/CServerHandler.cpp
Normal file
674
client/CServerHandler.cpp
Normal file
@ -0,0 +1,674 @@
|
||||
/*
|
||||
* CServerHandler.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CServerHandler.h"
|
||||
#include "Client.h"
|
||||
#include "CGameInfo.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
|
||||
#include "lobby/CSelectionBase.h"
|
||||
#include "lobby/CLobbyScreen.h"
|
||||
|
||||
#include "mainmenu/CMainMenu.h"
|
||||
|
||||
#ifndef VCMI_ANDROID
|
||||
#include "../lib/Interprocess.h"
|
||||
#endif
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CThreadHelper.h"
|
||||
#include "../lib/NetPacks.h"
|
||||
#include "../lib/StartInfo.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/mapping/CCampaignHandler.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/mapping/CMapInfo.h"
|
||||
#include "../lib/mapObjects/MiscObjects.h"
|
||||
#include "../lib/rmg/CMapGenOptions.h"
|
||||
#include "../lib/registerTypes/RegisterTypes.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "../lib/serializer/CMemorySerializer.h"
|
||||
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
|
||||
template<typename T> class CApplyOnLobby;
|
||||
|
||||
class CBaseForLobbyApply
|
||||
{
|
||||
public:
|
||||
virtual bool applyOnLobbyHandler(CServerHandler * handler, void * pack) const = 0;
|
||||
virtual void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, void * pack) const = 0;
|
||||
virtual ~CBaseForLobbyApply(){};
|
||||
template<typename U> static CBaseForLobbyApply * getApplier(const U * t = nullptr)
|
||||
{
|
||||
return new CApplyOnLobby<U>();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class CApplyOnLobby : public CBaseForLobbyApply
|
||||
{
|
||||
public:
|
||||
bool applyOnLobbyHandler(CServerHandler * handler, void * pack) const override
|
||||
{
|
||||
T * ptr = static_cast<T *>(pack);
|
||||
logNetwork->trace("\tImmidiately apply on lobby: %s", typeList.getTypeInfo(ptr)->name());
|
||||
return ptr->applyOnLobbyHandler(handler);
|
||||
}
|
||||
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, void * pack) const override
|
||||
{
|
||||
T * ptr = static_cast<T *>(pack);
|
||||
logNetwork->trace("\tApply on lobby from queue: %s", typeList.getTypeInfo(ptr)->name());
|
||||
ptr->applyOnLobbyScreen(lobby, handler);
|
||||
}
|
||||
};
|
||||
|
||||
template<> class CApplyOnLobby<CPack>: public CBaseForLobbyApply
|
||||
{
|
||||
public:
|
||||
bool applyOnLobbyHandler(CServerHandler * handler, void * pack) const override
|
||||
{
|
||||
logGlobal->error("Cannot apply plain CPack!");
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, void * pack) const override
|
||||
{
|
||||
logGlobal->error("Cannot apply plain CPack!");
|
||||
assert(0);
|
||||
}
|
||||
};
|
||||
|
||||
extern std::string NAME;
|
||||
|
||||
CServerHandler::CServerHandler()
|
||||
: state(EClientState::NONE), mx(std::make_shared<boost::recursive_mutex>()), client(nullptr), loadMode(0), campaignStateToSend(nullptr)
|
||||
{
|
||||
uuid = boost::uuids::to_string(boost::uuids::random_generator()());
|
||||
applier = std::make_shared<CApplier<CBaseForLobbyApply>>();
|
||||
registerTypesLobbyPacks(*applier);
|
||||
}
|
||||
|
||||
void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std::vector<std::string> * names)
|
||||
{
|
||||
hostClientId = -1;
|
||||
state = EClientState::NONE;
|
||||
th = make_unique<CStopWatch>();
|
||||
packsForLobbyScreen.clear();
|
||||
c.reset();
|
||||
si.reset(new StartInfo());
|
||||
playerNames.clear();
|
||||
si->difficulty = 1;
|
||||
si->mode = mode;
|
||||
myNames.clear();
|
||||
if(names && !names->empty()) //if have custom set of player names - use it
|
||||
myNames = *names;
|
||||
else
|
||||
myNames.push_back(settings["general"]["playerName"].String());
|
||||
|
||||
#ifndef VCMI_ANDROID
|
||||
shm.reset();
|
||||
|
||||
if(!settings["session"]["disable-shm"].Bool())
|
||||
{
|
||||
std::string sharedMemoryName = "vcmi_memory";
|
||||
if(settings["session"]["enable-shm-uuid"].Bool())
|
||||
{
|
||||
//used or automated testing when multiple clients start simultaneously
|
||||
sharedMemoryName += "_" + uuid;
|
||||
}
|
||||
try
|
||||
{
|
||||
shm = std::make_shared<SharedMemory>(sharedMemoryName, true);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
shm.reset();
|
||||
logNetwork->error("Cannot open interprocess memory. Continue without it...");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CServerHandler::startLocalServerAndConnect()
|
||||
{
|
||||
if(threadRunLocalServer)
|
||||
threadRunLocalServer->join();
|
||||
|
||||
th->update();
|
||||
#ifdef VCMI_ANDROID
|
||||
CAndroidVMHelper envHelper;
|
||||
envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "startServer", true);
|
||||
#else
|
||||
threadRunLocalServer = std::make_shared<boost::thread>(&CServerHandler::threadRunServer, this); //runs server executable;
|
||||
#endif
|
||||
logNetwork->trace("Setting up thread calling server: %d ms", th->getDiff());
|
||||
|
||||
th->update();
|
||||
|
||||
#ifndef VCMI_ANDROID
|
||||
if(shm)
|
||||
shm->sr->waitTillReady();
|
||||
#else
|
||||
logNetwork->info("waiting for server");
|
||||
while(!androidTestServerReadyFlag.load())
|
||||
{
|
||||
logNetwork->info("still waiting...");
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
|
||||
}
|
||||
logNetwork->info("waiting for server finished...");
|
||||
androidTestServerReadyFlag = false;
|
||||
#endif
|
||||
logNetwork->trace("Waiting for server: %d ms", th->getDiff());
|
||||
|
||||
th->update(); //put breakpoint here to attach to server before it does something stupid
|
||||
|
||||
#ifndef VCMI_ANDROID
|
||||
justConnectToServer(settings["server"]["server"].String(), shm ? shm->sr->port : 0);
|
||||
#else
|
||||
justConnectToServer(settings["server"]["server"].String());
|
||||
#endif
|
||||
|
||||
logNetwork->trace("\tConnecting to the server: %d ms", th->getDiff());
|
||||
}
|
||||
|
||||
void CServerHandler::justConnectToServer(const std::string & addr, const ui16 port)
|
||||
{
|
||||
state = EClientState::CONNECTING;
|
||||
while(!c && state != EClientState::CONNECTION_CANCELLED)
|
||||
{
|
||||
try
|
||||
{
|
||||
logNetwork->info("Establishing connection...");
|
||||
c = std::make_shared<CConnection>(
|
||||
addr.size() ? addr : settings["server"]["server"].String(),
|
||||
port ? port : getDefaultPort(),
|
||||
NAME, uuid);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
logNetwork->error("\nCannot establish connection! Retrying within 1 second");
|
||||
boost::this_thread::sleep(boost::posix_time::seconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
if(state == EClientState::CONNECTION_CANCELLED)
|
||||
logNetwork->info("Connection aborted by player!");
|
||||
else
|
||||
c->handler = std::make_shared<boost::thread>(&CServerHandler::threadHandleConnection, this);
|
||||
}
|
||||
|
||||
void CServerHandler::applyPacksOnLobbyScreen()
|
||||
{
|
||||
if(!c || !c->handler)
|
||||
return;
|
||||
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*mx);
|
||||
while(!packsForLobbyScreen.empty())
|
||||
{
|
||||
CPackForLobby * pack = packsForLobbyScreen.front();
|
||||
packsForLobbyScreen.pop_front();
|
||||
CBaseForLobbyApply * apply = applier->getApplier(typeList.getTypeID(pack)); //find the applier
|
||||
apply->applyOnLobbyScreen(static_cast<CLobbyScreen *>(SEL), this, pack);
|
||||
GH.totalRedraw();
|
||||
delete pack;
|
||||
}
|
||||
}
|
||||
|
||||
void CServerHandler::stopServerConnection()
|
||||
{
|
||||
if(c->handler)
|
||||
{
|
||||
while(!c->handler->timed_join(boost::posix_time::milliseconds(50)))
|
||||
applyPacksOnLobbyScreen();
|
||||
c->handler->join();
|
||||
}
|
||||
}
|
||||
|
||||
std::set<PlayerColor> CServerHandler::getHumanColors()
|
||||
{
|
||||
return clientHumanColors(c->connectionID);
|
||||
}
|
||||
|
||||
|
||||
PlayerColor CServerHandler::myFirstColor() const
|
||||
{
|
||||
return clientFirstColor(c->connectionID);
|
||||
}
|
||||
|
||||
bool CServerHandler::isMyColor(PlayerColor color) const
|
||||
{
|
||||
return isClientColor(c->connectionID, color);
|
||||
}
|
||||
|
||||
ui8 CServerHandler::myFirstId() const
|
||||
{
|
||||
return clientFirstId(c->connectionID);
|
||||
}
|
||||
|
||||
bool CServerHandler::isServerLocal() const
|
||||
{
|
||||
if(threadRunLocalServer)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CServerHandler::isHost() const
|
||||
{
|
||||
return c && hostClientId == c->connectionID;
|
||||
}
|
||||
|
||||
bool CServerHandler::isGuest() const
|
||||
{
|
||||
return !c || hostClientId != c->connectionID;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
void CServerHandler::sendClientConnecting() const
|
||||
{
|
||||
LobbyClientConnected lcc;
|
||||
lcc.uuid = uuid;
|
||||
lcc.names = myNames;
|
||||
lcc.mode = si->mode;
|
||||
sendLobbyPack(lcc);
|
||||
}
|
||||
|
||||
void CServerHandler::sendClientDisconnecting()
|
||||
{
|
||||
// FIXME: This is workaround needed to make sure client not trying to sent anything to non existed server
|
||||
if(state == EClientState::DISCONNECTING)
|
||||
return;
|
||||
|
||||
state = EClientState::DISCONNECTING;
|
||||
LobbyClientDisconnected lcd;
|
||||
lcd.clientId = c->connectionID;
|
||||
logNetwork->info("Connection has been requested to be closed.");
|
||||
if(isServerLocal())
|
||||
{
|
||||
lcd.shutdownServer = true;
|
||||
logNetwork->info("Sent closing signal to the server");
|
||||
}
|
||||
else
|
||||
{
|
||||
logNetwork->info("Sent leaving signal to the server");
|
||||
}
|
||||
sendLobbyPack(lcd);
|
||||
}
|
||||
|
||||
void CServerHandler::setCampaignState(std::shared_ptr<CCampaignState> newCampaign)
|
||||
{
|
||||
state = EClientState::LOBBY_CAMPAIGN;
|
||||
LobbySetCampaign lsc;
|
||||
lsc.ourCampaign = newCampaign;
|
||||
sendLobbyPack(lsc);
|
||||
}
|
||||
|
||||
void CServerHandler::setCampaignMap(int mapId) const
|
||||
{
|
||||
if(state == EClientState::GAMEPLAY) // FIXME: UI shouldn't sent commands in first place
|
||||
return;
|
||||
|
||||
LobbySetCampaignMap lscm;
|
||||
lscm.mapId = mapId;
|
||||
sendLobbyPack(lscm);
|
||||
}
|
||||
|
||||
void CServerHandler::setCampaignBonus(int bonusId) const
|
||||
{
|
||||
if(state == EClientState::GAMEPLAY) // FIXME: UI shouldn't sent commands in first place
|
||||
return;
|
||||
|
||||
LobbySetCampaignBonus lscb;
|
||||
lscb.bonusId = bonusId;
|
||||
sendLobbyPack(lscb);
|
||||
}
|
||||
|
||||
void CServerHandler::setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts) const
|
||||
{
|
||||
LobbySetMap lsm;
|
||||
lsm.mapInfo = to;
|
||||
lsm.mapGenOpts = mapGenOpts;
|
||||
sendLobbyPack(lsm);
|
||||
}
|
||||
|
||||
void CServerHandler::setPlayer(PlayerColor color) const
|
||||
{
|
||||
LobbySetPlayer lsp;
|
||||
lsp.clickedColor = color;
|
||||
sendLobbyPack(lsp);
|
||||
}
|
||||
|
||||
void CServerHandler::setPlayerOption(ui8 what, ui8 dir, PlayerColor player) const
|
||||
{
|
||||
LobbyChangePlayerOption lcpo;
|
||||
lcpo.what = what;
|
||||
lcpo.direction = dir;
|
||||
lcpo.color = player;
|
||||
sendLobbyPack(lcpo);
|
||||
}
|
||||
|
||||
void CServerHandler::setDifficulty(int to) const
|
||||
{
|
||||
LobbySetDifficulty lsd;
|
||||
lsd.difficulty = to;
|
||||
sendLobbyPack(lsd);
|
||||
}
|
||||
|
||||
void CServerHandler::setTurnLength(int npos) const
|
||||
{
|
||||
vstd::amin(npos, GameConstants::POSSIBLE_TURNTIME.size() - 1);
|
||||
LobbySetTurnTime lstt;
|
||||
lstt.turnTime = GameConstants::POSSIBLE_TURNTIME[npos];
|
||||
sendLobbyPack(lstt);
|
||||
}
|
||||
|
||||
void CServerHandler::sendMessage(const std::string & txt) const
|
||||
{
|
||||
std::istringstream readed;
|
||||
readed.str(txt);
|
||||
std::string command;
|
||||
readed >> command;
|
||||
if(command == "!passhost")
|
||||
{
|
||||
std::string id;
|
||||
readed >> id;
|
||||
if(id.length())
|
||||
{
|
||||
LobbyChangeHost lch;
|
||||
lch.newHostConnectionId = boost::lexical_cast<int>(id);
|
||||
sendLobbyPack(lch);
|
||||
}
|
||||
}
|
||||
else if(command == "!forcep")
|
||||
{
|
||||
std::string connectedId, playerColorId;
|
||||
readed >> connectedId;
|
||||
readed >> playerColorId;
|
||||
if(connectedId.length(), playerColorId.length())
|
||||
{
|
||||
ui8 connected = boost::lexical_cast<int>(connectedId);
|
||||
auto color = PlayerColor(boost::lexical_cast<int>(playerColorId));
|
||||
if(color.isValidPlayer() && playerNames.find(connected) != playerNames.end())
|
||||
{
|
||||
LobbyForceSetPlayer lfsp;
|
||||
lfsp.targetConnectedPlayer = connected;
|
||||
lfsp.targetPlayerColor = color;
|
||||
sendLobbyPack(lfsp);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LobbyChatMessage lcm;
|
||||
lcm.message = txt;
|
||||
lcm.playerName = playerNames.find(myFirstId())->second.name;
|
||||
sendLobbyPack(lcm);
|
||||
}
|
||||
}
|
||||
|
||||
void CServerHandler::sendGuiAction(ui8 action) const
|
||||
{
|
||||
LobbyGuiAction lga;
|
||||
lga.action = static_cast<LobbyGuiAction::EAction>(action);
|
||||
sendLobbyPack(lga);
|
||||
}
|
||||
|
||||
void CServerHandler::sendStartGame(bool allowOnlyAI) const
|
||||
{
|
||||
verifyStateBeforeStart(allowOnlyAI ? true : settings["session"]["onlyai"].Bool());
|
||||
LobbyStartGame lsg;
|
||||
sendLobbyPack(lsg);
|
||||
}
|
||||
|
||||
void CServerHandler::startGameplay()
|
||||
{
|
||||
client = new CClient();
|
||||
|
||||
switch(si->mode)
|
||||
{
|
||||
case StartInfo::NEW_GAME:
|
||||
client->newGame();
|
||||
break;
|
||||
case StartInfo::CAMPAIGN:
|
||||
client->newGame();
|
||||
break;
|
||||
case StartInfo::LOAD_GAME:
|
||||
client->loadGame();
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Invalid mode");
|
||||
}
|
||||
// After everything initialized we can accept CPackToClient netpacks
|
||||
c->enterGameplayConnectionMode(client->gameState());
|
||||
state = EClientState::GAMEPLAY;
|
||||
}
|
||||
|
||||
void CServerHandler::endGameplay(bool closeConnection)
|
||||
{
|
||||
client->endGame();
|
||||
vstd::clear_pointer(client);
|
||||
|
||||
// Game is ending
|
||||
// Tell the network thread to reach a stable state
|
||||
CSH->sendClientDisconnecting();
|
||||
logNetwork->info("Closed connection.");
|
||||
}
|
||||
|
||||
void CServerHandler::startCampaignScenario(std::shared_ptr<CCampaignState> cs)
|
||||
{
|
||||
SDL_Event event;
|
||||
event.type = SDL_USEREVENT;
|
||||
event.user.code = EUserEvent::CAMPAIGN_START_SCENARIO;
|
||||
if(cs)
|
||||
event.user.data1 = CMemorySerializer::deepCopy(*cs.get()).release();
|
||||
else
|
||||
event.user.data1 = CMemorySerializer::deepCopy(*si->campState.get()).release();
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
int CServerHandler::howManyPlayerInterfaces()
|
||||
{
|
||||
int playerInts = 0;
|
||||
for(auto pint : client->playerint)
|
||||
{
|
||||
if(dynamic_cast<CPlayerInterface *>(pint.second.get()))
|
||||
playerInts++;
|
||||
}
|
||||
|
||||
return playerInts;
|
||||
}
|
||||
|
||||
ui8 CServerHandler::getLoadMode()
|
||||
{
|
||||
if(state == EClientState::GAMEPLAY)
|
||||
{
|
||||
if(si->campState)
|
||||
return ELoadMode::CAMPAIGN;
|
||||
for(auto pn : playerNames)
|
||||
{
|
||||
if(pn.second.connection != c->connectionID)
|
||||
return ELoadMode::MULTI;
|
||||
}
|
||||
|
||||
return ELoadMode::SINGLE;
|
||||
}
|
||||
return loadMode;
|
||||
}
|
||||
|
||||
void CServerHandler::debugStartTest(std::string filename, bool save)
|
||||
{
|
||||
logGlobal->info("Starting debug test with file: %s", filename);
|
||||
auto mapInfo = std::make_shared<CMapInfo>();
|
||||
if(save)
|
||||
{
|
||||
resetStateForLobby(StartInfo::LOAD_GAME);
|
||||
mapInfo->saveInit(ResourceID(filename, EResType::CLIENT_SAVEGAME));
|
||||
screenType = ESelectionScreen::loadGame;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetStateForLobby(StartInfo::NEW_GAME);
|
||||
mapInfo->mapInit(filename);
|
||||
screenType = ESelectionScreen::newGame;
|
||||
}
|
||||
if(settings["session"]["donotstartserver"].Bool())
|
||||
justConnectToServer("127.0.0.1", 3030);
|
||||
else
|
||||
startLocalServerAndConnect();
|
||||
|
||||
while(!settings["session"]["headless"].Bool() && !dynamic_cast<CLobbyScreen *>(GH.topInt()))
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
|
||||
while(!mi || mapInfo->fileURI != CSH->mi->fileURI)
|
||||
{
|
||||
setMapInfo(mapInfo);
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
|
||||
}
|
||||
// "Click" on color to remove us from it
|
||||
setPlayer(myFirstColor());
|
||||
while(myFirstColor() != PlayerColor::CANNOT_DETERMINE)
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
|
||||
|
||||
while(true)
|
||||
{
|
||||
try
|
||||
{
|
||||
sendStartGame();
|
||||
break;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
||||
}
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
|
||||
}
|
||||
}
|
||||
|
||||
void CServerHandler::threadHandleConnection()
|
||||
{
|
||||
setThreadName("CServerHandler::threadHandleConnection");
|
||||
c->enterLobbyConnectionMode();
|
||||
|
||||
try
|
||||
{
|
||||
sendClientConnecting();
|
||||
while(c->connected)
|
||||
{
|
||||
while(state == EClientState::STARTING)
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(10));
|
||||
|
||||
CPack * pack = c->retrievePack();
|
||||
if(state == EClientState::DISCONNECTING)
|
||||
{
|
||||
// FIXME: server shouldn't really send netpacks after it's tells client to disconnect
|
||||
// Though currently they'll be delivered and might cause crash.
|
||||
vstd::clear_pointer(pack);
|
||||
}
|
||||
else if(auto lobbyPack = dynamic_cast<CPackForLobby *>(pack))
|
||||
{
|
||||
if(applier->getApplier(typeList.getTypeID(pack))->applyOnLobbyHandler(this, pack))
|
||||
{
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*mx);
|
||||
packsForLobbyScreen.push_back(lobbyPack);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(auto clientPack = dynamic_cast<CPackForClient *>(pack))
|
||||
{
|
||||
client->handlePack(clientPack);
|
||||
}
|
||||
}
|
||||
}
|
||||
//catch only asio exceptions
|
||||
catch(const boost::system::system_error & e)
|
||||
{
|
||||
if(state == EClientState::DISCONNECTING)
|
||||
{
|
||||
logNetwork->info("Successfully closed connection to server, ending listening thread!");
|
||||
}
|
||||
else
|
||||
{
|
||||
logNetwork->error("Lost connection to server, ending listening thread!");
|
||||
logNetwork->error(e.what());
|
||||
if(client)
|
||||
{
|
||||
CGuiHandler::pushSDLEvent(SDL_USEREVENT, EUserEvent::RETURN_TO_MAIN_MENU);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lcd = new LobbyClientDisconnected();
|
||||
lcd->clientId = c->connectionID;
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*mx);
|
||||
packsForLobbyScreen.push_back(lcd);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
handleException();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void CServerHandler::threadRunServer()
|
||||
{
|
||||
#ifndef VCMI_ANDROID
|
||||
setThreadName("CServerHandler::threadRunServer");
|
||||
const std::string logName = (VCMIDirs::get().userCachePath() / "server_log.txt").string();
|
||||
std::string comm = VCMIDirs::get().serverPath().string()
|
||||
+ " --port=" + getDefaultPortStr()
|
||||
+ " --run-by-client"
|
||||
+ " --uuid=" + uuid;
|
||||
if(shm)
|
||||
{
|
||||
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)
|
||||
{
|
||||
logNetwork->info("Server closed correctly");
|
||||
}
|
||||
else
|
||||
{
|
||||
logNetwork->error("Error: server failed to close correctly or crashed!");
|
||||
logNetwork->error("Check %s for more info", logName);
|
||||
}
|
||||
threadRunLocalServer.reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CServerHandler::sendLobbyPack(const CPackForLobby & pack) const
|
||||
{
|
||||
if(state != EClientState::STARTING)
|
||||
c->sendPack(&pack);
|
||||
}
|
145
client/CServerHandler.h
Normal file
145
client/CServerHandler.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* CServerHandler.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../lib/CStopWatch.h"
|
||||
|
||||
#include "../lib/StartInfo.h"
|
||||
|
||||
struct SharedMemory;
|
||||
class CConnection;
|
||||
class PlayerColor;
|
||||
struct StartInfo;
|
||||
|
||||
class CMapInfo;
|
||||
struct ClientPlayer;
|
||||
struct CPack;
|
||||
struct CPackForLobby;
|
||||
class CClient;
|
||||
|
||||
template<typename T> class CApplier;
|
||||
class CBaseForLobbyApply;
|
||||
|
||||
// TODO: Add mutex so we can't set CONNECTION_CANCELLED if client already connected, but thread not setup yet
|
||||
enum class EClientState : ui8
|
||||
{
|
||||
NONE = 0,
|
||||
CONNECTING, // Trying to connect to server
|
||||
CONNECTION_CANCELLED, // Connection cancelled by player, stop attempts to connect
|
||||
LOBBY, // Client is connected to lobby
|
||||
LOBBY_CAMPAIGN, // Client is on scenario bonus selection screen
|
||||
STARTING, // Gameplay interfaces being created, we pause netpacks retrieving
|
||||
GAMEPLAY, // In-game, used by some UI
|
||||
DISCONNECTING // We disconnecting, drop all netpacks
|
||||
};
|
||||
|
||||
class IServerAPI
|
||||
{
|
||||
protected:
|
||||
virtual void sendLobbyPack(const CPackForLobby & pack) const = 0;
|
||||
|
||||
public:
|
||||
virtual ~IServerAPI() {}
|
||||
|
||||
virtual void sendClientConnecting() const = 0;
|
||||
virtual void sendClientDisconnecting() = 0;
|
||||
virtual void setCampaignState(std::shared_ptr<CCampaignState> newCampaign) = 0;
|
||||
virtual void setCampaignMap(int mapId) const = 0;
|
||||
virtual void setCampaignBonus(int bonusId) const = 0;
|
||||
virtual void setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts = {}) const = 0;
|
||||
virtual void setPlayer(PlayerColor color) const = 0;
|
||||
virtual void setPlayerOption(ui8 what, ui8 dir, PlayerColor player) const = 0;
|
||||
virtual void setDifficulty(int to) const = 0;
|
||||
virtual void setTurnLength(int npos) const = 0;
|
||||
virtual void sendMessage(const std::string & txt) const = 0;
|
||||
virtual void sendGuiAction(ui8 action) const = 0; // TODO: possibly get rid of it?
|
||||
virtual void sendStartGame(bool allowOnlyAI = false) const = 0;
|
||||
};
|
||||
|
||||
/// structure to handle running server and connecting to it
|
||||
class CServerHandler : public IServerAPI, public LobbyInfo
|
||||
{
|
||||
std::shared_ptr<CApplier<CBaseForLobbyApply>> applier;
|
||||
|
||||
std::shared_ptr<boost::recursive_mutex> mx;
|
||||
std::list<CPackForLobby *> packsForLobbyScreen; //protected by mx
|
||||
|
||||
std::vector<std::string> myNames;
|
||||
|
||||
void threadHandleConnection();
|
||||
void threadRunServer();
|
||||
void sendLobbyPack(const CPackForLobby & pack) const override;
|
||||
|
||||
public:
|
||||
std::atomic<EClientState> state;
|
||||
////////////////////
|
||||
// FIXME: Bunch of crutches to glue it all together
|
||||
|
||||
// For starting non-custom campaign and continue to next mission
|
||||
std::shared_ptr<CCampaignState> campaignStateToSend;
|
||||
|
||||
ui8 screenType; // To create lobby UI only after server is setup
|
||||
ui8 loadMode; // For saves filtering in SelectionTab
|
||||
////////////////////
|
||||
|
||||
std::unique_ptr<CStopWatch> th;
|
||||
std::shared_ptr<boost::thread> threadRunLocalServer;
|
||||
|
||||
std::shared_ptr<CConnection> c;
|
||||
CClient * client;
|
||||
|
||||
CServerHandler();
|
||||
|
||||
void resetStateForLobby(const StartInfo::EMode mode, const std::vector<std::string> * names = nullptr);
|
||||
void startLocalServerAndConnect();
|
||||
void justConnectToServer(const std::string &addr = "", const ui16 port = 0);
|
||||
void applyPacksOnLobbyScreen();
|
||||
void stopServerConnection();
|
||||
|
||||
// Helpers for lobby state access
|
||||
std::set<PlayerColor> getHumanColors();
|
||||
PlayerColor myFirstColor() const;
|
||||
bool isMyColor(PlayerColor color) const;
|
||||
ui8 myFirstId() const; // Used by chat only!
|
||||
|
||||
bool isServerLocal() const;
|
||||
bool isHost() const;
|
||||
bool isGuest() const;
|
||||
|
||||
static ui16 getDefaultPort();
|
||||
static std::string getDefaultPortStr();
|
||||
|
||||
// Lobby server API for UI
|
||||
void sendClientConnecting() const override;
|
||||
void sendClientDisconnecting() override;
|
||||
void setCampaignState(std::shared_ptr<CCampaignState> newCampaign) override;
|
||||
void setCampaignMap(int mapId) const override;
|
||||
void setCampaignBonus(int bonusId) const override;
|
||||
void setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts = {}) const override;
|
||||
void setPlayer(PlayerColor color) const override;
|
||||
void setPlayerOption(ui8 what, ui8 dir, PlayerColor player) const override;
|
||||
void setDifficulty(int to) const override;
|
||||
void setTurnLength(int npos) const override;
|
||||
void sendMessage(const std::string & txt) const override;
|
||||
void sendGuiAction(ui8 action) const override;
|
||||
void sendStartGame(bool allowOnlyAI = false) const override;
|
||||
|
||||
void startGameplay();
|
||||
void endGameplay(bool closeConnection = true);
|
||||
void startCampaignScenario(std::shared_ptr<CCampaignState> cs = {});
|
||||
|
||||
// TODO: LobbyState must be updated within game so we should always know how many player interfaces our client handle
|
||||
int howManyPlayerInterfaces();
|
||||
ui8 getLoadMode();
|
||||
|
||||
void debugStartTest(std::string filename, bool save = false);
|
||||
};
|
||||
|
||||
extern CServerHandler * CSH;
|
1207
client/Client.cpp
1207
client/Client.cpp
File diff suppressed because it is too large
Load Diff
195
client/Client.h
195
client/Client.h
@ -9,25 +9,24 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../lib/IGameCallback.h"
|
||||
#include "../lib/battle/BattleAction.h"
|
||||
#include "../lib/CStopWatch.h"
|
||||
#include "../lib/int3.h"
|
||||
#include "../lib/CondSh.h"
|
||||
#include "../lib/CPathfinder.h"
|
||||
|
||||
struct CPack;
|
||||
struct CPackForServer;
|
||||
class CCampaignState;
|
||||
class CBattleCallback;
|
||||
class IGameEventsReceiver;
|
||||
class IBattleEventsReceiver;
|
||||
class CBattleGameInterface;
|
||||
struct StartInfo;
|
||||
class CGameState;
|
||||
class CGameInterface;
|
||||
class CConnection;
|
||||
class CCallback;
|
||||
class BattleAction;
|
||||
struct SharedMemory;
|
||||
struct BattleAction;
|
||||
class CClient;
|
||||
class CScriptingModule;
|
||||
struct CPathsInfo;
|
||||
@ -35,31 +34,8 @@ class BinaryDeserializer;
|
||||
class BinarySerializer;
|
||||
namespace boost { class thread; }
|
||||
|
||||
/// structure to handle running server and connecting to it
|
||||
class CServerHandler
|
||||
{
|
||||
private:
|
||||
void callServer(); //calls server via system(), should be called as thread
|
||||
public:
|
||||
CStopWatch th;
|
||||
boost::thread *serverThread; //thread that called system to run server
|
||||
SharedMemory * shared;
|
||||
std::string uuid;
|
||||
bool verbose; //whether to print log msgs
|
||||
|
||||
//functions setting up local server
|
||||
void startServer(); //creates a thread with callServer
|
||||
void waitForServer(); //waits till server is ready
|
||||
CConnection * connectToServer(); //connects to 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();
|
||||
};
|
||||
template<typename T> class CApplier;
|
||||
class CBaseForCLApply;
|
||||
|
||||
template<typename T>
|
||||
class ThreadSafeVector
|
||||
@ -78,7 +54,7 @@ public:
|
||||
cond.notify_all();
|
||||
}
|
||||
|
||||
void pushBack(const T &item)
|
||||
void pushBack(const T & item)
|
||||
{
|
||||
TLock lock(mx);
|
||||
items.push_back(item);
|
||||
@ -97,14 +73,14 @@ public:
|
||||
return TLock(mx);
|
||||
}
|
||||
|
||||
void waitWhileContains(const T &item)
|
||||
void waitWhileContains(const T & item)
|
||||
{
|
||||
auto lock = getLock();
|
||||
while(vstd::contains(items, item))
|
||||
cond.wait(lock);
|
||||
}
|
||||
|
||||
bool tryRemovingElement(const T&item) //returns false if element was not present
|
||||
bool tryRemovingElement(const T & item) //returns false if element was not present
|
||||
{
|
||||
auto lock = getLock();
|
||||
auto itr = vstd::find(items, item);
|
||||
@ -122,105 +98,104 @@ public:
|
||||
/// Class which handles client - server logic
|
||||
class CClient : public IGameCallback
|
||||
{
|
||||
std::shared_ptr<CApplier<CBaseForCLApply>> applier;
|
||||
std::unique_ptr<CPathsInfo> pathInfo;
|
||||
|
||||
std::map<PlayerColor, std::shared_ptr<boost::thread>> playerActionThreads;
|
||||
void waitForMoveAndSend(PlayerColor color);
|
||||
|
||||
public:
|
||||
std::map<PlayerColor,std::shared_ptr<CCallback> > callbacks; //callbacks given to player interfaces
|
||||
std::map<PlayerColor,std::shared_ptr<CBattleCallback> > battleCallbacks; //callbacks given to player interfaces
|
||||
std::map<PlayerColor, std::shared_ptr<CCallback>> callbacks; //callbacks given to player interfaces
|
||||
std::map<PlayerColor, std::shared_ptr<CBattleCallback>> battleCallbacks; //callbacks given to player interfaces
|
||||
std::vector<std::shared_ptr<IGameEventsReceiver>> privilegedGameEventReceivers; //scripting modules, spectator interfaces
|
||||
std::vector<std::shared_ptr<IBattleEventsReceiver>> privilegedBattleEventReceivers; //scripting modules, spectator interfaces
|
||||
std::map<PlayerColor, std::shared_ptr<CGameInterface>> playerint;
|
||||
std::map<PlayerColor, std::shared_ptr<CBattleGameInterface>> battleints;
|
||||
|
||||
std::map<PlayerColor,std::vector<std::shared_ptr<IGameEventsReceiver>>> additionalPlayerInts;
|
||||
std::map<PlayerColor,std::vector<std::shared_ptr<IBattleEventsReceiver>>> additionalBattleInts;
|
||||
|
||||
bool hotSeat;
|
||||
CConnection *serv;
|
||||
std::map<PlayerColor, std::vector<std::shared_ptr<IGameEventsReceiver>>> additionalPlayerInts;
|
||||
std::map<PlayerColor, std::vector<std::shared_ptr<IBattleEventsReceiver>>> additionalBattleInts;
|
||||
|
||||
boost::optional<BattleAction> curbaction;
|
||||
|
||||
CScriptingModule *erm;
|
||||
|
||||
static ThreadSafeVector<int> waitingRequest;//FIXME: make this normal field (need to join all threads before client destruction)
|
||||
|
||||
|
||||
//void sendRequest(const CPackForServer *request, bool waitForRealization);
|
||||
CScriptingModule * erm;
|
||||
CClient();
|
||||
CClient(CConnection *con, StartInfo *si);
|
||||
~CClient();
|
||||
|
||||
void init();
|
||||
void newGame(CConnection *con, StartInfo *si); //con - connection to server
|
||||
void newGame();
|
||||
void loadGame();
|
||||
void serialize(BinarySerializer & h, const int version);
|
||||
void serialize(BinaryDeserializer & h, const int version);
|
||||
|
||||
void loadNeutralBattleAI();
|
||||
void save(const std::string & fname);
|
||||
void endGame();
|
||||
|
||||
void initMapHandler();
|
||||
void initPlayerInterfaces();
|
||||
std::string aiNameForPlayer(const PlayerSettings & ps, bool battleAI); //empty means no AI -> human
|
||||
std::string aiNameForPlayer(bool battleAI);
|
||||
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 ui16 port = 0);
|
||||
void run();
|
||||
void campaignMapFinished( std::shared_ptr<CCampaignState> camp );
|
||||
void finishCampaign( std::shared_ptr<CCampaignState> camp );
|
||||
void proposeNextMission(std::shared_ptr<CCampaignState> camp);
|
||||
|
||||
static ThreadSafeVector<int> waitingRequest; //FIXME: make this normal field (need to join all threads before client destruction)
|
||||
|
||||
void handlePack(CPack * pack); //applies the given pack and deletes it
|
||||
void commitPackage(CPackForClient * pack) override;
|
||||
int sendRequest(const CPackForServer * request, PlayerColor player); //returns ID given to that request
|
||||
|
||||
void battleStarted(const BattleInfo * info);
|
||||
void commenceTacticPhaseForInt(std::shared_ptr<CBattleGameInterface> battleInt); //will be called as separate thread
|
||||
void battleFinished();
|
||||
void startPlayerBattleAction(PlayerColor color);
|
||||
void stopPlayerBattleAction(PlayerColor color);
|
||||
void stopAllBattleActions();
|
||||
|
||||
void invalidatePaths();
|
||||
const CPathsInfo * getPathsInfo(const CGHeroInstance *h);
|
||||
|
||||
bool terminate; // tell to terminate
|
||||
std::unique_ptr<boost::thread> connectionHandler; //thread running run() method
|
||||
boost::mutex connectionHandlerMutex;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const CPathsInfo * getPathsInfo(const CGHeroInstance * h);
|
||||
virtual PlayerColor getLocalPlayer() const override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//not working yet, will be implement somewhen later with support for local-sim-based gameplay
|
||||
void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> &spells) override {};
|
||||
friend class CCallback; //handling players actions
|
||||
friend class CBattleCallback; //handling players actions
|
||||
|
||||
void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> & spells) override {};
|
||||
bool removeObject(const CGObjectInstance * obj) override {return false;};
|
||||
void setBlockVis(ObjectInstanceID objid, bool bv) override {};
|
||||
void setOwner(const CGObjectInstance * obj, PlayerColor owner) override {};
|
||||
void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false) override {};
|
||||
void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) override {};
|
||||
void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs = false) override {};
|
||||
void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs = false) override {};
|
||||
|
||||
void showBlockingDialog(BlockingDialog *iw) override {};
|
||||
void showBlockingDialog(BlockingDialog * iw) override {};
|
||||
void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override {};
|
||||
void showTeleportDialog(TeleportDialog *iw) override {};
|
||||
void showTeleportDialog(TeleportDialog * iw) override {};
|
||||
void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) override {};
|
||||
void giveResource(PlayerColor player, Res::ERes which, int val) override {};
|
||||
virtual void giveResources(PlayerColor player, TResources resources) override {};
|
||||
|
||||
void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) override {};
|
||||
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) override {};
|
||||
bool changeStackType(const StackLocation &sl, const CCreature *c) override {return false;};
|
||||
bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) override {return false;};
|
||||
bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count) override {return false;};
|
||||
bool eraseStack(const StackLocation &sl, bool forceRemoval = false) override{return false;};
|
||||
bool swapStacks(const StackLocation &sl1, const StackLocation &sl2) override {return false;}
|
||||
bool addToSlot(const StackLocation &sl, const CCreature *c, TQuantity count) override {return false;}
|
||||
void tryJoiningArmy(const CArmedInstance *src, const CArmedInstance *dst, bool removeObjWhenFinished, bool allowMerging) override {}
|
||||
bool moveStack(const StackLocation &src, const StackLocation &dst, TQuantity count = -1) override {return false;}
|
||||
void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet & creatures, bool remove) override {};
|
||||
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> & creatures) override {};
|
||||
bool changeStackType(const StackLocation & sl, const CCreature * c) override {return false;};
|
||||
bool changeStackCount(const StackLocation & sl, TQuantity count, bool absoluteValue = false) override {return false;};
|
||||
bool insertNewStack(const StackLocation & sl, const CCreature * c, TQuantity count) override {return false;};
|
||||
bool eraseStack(const StackLocation & sl, bool forceRemoval = false) override {return false;};
|
||||
bool swapStacks(const StackLocation & sl1, const StackLocation & sl2) override {return false;}
|
||||
bool addToSlot(const StackLocation & sl, const CCreature * c, TQuantity count) override {return false;}
|
||||
void tryJoiningArmy(const CArmedInstance * src, const CArmedInstance * dst, bool removeObjWhenFinished, bool allowMerging) override {}
|
||||
bool moveStack(const StackLocation & src, const StackLocation & dst, TQuantity count = -1) override {return false;}
|
||||
|
||||
void removeAfterVisit(const CGObjectInstance *object) override {};
|
||||
void removeAfterVisit(const CGObjectInstance * object) override {};
|
||||
|
||||
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 {};
|
||||
void removeArtifact(const ArtifactLocation &al) override {};
|
||||
bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) override {return false;};
|
||||
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 {};
|
||||
void removeArtifact(const ArtifactLocation & al) override {};
|
||||
bool moveArtifact(const ArtifactLocation & al1, const ArtifactLocation & al2) override {return false;};
|
||||
void synchronizeArtifactHandlerLists() override {};
|
||||
|
||||
void showCompInfo(ShowInInfobox * comp) override {};
|
||||
void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override {};
|
||||
void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override {};
|
||||
void startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, const CGTownInstance *town = nullptr) override {}; //use hero=nullptr for no hero
|
||||
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used
|
||||
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
|
||||
void startBattlePrimary(const CArmedInstance * army1, const CArmedInstance * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank = false, const CGTownInstance * town = nullptr) override {}; //use hero=nullptr for no hero
|
||||
void startBattleI(const CArmedInstance * army1, const CArmedInstance * army2, int3 tile, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used
|
||||
void startBattleI(const CArmedInstance * army1, const CArmedInstance * army2, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
|
||||
void setAmount(ObjectInstanceID objid, ui32 val) override {};
|
||||
bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, bool transit = false, PlayerColor asker = PlayerColor::NEUTRAL) override {return false;};
|
||||
void giveHeroBonus(GiveBonus * bonus) override {};
|
||||
@ -228,37 +203,9 @@ public:
|
||||
void setManaPoints(ObjectInstanceID hid, int val) override {};
|
||||
void giveHero(ObjectInstanceID id, PlayerColor player) override {};
|
||||
void changeObjPos(ObjectInstanceID objid, int3 newPos, ui8 flags) override {};
|
||||
void sendAndApply(CPackForClient * info) override {};
|
||||
void sendAndApply(CPackForClient * pack) override {};
|
||||
void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override {};
|
||||
|
||||
void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) override {}
|
||||
void changeFogOfWar(std::unordered_set<int3, ShashInt3> &tiles, PlayerColor player, bool hide) override {}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
friend class CCallback; //handling players actions
|
||||
friend class CBattleCallback; //handling players actions
|
||||
|
||||
int sendRequest(const CPack *request, PlayerColor player); //returns ID given to that request
|
||||
|
||||
void handlePack( CPack * pack ); //applies the given pack and deletes it
|
||||
void battleStarted(const BattleInfo * info);
|
||||
void commenceTacticPhaseForInt(std::shared_ptr<CBattleGameInterface> battleInt); //will be called as separate thread
|
||||
|
||||
void commitPackage(CPackForClient *pack) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void serialize(BinarySerializer & h, const int version);
|
||||
void serialize(BinaryDeserializer & h, const int version);
|
||||
|
||||
void serialize(BinarySerializer & h, const int version, const std::set<PlayerColor>& playerIDs);
|
||||
void serialize(BinaryDeserializer & h, const int version, const std::set<PlayerColor>& playerIDs);
|
||||
void battleFinished();
|
||||
|
||||
void startPlayerBattleAction(PlayerColor color);
|
||||
|
||||
void stopPlayerBattleAction(PlayerColor color);
|
||||
void stopAllBattleActions();
|
||||
private:
|
||||
void waitForMoveAndSend(PlayerColor color);
|
||||
void changeFogOfWar(std::unordered_set<int3, ShashInt3> & tiles, PlayerColor player, bool hide) override {}
|
||||
};
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "widgets/MiscWidgets.h"
|
||||
#include "widgets/AdventureMapClasses.h"
|
||||
#include "CMT.h"
|
||||
#include "CServerHandler.h"
|
||||
|
||||
// TODO: as Tow suggested these template should all be part of CClient
|
||||
// This will require rework spectator interface properly though
|
||||
@ -357,17 +358,6 @@ void RemoveBonus::applyCl(CClient *cl)
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateCampaignState::applyCl(CClient *cl)
|
||||
{
|
||||
cl->stopConnection();
|
||||
cl->campaignMapFinished(camp);
|
||||
}
|
||||
|
||||
void PrepareForAdvancingCampaign::applyCl(CClient *cl)
|
||||
{
|
||||
cl->serv->prepareForSendingHeroes();
|
||||
}
|
||||
|
||||
void RemoveObject::applyFirstCl(CClient *cl)
|
||||
{
|
||||
const CGObjectInstance *o = cl->getObj(id);
|
||||
@ -757,7 +747,7 @@ void PackageApplied::applyCl(CClient *cl)
|
||||
{
|
||||
callInterfaceIfPresent(cl, player, &IGameEventsReceiver::requestRealized, this);
|
||||
if(!CClient::waitingRequest.tryRemovingElement(requestID))
|
||||
logNetwork->warn("Surprising server message!");
|
||||
logNetwork->warn("Surprising server message! PackageApplied for unknown requestID!");
|
||||
}
|
||||
|
||||
void SystemMessage::applyCl(CClient *cl)
|
||||
@ -777,11 +767,13 @@ void PlayerBlocked::applyCl(CClient *cl)
|
||||
|
||||
void YourTurn::applyCl(CClient *cl)
|
||||
{
|
||||
logNetwork->debug("Server gives turn to %s", player.getStr());
|
||||
|
||||
callAllInterfaces(cl, &IGameEventsReceiver::playerStartsTurn, player);
|
||||
callOnlyThatInterface(cl, player, &CGameInterface::yourTurn);
|
||||
}
|
||||
|
||||
void SaveGame::applyCl(CClient *cl)
|
||||
void SaveGameClient::applyCl(CClient *cl)
|
||||
{
|
||||
const auto stem = FileInfo::GetPathStem(fname);
|
||||
CResourceHandler::get("local")->createResource(stem.to_string() + ".vcgm1");
|
||||
@ -798,7 +790,7 @@ void SaveGame::applyCl(CClient *cl)
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerMessage::applyCl(CClient *cl)
|
||||
void PlayerMessageClient::applyCl(CClient *cl)
|
||||
{
|
||||
logNetwork->debug("Player %s sends a message: %s", player.getStr(), text);
|
||||
|
||||
|
142
client/NetPacksLobbyClient.cpp
Normal file
142
client/NetPacksLobbyClient.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* NetPacksLobbyClient.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "lobby/CSelectionBase.h"
|
||||
#include "lobby/CLobbyScreen.h"
|
||||
|
||||
#include "lobby/OptionsTab.h"
|
||||
#include "lobby/RandomMapTab.h"
|
||||
#include "lobby/SelectionTab.h"
|
||||
#include "lobby/CBonusSelection.h"
|
||||
|
||||
#include "CServerHandler.h"
|
||||
#include "CGameInfo.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "widgets/Buttons.h"
|
||||
#include "widgets/TextControls.h"
|
||||
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/NetPacksLobby.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
|
||||
bool LobbyClientConnected::applyOnLobbyHandler(CServerHandler * handler)
|
||||
{
|
||||
// Check if it's LobbyClientConnected for our client
|
||||
if(uuid == handler->c->uuid)
|
||||
{
|
||||
handler->c->connectionID = clientId;
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
GH.pushInt(new CLobbyScreen(static_cast<ESelectionScreen>(handler->screenType)));
|
||||
handler->state = EClientState::LOBBY;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LobbyClientConnected::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
||||
{
|
||||
if(uuid == handler->c->uuid)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
bool LobbyClientDisconnected::applyOnLobbyHandler(CServerHandler * handler)
|
||||
{
|
||||
if(clientId != c->connectionID)
|
||||
return false;
|
||||
|
||||
handler->stopServerConnection();
|
||||
return true;
|
||||
}
|
||||
|
||||
void LobbyClientDisconnected::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
||||
{
|
||||
GH.popIntTotally(lobby);
|
||||
}
|
||||
|
||||
void LobbyChatMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
||||
{
|
||||
if(lobby)
|
||||
{
|
||||
lobby->card->chat->addNewMessage(playerName + ": " + message);
|
||||
lobby->card->setChat(true);
|
||||
if(lobby->buttonChat)
|
||||
lobby->buttonChat->addTextOverlay(CGI->generaltexth->allTexts[531], FONT_SMALL);
|
||||
}
|
||||
}
|
||||
|
||||
void LobbyGuiAction::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
||||
{
|
||||
if(!handler->isGuest())
|
||||
return;
|
||||
|
||||
switch(action)
|
||||
{
|
||||
case NO_TAB:
|
||||
lobby->toggleTab(lobby->curTab);
|
||||
break;
|
||||
case OPEN_OPTIONS:
|
||||
lobby->toggleTab(lobby->tabOpt);
|
||||
break;
|
||||
case OPEN_SCENARIO_LIST:
|
||||
lobby->toggleTab(lobby->tabSel);
|
||||
break;
|
||||
case OPEN_RANDOM_MAP_OPTIONS:
|
||||
lobby->toggleTab(lobby->tabRand);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool LobbyStartGame::applyOnLobbyHandler(CServerHandler * handler)
|
||||
{
|
||||
if(handler->state == EClientState::GAMEPLAY)
|
||||
{
|
||||
handler->endGameplay(false);
|
||||
}
|
||||
handler->state = EClientState::STARTING;
|
||||
if(handler->si->mode != StartInfo::LOAD_GAME)
|
||||
{
|
||||
handler->si = initializedStartInfo;
|
||||
}
|
||||
if(settings["session"]["headless"].Bool())
|
||||
handler->startGameplay();
|
||||
return true;
|
||||
}
|
||||
|
||||
void LobbyStartGame::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
||||
{
|
||||
CMM->showLoadingScreen(std::bind(&CServerHandler::startGameplay, handler));
|
||||
}
|
||||
|
||||
bool LobbyUpdateState::applyOnLobbyHandler(CServerHandler * handler)
|
||||
{
|
||||
hostChanged = state.hostClientId != handler->hostClientId;
|
||||
static_cast<LobbyState &>(*handler) = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LobbyUpdateState::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
||||
{
|
||||
if(!lobby->bonusSel && handler->si->campState && handler->state == EClientState::LOBBY_CAMPAIGN)
|
||||
{
|
||||
lobby->bonusSel = new CBonusSelection();
|
||||
GH.pushInt(lobby->bonusSel);
|
||||
}
|
||||
|
||||
if(lobby->bonusSel)
|
||||
lobby->bonusSel->updateAfterStateChange();
|
||||
else
|
||||
lobby->updateAfterStateChange();
|
||||
|
||||
if(hostChanged)
|
||||
lobby->toggleMode(handler->isHost());
|
||||
}
|
@ -106,7 +106,7 @@ void CGuiHandler::popInt(IShowActivatable *top)
|
||||
listInt.front()->activate();
|
||||
totalRedraw();
|
||||
|
||||
pushSDLEvent(SDL_USEREVENT, INTERFACE_CHANGED);
|
||||
pushSDLEvent(SDL_USEREVENT, EUserEvent::INTERFACE_CHANGED);
|
||||
}
|
||||
|
||||
void CGuiHandler::popIntTotally(IShowActivatable *top)
|
||||
@ -132,7 +132,7 @@ void CGuiHandler::pushInt(IShowActivatable *newInt)
|
||||
objsToBlit.push_back(newInt);
|
||||
totalRedraw();
|
||||
|
||||
pushSDLEvent(SDL_USEREVENT, INTERFACE_CHANGED);
|
||||
pushSDLEvent(SDL_USEREVENT, EUserEvent::INTERFACE_CHANGED);
|
||||
}
|
||||
|
||||
void CGuiHandler::popInts(int howMany)
|
||||
@ -155,7 +155,7 @@ void CGuiHandler::popInts(int howMany)
|
||||
}
|
||||
fakeMouseMove();
|
||||
|
||||
pushSDLEvent(SDL_USEREVENT, INTERFACE_CHANGED);
|
||||
pushSDLEvent(SDL_USEREVENT, EUserEvent::INTERFACE_CHANGED);
|
||||
}
|
||||
|
||||
IShowActivatable * CGuiHandler::topInt()
|
||||
|
@ -22,6 +22,20 @@ class IShowable;
|
||||
enum class EIntObjMouseBtnType;
|
||||
template <typename T> struct CondSh;
|
||||
|
||||
// TODO: event handling need refactoring
|
||||
enum EUserEvent
|
||||
{
|
||||
/*CHANGE_SCREEN_RESOLUTION = 1,*/
|
||||
RETURN_TO_MAIN_MENU = 2,
|
||||
//STOP_CLIENT = 3,
|
||||
RESTART_GAME = 4,
|
||||
RETURN_TO_MENU_LOAD,
|
||||
FULLSCREEN_TOGGLED,
|
||||
CAMPAIGN_START_SCENARIO,
|
||||
FORCE_QUIT, //quit client without question
|
||||
INTERFACE_CHANGED
|
||||
};
|
||||
|
||||
// A fps manager which holds game updates at a constant rate
|
||||
class CFramerateManager
|
||||
{
|
||||
@ -119,11 +133,6 @@ public:
|
||||
|
||||
extern CGuiHandler GH; //global gui handler
|
||||
|
||||
template <typename T> void pushIntT()
|
||||
{
|
||||
GH.pushInt(new T());
|
||||
}
|
||||
|
||||
struct SObjectConstruction
|
||||
{
|
||||
CIntObject *myObj;
|
||||
@ -142,5 +151,6 @@ struct SSetCaptureState
|
||||
#define OBJ_CONSTRUCTION SObjectConstruction obj__i(this)
|
||||
#define OBJECT_CONSTRUCTION_CAPTURING(actions) defActions = actions; SSetCaptureState obj__i1(true, actions); SObjectConstruction obj__i(this)
|
||||
#define OBJ_CONSTRUCTION_CAPTURING_ALL defActions = 255; SSetCaptureState obj__i1(true, 255); SObjectConstruction obj__i(this)
|
||||
#define OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE defActions = 255 - DISPOSE; SSetCaptureState obj__i1(true, 255 - DISPOSE); SObjectConstruction obj__i(this)
|
||||
#define BLOCK_CAPTURING SSetCaptureState obj__i(false, 0)
|
||||
#define BLOCK_CAPTURING_DONT_TOUCH_REC_ACTIONS SSetCaptureState obj__i(false, GH.defActionsDef)
|
||||
|
@ -20,6 +20,8 @@ const SDL_Color Colors::YELLOW = { 229, 215, 123, 0 };
|
||||
const SDL_Color Colors::WHITE = { 255, 243, 222, 0 };
|
||||
const SDL_Color Colors::METALLIC_GOLD = { 173, 142, 66, 0 };
|
||||
const SDL_Color Colors::GREEN = { 0, 255, 0, 0 };
|
||||
const SDL_Color Colors::ORANGE = { 232, 184, 32, 0 };
|
||||
const SDL_Color Colors::BRIGHT_YELLOW = { 242, 226, 110, 0 };
|
||||
const SDL_Color Colors::DEFAULT_KEY_COLOR = {0, 255, 255, 0};
|
||||
|
||||
void SDL_UpdateRect(SDL_Surface *surface, int x, int y, int w, int h)
|
||||
|
@ -89,6 +89,12 @@ public:
|
||||
/** green color used for in-game console */
|
||||
static const SDL_Color GREEN;
|
||||
|
||||
/** the h3 orange color, used for blocked buttons */
|
||||
static const SDL_Color ORANGE;
|
||||
|
||||
/** the h3 bright yellow color, used for selection border */
|
||||
static const SDL_Color BRIGHT_YELLOW;
|
||||
|
||||
/** default key color for all 8 & 24 bit graphics */
|
||||
static const SDL_Color DEFAULT_KEY_COLOR;
|
||||
};
|
||||
|
534
client/lobby/CBonusSelection.cpp
Normal file
534
client/lobby/CBonusSelection.cpp
Normal file
@ -0,0 +1,534 @@
|
||||
/*
|
||||
* CBonusSelection.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CBonusSelection.h"
|
||||
#include "CSelectionBase.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMessage.h"
|
||||
#include "../CBitmapHandler.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CVideoHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
#include "../mainmenu/CPrologEpilogVideo.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../windows/GUIClasses.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
|
||||
#include "../../lib/CArtHandler.h"
|
||||
#include "../../lib/CBuildingHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
|
||||
#include "../../lib/CSkillHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/StartInfo.h"
|
||||
|
||||
#include "../../lib/mapping/CCampaignHandler.h"
|
||||
#include "../../lib/mapping/CMapService.h"
|
||||
#include "../../lib/mapping/CMapInfo.h"
|
||||
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
|
||||
std::shared_ptr<CCampaignState> CBonusSelection::getCampaign()
|
||||
{
|
||||
return CSH->si->campState;
|
||||
}
|
||||
|
||||
CBonusSelection::CBonusSelection()
|
||||
: CWindowObject(BORDERED)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
static const std::string bgNames[] =
|
||||
{
|
||||
"E1_BG.BMP", "G2_BG.BMP", "E2_BG.BMP", "G1_BG.BMP", "G3_BG.BMP", "N1_BG.BMP",
|
||||
"S1_BG.BMP", "BR_BG.BMP", "IS_BG.BMP", "KR_BG.BMP", "NI_BG.BMP", "TA_BG.BMP", "AR_BG.BMP", "HS_BG.BMP",
|
||||
"BB_BG.BMP", "NB_BG.BMP", "EL_BG.BMP", "RN_BG.BMP", "UA_BG.BMP", "SP_BG.BMP"
|
||||
};
|
||||
loadPositionsOfGraphics();
|
||||
setBackground(bgNames[getCampaign()->camp->header.mapVersion]);
|
||||
|
||||
panelBackground = std::make_shared<CPicture>("CAMPBRF.BMP", 456, 6);
|
||||
|
||||
buttonStart = std::make_shared<CButton>(Point(475, 536), "CBBEGIB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::startMap, this), SDLK_RETURN);
|
||||
buttonRestart = std::make_shared<CButton>(Point(475, 536), "CBRESTB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::restartMap, this), SDLK_RETURN);
|
||||
buttonBack = std::make_shared<CButton>(Point(624, 536), "CBCANCB.DEF", CButton::tooltip(), std::bind(&CBonusSelection::goBack, this), SDLK_ESCAPE);
|
||||
|
||||
campaignName = std::make_shared<CLabel>(481, 28, FONT_BIG, EAlignment::TOPLEFT, Colors::YELLOW, CSH->si->getCampaignName());
|
||||
|
||||
iconsMapSizes = std::make_shared<CAnimImage>("SCNRMPSZ", 4, 0, 735, 26);
|
||||
|
||||
labelCampaignDescription = std::make_shared<CLabel>(481, 63, FONT_SMALL, EAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[38]);
|
||||
campaignDescription = std::make_shared<CTextBox>(getCampaign()->camp->header.description, Rect(480, 86, 286, 117), 1);
|
||||
|
||||
mapName = std::make_shared<CLabel>(481, 219, FONT_BIG, EAlignment::TOPLEFT, Colors::YELLOW, CSH->mi->getName());
|
||||
labelMapDescription = std::make_shared<CLabel>(481, 253, FONT_SMALL, EAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[496]);
|
||||
mapDescription = std::make_shared<CTextBox>("", Rect(480, 280, 286, 117), 1);
|
||||
|
||||
labelChooseBonus = std::make_shared<CLabel>(511, 432, FONT_SMALL, EAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[71]);
|
||||
groupBonuses = std::make_shared<CToggleGroup>(std::bind(&IServerAPI::setCampaignBonus, CSH, _1));
|
||||
|
||||
flagbox = std::make_shared<CFlagBox>(Rect(486, 407, 335, 23));
|
||||
|
||||
std::vector<std::string> difficulty;
|
||||
boost::split(difficulty, CGI->generaltexth->allTexts[492], boost::is_any_of(" "));
|
||||
labelDifficulty = std::make_shared<CLabel>(689, 432, FONT_MEDIUM, EAlignment::TOPLEFT, Colors::WHITE, difficulty.back());
|
||||
|
||||
for(size_t b = 0; b < difficultyIcons.size(); ++b)
|
||||
{
|
||||
difficultyIcons[b] = std::make_shared<CAnimImage>("GSPBUT" + boost::lexical_cast<std::string>(b + 3) + ".DEF", 0, 0, 709, 455);
|
||||
}
|
||||
|
||||
if(getCampaign()->camp->header.difficultyChoosenByPlayer)
|
||||
{
|
||||
buttonDifficultyLeft = std::make_shared<CButton>(Point(694, 508), "SCNRBLF.DEF", CButton::tooltip(), std::bind(&CBonusSelection::decreaseDifficulty, this));
|
||||
buttonDifficultyRight = std::make_shared<CButton>(Point(738, 508), "SCNRBRT.DEF", CButton::tooltip(), std::bind(&CBonusSelection::increaseDifficulty, this));
|
||||
}
|
||||
|
||||
for(int g = 0; g < getCampaign()->camp->scenarios.size(); ++g)
|
||||
{
|
||||
if(getCampaign()->camp->conquerable(g))
|
||||
regions.push_back(std::make_shared<CRegion>(g, true, true, campDescriptions[getCampaign()->camp->header.mapVersion]));
|
||||
else if(getCampaign()->camp->scenarios[g].conquered) //display as striped
|
||||
regions.push_back(std::make_shared<CRegion>(g, false, false, campDescriptions[getCampaign()->camp->header.mapVersion]));
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSelection::loadPositionsOfGraphics()
|
||||
{
|
||||
const JsonNode config(ResourceID("config/campaign_regions.json"));
|
||||
int idx = 0;
|
||||
|
||||
for(const JsonNode & campaign : config["campaign_regions"].Vector())
|
||||
{
|
||||
SCampPositions sc;
|
||||
|
||||
sc.campPrefix = campaign["prefix"].String();
|
||||
sc.colorSuffixLength = campaign["color_suffix_length"].Float();
|
||||
|
||||
for(const JsonNode & desc : campaign["desc"].Vector())
|
||||
{
|
||||
SCampPositions::SRegionDesc rd;
|
||||
|
||||
rd.infix = desc["infix"].String();
|
||||
rd.xpos = desc["x"].Float();
|
||||
rd.ypos = desc["y"].Float();
|
||||
sc.regions.push_back(rd);
|
||||
}
|
||||
|
||||
campDescriptions.push_back(sc);
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
assert(idx == CGI->generaltexth->campaignMapNames.size());
|
||||
}
|
||||
|
||||
void CBonusSelection::createBonusesIcons()
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
const CCampaignScenario & scenario = getCampaign()->camp->scenarios[CSH->campaignMap];
|
||||
const std::vector<CScenarioTravel::STravelBonus> & bonDescs = scenario.travelOptions.bonusesToChoose;
|
||||
groupBonuses = std::make_shared<CToggleGroup>(std::bind(&IServerAPI::setCampaignBonus, CSH, _1));
|
||||
|
||||
static const char * bonusPics[] =
|
||||
{
|
||||
"SPELLBON.DEF", // Spell
|
||||
"TWCRPORT.DEF", // Monster
|
||||
"", // Building - BO*.BMP
|
||||
"ARTIFBON.DEF", // Artifact
|
||||
"SPELLBON.DEF", // Spell scroll
|
||||
"PSKILBON.DEF", // Primary skill
|
||||
"SSKILBON.DEF", // Secondary skill
|
||||
"BORES.DEF", // Resource
|
||||
"PORTRAITSLARGE", // Hero HPL*.BMP
|
||||
"PORTRAITSLARGE"
|
||||
// Player - CREST58.DEF
|
||||
};
|
||||
|
||||
for(int i = 0; i < bonDescs.size(); i++)
|
||||
{
|
||||
std::string picName = bonusPics[bonDescs[i].type];
|
||||
size_t picNumber = bonDescs[i].info2;
|
||||
|
||||
std::string desc;
|
||||
switch(bonDescs[i].type)
|
||||
{
|
||||
case CScenarioTravel::STravelBonus::SPELL:
|
||||
desc = CGI->generaltexth->allTexts[715];
|
||||
boost::algorithm::replace_first(desc, "%s", CGI->spellh->objects[bonDescs[i].info2]->name);
|
||||
break;
|
||||
case CScenarioTravel::STravelBonus::MONSTER:
|
||||
picNumber = bonDescs[i].info2 + 2;
|
||||
desc = CGI->generaltexth->allTexts[717];
|
||||
boost::algorithm::replace_first(desc, "%d", boost::lexical_cast<std::string>(bonDescs[i].info3));
|
||||
boost::algorithm::replace_first(desc, "%s", CGI->creh->creatures[bonDescs[i].info2]->namePl);
|
||||
break;
|
||||
case CScenarioTravel::STravelBonus::BUILDING:
|
||||
{
|
||||
int faction = -1;
|
||||
for(auto & elem : CSH->si->playerInfos)
|
||||
{
|
||||
if(elem.second.isControlledByHuman())
|
||||
{
|
||||
faction = elem.second.castle;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
assert(faction != -1);
|
||||
|
||||
BuildingID buildID = CBuildingHandler::campToERMU(bonDescs[i].info1, faction, std::set<BuildingID>());
|
||||
picName = graphics->ERMUtoPicture[faction][buildID];
|
||||
picNumber = -1;
|
||||
|
||||
if(vstd::contains(CGI->townh->factions[faction]->town->buildings, buildID))
|
||||
desc = CGI->townh->factions[faction]->town->buildings.find(buildID)->second->Name();
|
||||
|
||||
break;
|
||||
}
|
||||
case CScenarioTravel::STravelBonus::ARTIFACT:
|
||||
desc = CGI->generaltexth->allTexts[715];
|
||||
boost::algorithm::replace_first(desc, "%s", CGI->arth->artifacts[bonDescs[i].info2]->Name());
|
||||
break;
|
||||
case CScenarioTravel::STravelBonus::SPELL_SCROLL:
|
||||
desc = CGI->generaltexth->allTexts[716];
|
||||
boost::algorithm::replace_first(desc, "%s", CGI->spellh->objects[bonDescs[i].info2]->name);
|
||||
break;
|
||||
case CScenarioTravel::STravelBonus::PRIMARY_SKILL:
|
||||
{
|
||||
int leadingSkill = -1;
|
||||
std::vector<std::pair<int, int>> toPrint; //primary skills to be listed <num, val>
|
||||
const ui8 * ptr = reinterpret_cast<const ui8 *>(&bonDescs[i].info2);
|
||||
for(int g = 0; g < GameConstants::PRIMARY_SKILLS; ++g)
|
||||
{
|
||||
if(leadingSkill == -1 || ptr[g] > ptr[leadingSkill])
|
||||
{
|
||||
leadingSkill = g;
|
||||
}
|
||||
if(ptr[g] != 0)
|
||||
{
|
||||
toPrint.push_back(std::make_pair(g, ptr[g]));
|
||||
}
|
||||
}
|
||||
picNumber = leadingSkill;
|
||||
desc = CGI->generaltexth->allTexts[715];
|
||||
|
||||
std::string substitute; //text to be printed instead of %s
|
||||
for(int v = 0; v < toPrint.size(); ++v)
|
||||
{
|
||||
substitute += boost::lexical_cast<std::string>(toPrint[v].second);
|
||||
substitute += " " + CGI->generaltexth->primarySkillNames[toPrint[v].first];
|
||||
if(v != toPrint.size() - 1)
|
||||
{
|
||||
substitute += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
boost::algorithm::replace_first(desc, "%s", substitute);
|
||||
break;
|
||||
}
|
||||
case CScenarioTravel::STravelBonus::SECONDARY_SKILL:
|
||||
desc = CGI->generaltexth->allTexts[718];
|
||||
|
||||
boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->levels[bonDescs[i].info3 - 1]); //skill level
|
||||
boost::algorithm::replace_first(desc, "%s", CGI->skillh->skillName(bonDescs[i].info2)); //skill name
|
||||
picNumber = bonDescs[i].info2 * 3 + bonDescs[i].info3 - 1;
|
||||
|
||||
break;
|
||||
case CScenarioTravel::STravelBonus::RESOURCE:
|
||||
{
|
||||
int serialResID = 0;
|
||||
switch(bonDescs[i].info1)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
serialResID = bonDescs[i].info1;
|
||||
break;
|
||||
case 0xFD: //wood + ore
|
||||
serialResID = 7;
|
||||
break;
|
||||
case 0xFE: //rare resources
|
||||
serialResID = 8;
|
||||
break;
|
||||
}
|
||||
picNumber = serialResID;
|
||||
|
||||
desc = CGI->generaltexth->allTexts[717];
|
||||
boost::algorithm::replace_first(desc, "%d", boost::lexical_cast<std::string>(bonDescs[i].info2));
|
||||
std::string replacement;
|
||||
if(serialResID <= 6)
|
||||
{
|
||||
replacement = CGI->generaltexth->restypes[serialResID];
|
||||
}
|
||||
else
|
||||
{
|
||||
replacement = CGI->generaltexth->allTexts[714 + serialResID];
|
||||
}
|
||||
boost::algorithm::replace_first(desc, "%s", replacement);
|
||||
break;
|
||||
}
|
||||
case CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO:
|
||||
{
|
||||
auto superhero = getCampaign()->camp->scenarios[bonDescs[i].info2].strongestHero(PlayerColor(bonDescs[i].info1));
|
||||
if(!superhero)
|
||||
logGlobal->warn("No superhero! How could it be transferred?");
|
||||
picNumber = superhero ? superhero->portrait : 0;
|
||||
desc = CGI->generaltexth->allTexts[719];
|
||||
|
||||
boost::algorithm::replace_first(desc, "%s", getCampaign()->camp->scenarios[bonDescs[i].info2].scenarioName);
|
||||
break;
|
||||
}
|
||||
|
||||
case CScenarioTravel::STravelBonus::HERO:
|
||||
|
||||
desc = CGI->generaltexth->allTexts[718];
|
||||
boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->capColors[bonDescs[i].info1]); //hero's color
|
||||
|
||||
if(bonDescs[i].info2 == 0xFFFF)
|
||||
{
|
||||
boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->allTexts[101]); //hero's name
|
||||
picNumber = -1;
|
||||
picName = "CBONN1A3.BMP";
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::algorithm::replace_first(desc, "%s", CGI->heroh->heroes[bonDescs[i].info2]->name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
CToggleButton * bonusButton = new CToggleButton(Point(475 + i * 68, 455), "", CButton::tooltip(desc, desc));
|
||||
|
||||
if(picNumber != -1)
|
||||
picName += ":" + boost::lexical_cast<std::string>(picNumber);
|
||||
|
||||
auto anim = std::make_shared<CAnimation>();
|
||||
anim->setCustom(picName, 0);
|
||||
bonusButton->setImage(anim);
|
||||
if(CSH->campaignBonus == i)
|
||||
bonusButton->setBorderColor(Colors::BRIGHT_YELLOW);
|
||||
groupBonuses->addToggle(i, bonusButton);
|
||||
}
|
||||
|
||||
if(vstd::contains(getCampaign()->chosenCampaignBonuses, CSH->campaignMap))
|
||||
{
|
||||
groupBonuses->setSelected(getCampaign()->chosenCampaignBonuses[CSH->campaignMap]);
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSelection::updateAfterStateChange()
|
||||
{
|
||||
if(CSH->state != EClientState::GAMEPLAY)
|
||||
{
|
||||
buttonRestart->disable();
|
||||
buttonStart->enable();
|
||||
if(!getCampaign()->mapsConquered.empty())
|
||||
buttonBack->block(true);
|
||||
else
|
||||
buttonBack->block(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonStart->disable();
|
||||
buttonRestart->enable();
|
||||
buttonBack->block(false);
|
||||
}
|
||||
if(CSH->campaignBonus == -1)
|
||||
{
|
||||
buttonStart->block(getCampaign()->camp->scenarios[CSH->campaignMap].travelOptions.bonusesToChoose.size());
|
||||
}
|
||||
else if(buttonStart->isBlocked())
|
||||
{
|
||||
buttonStart->block(false);
|
||||
}
|
||||
|
||||
for(auto region : regions)
|
||||
region->updateState();
|
||||
|
||||
if(!CSH->mi)
|
||||
return;
|
||||
iconsMapSizes->setFrame(CSH->mi->getMapSizeIconId());
|
||||
mapDescription->setText(CSH->mi->getDescription());
|
||||
for(size_t i = 0; i < difficultyIcons.size(); i++)
|
||||
{
|
||||
if(i == CSH->si->difficulty)
|
||||
difficultyIcons[i]->enable();
|
||||
else
|
||||
difficultyIcons[i]->disable();
|
||||
}
|
||||
flagbox->recreate();
|
||||
createBonusesIcons();
|
||||
}
|
||||
|
||||
void CBonusSelection::goBack()
|
||||
{
|
||||
if(CSH->state != EClientState::GAMEPLAY)
|
||||
{
|
||||
GH.popInts(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
GH.popIntTotally(this);
|
||||
}
|
||||
// TODO: we can actually only pop bonus selection interface for custom campaigns
|
||||
// Though this would require clearing CLobbyScreen::bonusSel pointer when poping this interface
|
||||
/*
|
||||
else
|
||||
{
|
||||
GH.popIntTotally(this);
|
||||
CSH->state = EClientState::LOBBY;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void CBonusSelection::startMap()
|
||||
{
|
||||
auto showPrologVideo = [=]()
|
||||
{
|
||||
auto exitCb = [=]()
|
||||
{
|
||||
logGlobal->info("Starting scenario %d", CSH->campaignMap);
|
||||
CSH->sendStartGame();
|
||||
};
|
||||
|
||||
const CCampaignScenario & scenario = getCampaign()->camp->scenarios[CSH->campaignMap];
|
||||
if(scenario.prolog.hasPrologEpilog)
|
||||
{
|
||||
GH.pushInt(new CPrologEpilogVideo(scenario.prolog, exitCb));
|
||||
}
|
||||
else
|
||||
{
|
||||
exitCb();
|
||||
}
|
||||
};
|
||||
|
||||
if(LOCPLINT) // we're currently ingame, so ask for starting new map and end game
|
||||
{
|
||||
GH.popInt(this);
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [=]()
|
||||
{
|
||||
GH.curInt = CMainMenu::create();
|
||||
showPrologVideo();
|
||||
}, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
showPrologVideo();
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSelection::restartMap()
|
||||
{
|
||||
GH.popInt(this);
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [=]()
|
||||
{
|
||||
CSH->startCampaignScenario();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
void CBonusSelection::increaseDifficulty()
|
||||
{
|
||||
CSH->setDifficulty(CSH->si->difficulty + 1);
|
||||
}
|
||||
|
||||
void CBonusSelection::decreaseDifficulty()
|
||||
{
|
||||
CSH->setDifficulty(CSH->si->difficulty - 1);
|
||||
}
|
||||
|
||||
CBonusSelection::CRegion::CRegion(int id, bool accessible, bool selectable, const SCampPositions & campDsc)
|
||||
: CIntObject(LCLICK | RCLICK), idOfMapAndRegion(id), accessible(accessible), selectable(selectable)
|
||||
{
|
||||
OBJ_CONSTRUCTION;
|
||||
static const std::string colors[2][8] =
|
||||
{
|
||||
{"R", "B", "N", "G", "O", "V", "T", "P"},
|
||||
{"Re", "Bl", "Br", "Gr", "Or", "Vi", "Te", "Pi"}
|
||||
};
|
||||
|
||||
const SCampPositions::SRegionDesc & desc = campDsc.regions[idOfMapAndRegion];
|
||||
pos.x += desc.xpos;
|
||||
pos.y += desc.ypos;
|
||||
|
||||
std::string prefix = campDsc.campPrefix + desc.infix + "_";
|
||||
std::string suffix = colors[campDsc.colorSuffixLength - 1][CSH->si->campState->camp->scenarios[idOfMapAndRegion].regionColor];
|
||||
graphicsNotSelected = std::make_shared<CPicture>(prefix + "En" + suffix + ".BMP");
|
||||
graphicsNotSelected->disable();
|
||||
graphicsSelected = std::make_shared<CPicture>(prefix + "Se" + suffix + ".BMP");
|
||||
graphicsSelected->disable();
|
||||
graphicsStriped = std::make_shared<CPicture>(prefix + "Co" + suffix + ".BMP");
|
||||
graphicsStriped->disable();
|
||||
pos.w = graphicsNotSelected->bg->w;
|
||||
pos.h = graphicsNotSelected->bg->h;
|
||||
|
||||
}
|
||||
|
||||
void CBonusSelection::CRegion::updateState()
|
||||
{
|
||||
if(!accessible)
|
||||
{
|
||||
graphicsNotSelected->disable();
|
||||
graphicsSelected->disable();
|
||||
graphicsStriped->enable();
|
||||
}
|
||||
else if(CSH->campaignMap == idOfMapAndRegion)
|
||||
{
|
||||
graphicsNotSelected->disable();
|
||||
graphicsSelected->enable();
|
||||
graphicsStriped->disable();
|
||||
}
|
||||
else
|
||||
{
|
||||
graphicsNotSelected->enable();
|
||||
graphicsSelected->disable();
|
||||
graphicsStriped->disable();
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSelection::CRegion::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
//select if selectable & clicked inside our graphic
|
||||
if(indeterminate(down))
|
||||
return;
|
||||
|
||||
if(!down && selectable && !CSDL_Ext::isTransparent(*graphicsNotSelected, GH.current->motion.x - pos.x, GH.current->motion.y - pos.y))
|
||||
{
|
||||
CSH->setCampaignMap(idOfMapAndRegion);
|
||||
}
|
||||
}
|
||||
|
||||
void CBonusSelection::CRegion::clickRight(tribool down, bool previousState)
|
||||
{
|
||||
// FIXME: For some reason "down" is only ever contain indeterminate_value
|
||||
auto text = CSH->si->campState->camp->scenarios[idOfMapAndRegion].regionText;
|
||||
if(!CSDL_Ext::isTransparent(*graphicsNotSelected, GH.current->motion.x - pos.x, GH.current->motion.y - pos.y) && text.size())
|
||||
{
|
||||
CRClickPopup::createAndPush(text);
|
||||
}
|
||||
}
|
93
client/lobby/CBonusSelection.h
Normal file
93
client/lobby/CBonusSelection.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* CBonusSelection.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include "../windows/CWindowObject.h"
|
||||
|
||||
class SDL_Surface;
|
||||
class CCampaignState;
|
||||
class CButton;
|
||||
class CTextBox;
|
||||
class CToggleGroup;
|
||||
class CAnimImage;
|
||||
class CLabel;
|
||||
class CFlagBox;
|
||||
|
||||
/// Campaign screen where you can choose one out of three starting bonuses
|
||||
class CBonusSelection : public CWindowObject
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<CCampaignState> getCampaign();
|
||||
CBonusSelection();
|
||||
|
||||
struct SCampPositions
|
||||
{
|
||||
std::string campPrefix;
|
||||
int colorSuffixLength;
|
||||
|
||||
struct SRegionDesc
|
||||
{
|
||||
std::string infix;
|
||||
int xpos, ypos;
|
||||
};
|
||||
|
||||
std::vector<SRegionDesc> regions;
|
||||
|
||||
};
|
||||
|
||||
class CRegion
|
||||
: public CIntObject
|
||||
{
|
||||
CBonusSelection * owner;
|
||||
std::shared_ptr<CPicture> graphicsNotSelected;
|
||||
std::shared_ptr<CPicture> graphicsSelected;
|
||||
std::shared_ptr<CPicture> graphicsStriped;
|
||||
int idOfMapAndRegion;
|
||||
bool accessible; // false if region should be striped
|
||||
bool selectable; // true if region should be selectable
|
||||
public:
|
||||
CRegion(int id, bool accessible, bool selectable, const SCampPositions & campDsc);
|
||||
void updateState();
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void clickRight(tribool down, bool previousState) override;
|
||||
};
|
||||
|
||||
void loadPositionsOfGraphics();
|
||||
void createBonusesIcons();
|
||||
void updateAfterStateChange();
|
||||
|
||||
// Event handlers
|
||||
void goBack();
|
||||
void startMap();
|
||||
void restartMap();
|
||||
void increaseDifficulty();
|
||||
void decreaseDifficulty();
|
||||
|
||||
std::shared_ptr<CPicture> panelBackground;
|
||||
std::shared_ptr<CButton> buttonStart;
|
||||
std::shared_ptr<CButton> buttonRestart;
|
||||
std::shared_ptr<CButton> buttonBack;
|
||||
std::shared_ptr<CLabel> campaignName;
|
||||
std::shared_ptr<CLabel> labelCampaignDescription;
|
||||
std::shared_ptr<CTextBox> campaignDescription;
|
||||
std::shared_ptr<CLabel> mapName;
|
||||
std::shared_ptr<CLabel> labelMapDescription;
|
||||
std::shared_ptr<CTextBox> mapDescription;
|
||||
std::vector<SCampPositions> campDescriptions;
|
||||
std::vector<std::shared_ptr<CRegion>> regions;
|
||||
std::shared_ptr<CFlagBox> flagbox;
|
||||
|
||||
std::shared_ptr<CLabel> labelChooseBonus;
|
||||
std::shared_ptr<CToggleGroup> groupBonuses;
|
||||
std::shared_ptr<CLabel> labelDifficulty;
|
||||
std::array<std::shared_ptr<CAnimImage>, 5> difficultyIcons;
|
||||
std::shared_ptr<CButton> buttonDifficultyLeft;
|
||||
std::shared_ptr<CButton> buttonDifficultyRight;
|
||||
std::shared_ptr<CAnimImage> iconsMapSizes;
|
||||
};
|
201
client/lobby/CLobbyScreen.cpp
Normal file
201
client/lobby/CLobbyScreen.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* CLobbyScreen.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CLobbyScreen.h"
|
||||
#include "CBonusSelection.h"
|
||||
#include "SelectionTab.h"
|
||||
#include "RandomMapTab.h"
|
||||
#include "OptionsTab.h"
|
||||
#include "../CServerHandler.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../../lib/NetPacksLobby.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/mapping/CMapInfo.h"
|
||||
#include "../../lib/mapping/CCampaignHandler.h"
|
||||
#include "../../lib/rmg/CMapGenOptions.h"
|
||||
|
||||
CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)
|
||||
: CSelectionBase(screenType), bonusSel(nullptr)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
tabSel = std::make_shared<SelectionTab>(screenType);
|
||||
curTab = tabSel;
|
||||
|
||||
auto initLobby = [&]()
|
||||
{
|
||||
tabSel->callOnSelect = std::bind(&IServerAPI::setMapInfo, CSH, _1, nullptr);
|
||||
|
||||
buttonSelect = std::make_shared<CButton>(Point(411, 80), "GSPBUTT.DEF", CGI->generaltexth->zelp[45], 0, SDLK_s);
|
||||
buttonSelect->addCallback([&]()
|
||||
{
|
||||
toggleTab(tabSel);
|
||||
CSH->setMapInfo(tabSel->getSelectedMapInfo());
|
||||
});
|
||||
|
||||
buttonOptions = std::make_shared<CButton>(Point(411, 510), "GSPBUTT.DEF", CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabOpt), SDLK_a);
|
||||
};
|
||||
|
||||
buttonChat = std::make_shared<CButton>(Point(619, 83), "GSPBUT2.DEF", CGI->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), SDLK_h);
|
||||
buttonChat->addTextOverlay(CGI->generaltexth->allTexts[532], FONT_SMALL);
|
||||
|
||||
switch(screenType)
|
||||
{
|
||||
case ESelectionScreen::newGame:
|
||||
{
|
||||
tabOpt = std::make_shared<OptionsTab>();
|
||||
tabRand = std::make_shared<RandomMapTab>();
|
||||
tabRand->mapInfoChanged += std::bind(&IServerAPI::setMapInfo, CSH, _1, _2);
|
||||
buttonRMG = std::make_shared<CButton>(Point(411, 105), "GSPBUTT.DEF", CGI->generaltexth->zelp[47], 0, SDLK_r);
|
||||
buttonRMG->addCallback([&]()
|
||||
{
|
||||
toggleTab(tabRand);
|
||||
tabRand->updateMapInfoByHost(); // TODO: This is only needed to force-update mapInfo in CSH when tab is opened
|
||||
});
|
||||
|
||||
card->iconDifficulty->addCallback(std::bind(&IServerAPI::setDifficulty, CSH, _1));
|
||||
|
||||
buttonStart = std::make_shared<CButton>(Point(411, 535), "SCNRBEG.DEF", CGI->generaltexth->zelp[103], std::bind(&CLobbyScreen::startScenario, this, false), SDLK_b);
|
||||
initLobby();
|
||||
break;
|
||||
}
|
||||
case ESelectionScreen::loadGame:
|
||||
{
|
||||
tabOpt = std::make_shared<OptionsTab>();
|
||||
buttonStart = std::make_shared<CButton>(Point(411, 535), "SCNRLOD.DEF", CGI->generaltexth->zelp[103], std::bind(&CLobbyScreen::startScenario, this, false), SDLK_l);
|
||||
initLobby();
|
||||
break;
|
||||
}
|
||||
case ESelectionScreen::campaignList:
|
||||
tabSel->callOnSelect = std::bind(&IServerAPI::setMapInfo, CSH, _1, nullptr);
|
||||
buttonStart = std::make_shared<CButton>(Point(411, 535), "SCNRLOD.DEF", CButton::tooltip(), std::bind(&CLobbyScreen::startCampaign, this), SDLK_b);
|
||||
break;
|
||||
}
|
||||
|
||||
buttonStart->assignedKeys.insert(SDLK_RETURN);
|
||||
|
||||
buttonBack = std::make_shared<CButton>(Point(581, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], [&](){CSH->sendClientDisconnecting(); GH.popIntTotally(this);}, SDLK_ESCAPE);
|
||||
}
|
||||
|
||||
CLobbyScreen::~CLobbyScreen()
|
||||
{
|
||||
// TODO: For now we always destroy whole lobby when leaving bonus selection screen
|
||||
if(CSH->state == EClientState::LOBBY_CAMPAIGN)
|
||||
CSH->sendClientDisconnecting();
|
||||
}
|
||||
|
||||
void CLobbyScreen::toggleTab(std::shared_ptr<CIntObject> tab)
|
||||
{
|
||||
if(tab == curTab)
|
||||
CSH->sendGuiAction(LobbyGuiAction::NO_TAB);
|
||||
else if(tab == tabOpt)
|
||||
CSH->sendGuiAction(LobbyGuiAction::OPEN_OPTIONS);
|
||||
else if(tab == tabSel)
|
||||
CSH->sendGuiAction(LobbyGuiAction::OPEN_SCENARIO_LIST);
|
||||
else if(tab == tabRand)
|
||||
CSH->sendGuiAction(LobbyGuiAction::OPEN_RANDOM_MAP_OPTIONS);
|
||||
CSelectionBase::toggleTab(tab);
|
||||
}
|
||||
|
||||
void CLobbyScreen::startCampaign()
|
||||
{
|
||||
if(CSH->mi)
|
||||
{
|
||||
auto ourCampaign = std::make_shared<CCampaignState>(CCampaignHandler::getCampaign(CSH->mi->fileURI));
|
||||
CSH->setCampaignState(ourCampaign);
|
||||
}
|
||||
}
|
||||
|
||||
void CLobbyScreen::startScenario(bool allowOnlyAI)
|
||||
{
|
||||
try
|
||||
{
|
||||
CSH->sendStartGame(allowOnlyAI);
|
||||
buttonStart->block(true);
|
||||
}
|
||||
catch(ExceptionMapMissing & e)
|
||||
{
|
||||
|
||||
}
|
||||
catch(ExceptionNoHuman & e)
|
||||
{
|
||||
// You must position yourself prior to starting the game.
|
||||
CInfoWindow::showYesNoDialog(std::ref(CGI->generaltexth->allTexts[530]), nullptr, 0, std::bind(&CLobbyScreen::startScenario, this, true), false, PlayerColor(1));
|
||||
}
|
||||
catch(ExceptionNoTemplate & e)
|
||||
{
|
||||
GH.pushInt(CInfoWindow::create(CGI->generaltexth->allTexts[751]));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void CLobbyScreen::toggleMode(bool host)
|
||||
{
|
||||
tabSel->toggleMode();
|
||||
buttonStart->block(!host);
|
||||
if(screenType == ESelectionScreen::campaignList)
|
||||
return;
|
||||
|
||||
auto buttonColor = host ? Colors::WHITE : Colors::ORANGE;
|
||||
buttonSelect->addTextOverlay(CGI->generaltexth->allTexts[500], FONT_SMALL, buttonColor);
|
||||
buttonOptions->addTextOverlay(CGI->generaltexth->allTexts[501], FONT_SMALL, buttonColor);
|
||||
if(buttonRMG)
|
||||
{
|
||||
buttonRMG->addTextOverlay(CGI->generaltexth->allTexts[740], FONT_SMALL, buttonColor);
|
||||
buttonRMG->block(!host);
|
||||
}
|
||||
buttonSelect->block(!host);
|
||||
buttonOptions->block(!host);
|
||||
|
||||
if(CSH->mi)
|
||||
tabOpt->recreate();
|
||||
}
|
||||
|
||||
void CLobbyScreen::toggleChat()
|
||||
{
|
||||
card->toggleChat();
|
||||
if(card->showChat)
|
||||
buttonChat->addTextOverlay(CGI->generaltexth->allTexts[531], FONT_SMALL);
|
||||
else
|
||||
buttonChat->addTextOverlay(CGI->generaltexth->allTexts[532], FONT_SMALL);
|
||||
}
|
||||
|
||||
void CLobbyScreen::updateAfterStateChange()
|
||||
{
|
||||
if(CSH->mi && tabOpt)
|
||||
tabOpt->recreate();
|
||||
|
||||
card->changeSelection();
|
||||
if(card->iconDifficulty)
|
||||
card->iconDifficulty->setSelected(CSH->si->difficulty);
|
||||
|
||||
if(curTab == tabRand && CSH->si->mapGenOptions)
|
||||
tabRand->setMapGenOptions(CSH->si->mapGenOptions);
|
||||
}
|
||||
|
||||
const StartInfo * CLobbyScreen::getStartInfo()
|
||||
{
|
||||
return CSH->si.get();
|
||||
}
|
||||
|
||||
const CMapInfo * CLobbyScreen::getMapInfo()
|
||||
{
|
||||
return CSH->mi.get();
|
||||
}
|
35
client/lobby/CLobbyScreen.h
Normal file
35
client/lobby/CLobbyScreen.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* CLobbyScreen.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CSelectionBase.h"
|
||||
|
||||
class CBonusSelection;
|
||||
|
||||
class CLobbyScreen : public CSelectionBase
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<CButton> buttonChat;
|
||||
|
||||
CLobbyScreen(ESelectionScreen type);
|
||||
~CLobbyScreen();
|
||||
void toggleTab(std::shared_ptr<CIntObject> tab) override;
|
||||
void startCampaign();
|
||||
void startScenario(bool allowOnlyAI = false);
|
||||
void toggleMode(bool host);
|
||||
void toggleChat();
|
||||
|
||||
void updateAfterStateChange();
|
||||
|
||||
const CMapInfo * getMapInfo() override;
|
||||
const StartInfo * getStartInfo() override;
|
||||
|
||||
CBonusSelection * bonusSel;
|
||||
};
|
97
client/lobby/CSavingScreen.cpp
Normal file
97
client/lobby/CSavingScreen.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* CSavingScreen.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CSavingScreen.h"
|
||||
#include "SelectionTab.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/StartInfo.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/mapping/CMapInfo.h"
|
||||
|
||||
CSavingScreen::CSavingScreen()
|
||||
: CSelectionBase(ESelectionScreen::saveGame)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
center(pos);
|
||||
// TODO: we should really use std::shared_ptr for passing StartInfo around.
|
||||
localSi = new StartInfo(*LOCPLINT->cb->getStartInfo());
|
||||
localMi = new CMapInfo();
|
||||
localMi->mapHeader = std::unique_ptr<CMapHeader>(new CMapHeader(*LOCPLINT->cb->getMapHeader()));
|
||||
|
||||
tabSel = std::make_shared<SelectionTab>(screenType);
|
||||
curTab = tabSel;
|
||||
tabSel->toggleMode();
|
||||
|
||||
tabSel->callOnSelect = std::bind(&CSavingScreen::changeSelection, this, _1);
|
||||
buttonStart = std::make_shared<CButton>(Point(411, 535), "SCNRSAV.DEF", CGI->generaltexth->zelp[103], std::bind(&CSavingScreen::saveGame, this), SDLK_s);
|
||||
buttonStart->assignedKeys.insert(SDLK_RETURN);
|
||||
}
|
||||
|
||||
CSavingScreen::~CSavingScreen()
|
||||
{
|
||||
vstd::clear_pointer(localMi);
|
||||
}
|
||||
|
||||
const CMapInfo * CSavingScreen::getMapInfo()
|
||||
{
|
||||
return localMi;
|
||||
}
|
||||
|
||||
const StartInfo * CSavingScreen::getStartInfo()
|
||||
{
|
||||
return localSi;
|
||||
}
|
||||
|
||||
void CSavingScreen::changeSelection(std::shared_ptr<CMapInfo> to)
|
||||
{
|
||||
if(localMi == to.get())
|
||||
return;
|
||||
|
||||
localMi = to.get();
|
||||
localSi = localMi->scenarioOptionsOfSave;
|
||||
card->changeSelection();
|
||||
}
|
||||
|
||||
void CSavingScreen::saveGame()
|
||||
{
|
||||
if(!(tabSel && tabSel->inputName && tabSel->inputName->text.size()))
|
||||
return;
|
||||
|
||||
std::string path = "Saves/" + tabSel->inputName->text;
|
||||
|
||||
auto overWrite = [&]() -> void
|
||||
{
|
||||
Settings lastSave = settings.write["general"]["lastSave"];
|
||||
lastSave->String() = path;
|
||||
LOCPLINT->cb->save(path);
|
||||
GH.popIntTotally(this);
|
||||
};
|
||||
|
||||
if(CResourceHandler::get("local")->existsResource(ResourceID(path, EResType::CLIENT_SAVEGAME)))
|
||||
{
|
||||
std::string hlp = CGI->generaltexth->allTexts[493]; //%s exists. Overwrite?
|
||||
boost::algorithm::replace_first(hlp, "%s", tabSel->inputName->text);
|
||||
LOCPLINT->showYesNoDialog(hlp, overWrite, 0, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
overWrite();
|
||||
}
|
||||
}
|
32
client/lobby/CSavingScreen.h
Normal file
32
client/lobby/CSavingScreen.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* CSavingScreen.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CSelectionBase.h"
|
||||
|
||||
class CSelectionBase;
|
||||
struct StartInfo;
|
||||
class CMapInfo;
|
||||
|
||||
class CSavingScreen : public CSelectionBase
|
||||
{
|
||||
public:
|
||||
const StartInfo * localSi;
|
||||
CMapInfo * localMi;
|
||||
|
||||
CSavingScreen();
|
||||
~CSavingScreen();
|
||||
|
||||
void changeSelection(std::shared_ptr<CMapInfo> to);
|
||||
void saveGame();
|
||||
|
||||
const CMapInfo * getMapInfo() override;
|
||||
const StartInfo * getStartInfo() override;
|
||||
};
|
59
client/lobby/CScenarioInfoScreen.cpp
Normal file
59
client/lobby/CScenarioInfoScreen.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* CScenarioInfoScreen.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CScenarioInfoScreen.h"
|
||||
#include "OptionsTab.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/StartInfo.h"
|
||||
#include "../../lib/mapping/CMapInfo.h"
|
||||
|
||||
CScenarioInfoScreen::CScenarioInfoScreen()
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
localSi = new StartInfo(*LOCPLINT->cb->getStartInfo());
|
||||
localMi = new CMapInfo();
|
||||
localMi->mapHeader = std::unique_ptr<CMapHeader>(new CMapHeader(*LOCPLINT->cb->getMapHeader()));
|
||||
|
||||
screenType = ESelectionScreen::scenarioInfo;
|
||||
|
||||
card = std::make_shared<InfoCard>();
|
||||
opt = std::make_shared<OptionsTab>();
|
||||
opt->recActions = UPDATE | SHOWALL;
|
||||
opt->recreate();
|
||||
card->changeSelection();
|
||||
|
||||
card->iconDifficulty->setSelected(getCurrentDifficulty());
|
||||
buttonBack = std::make_shared<CButton>(Point(584, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], std::bind(&CGuiHandler::popIntTotally, &GH, this), SDLK_ESCAPE);
|
||||
}
|
||||
|
||||
CScenarioInfoScreen::~CScenarioInfoScreen()
|
||||
{
|
||||
vstd::clear_pointer(localSi);
|
||||
vstd::clear_pointer(localMi);
|
||||
}
|
||||
|
||||
const CMapInfo * CScenarioInfoScreen::getMapInfo()
|
||||
{
|
||||
return localMi;
|
||||
}
|
||||
|
||||
const StartInfo * CScenarioInfoScreen::getStartInfo()
|
||||
{
|
||||
return localSi;
|
||||
}
|
30
client/lobby/CScenarioInfoScreen.h
Normal file
30
client/lobby/CScenarioInfoScreen.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* CScenarioInfoScreen.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CSelectionBase.h"
|
||||
|
||||
/// Scenario information screen shown during the game
|
||||
class CScenarioInfoScreen : public CIntObject, public ISelectionScreenInfo
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<CButton> buttonBack;
|
||||
std::shared_ptr<InfoCard> card;
|
||||
std::shared_ptr<OptionsTab> opt;
|
||||
|
||||
const StartInfo * localSi;
|
||||
CMapInfo * localMi;
|
||||
|
||||
CScenarioInfoScreen();
|
||||
~CScenarioInfoScreen();
|
||||
|
||||
const CMapInfo * getMapInfo() override;
|
||||
const StartInfo * getStartInfo() override;
|
||||
};
|
407
client/lobby/CSelectionBase.cpp
Normal file
407
client/lobby/CSelectionBase.cpp
Normal file
@ -0,0 +1,407 @@
|
||||
/*
|
||||
* CSelectionBase.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CSelectionBase.h"
|
||||
#include "CBonusSelection.h"
|
||||
#include "CLobbyScreen.h"
|
||||
#include "OptionsTab.h"
|
||||
#include "RandomMapTab.h"
|
||||
#include "SelectionTab.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CMessage.h"
|
||||
#include "../CBitmapHandler.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CVideoHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../windows/GUIClasses.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
|
||||
#include "../../lib/NetPacksLobby.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/CThreadHelper.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/mapping/CMapInfo.h"
|
||||
#include "../../lib/serializer/Connection.h"
|
||||
|
||||
ISelectionScreenInfo::ISelectionScreenInfo(ESelectionScreen ScreenType)
|
||||
: screenType(ScreenType)
|
||||
{
|
||||
assert(!SEL);
|
||||
SEL = this;
|
||||
}
|
||||
|
||||
ISelectionScreenInfo::~ISelectionScreenInfo()
|
||||
{
|
||||
assert(SEL == this);
|
||||
SEL = nullptr;
|
||||
}
|
||||
|
||||
int ISelectionScreenInfo::getCurrentDifficulty()
|
||||
{
|
||||
return getStartInfo()->difficulty;
|
||||
}
|
||||
|
||||
PlayerInfo ISelectionScreenInfo::getPlayerInfo(int color)
|
||||
{
|
||||
return getMapInfo()->mapHeader->players[color];
|
||||
}
|
||||
|
||||
CSelectionBase::CSelectionBase(ESelectionScreen type)
|
||||
: CWindowObject(BORDERED | SHADOW_DISABLED), ISelectionScreenInfo(type)
|
||||
{
|
||||
CMainMenu::create(); //we depend on its graphics
|
||||
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
IShowActivatable::type = BLOCK_ADV_HOTKEYS;
|
||||
pos.w = 762;
|
||||
pos.h = 584;
|
||||
if(screenType == ESelectionScreen::campaignList)
|
||||
{
|
||||
setBackground("CamCust.bmp");
|
||||
}
|
||||
else
|
||||
{
|
||||
const JsonVector & bgNames = CMainMenuConfig::get().getConfig()["game-select"].Vector();
|
||||
setBackground(RandomGeneratorUtil::nextItem(bgNames, CRandomGenerator::getDefault())->String());
|
||||
}
|
||||
pos = background->center();
|
||||
card = std::make_shared<InfoCard>();
|
||||
buttonBack = std::make_shared<CButton>(Point(581, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], std::bind(&CGuiHandler::popIntTotally, &GH, this), SDLK_ESCAPE);
|
||||
}
|
||||
|
||||
void CSelectionBase::toggleTab(std::shared_ptr<CIntObject> tab)
|
||||
{
|
||||
if(curTab && curTab->active)
|
||||
{
|
||||
curTab->deactivate();
|
||||
curTab->recActions = 0;
|
||||
}
|
||||
|
||||
if(curTab != tab)
|
||||
{
|
||||
tab->recActions = 255 - DISPOSE;
|
||||
tab->activate();
|
||||
curTab = tab;
|
||||
}
|
||||
else
|
||||
{
|
||||
curTab.reset();
|
||||
}
|
||||
GH.totalRedraw();
|
||||
}
|
||||
|
||||
InfoCard::InfoCard()
|
||||
: showChat(true)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
CIntObject::type |= REDRAW_PARENT;
|
||||
pos.x += 393;
|
||||
pos.y += 6;
|
||||
|
||||
labelSaveDate = std::make_shared<CLabel>(158, 19, FONT_SMALL, TOPLEFT, Colors::WHITE);
|
||||
mapName = std::make_shared<CLabel>(26, 39, FONT_BIG, TOPLEFT, Colors::YELLOW);
|
||||
Rect descriptionRect(26, 149, 320, 115);
|
||||
mapDescription = std::make_shared<CTextBox>("", descriptionRect, 1);
|
||||
playerListBg = std::make_shared<CPicture>("CHATPLUG.bmp", 16, 276);
|
||||
chat = std::make_shared<CChatBox>(Rect(26, 132, 340, 132));
|
||||
|
||||
if(SEL->screenType == ESelectionScreen::campaignList)
|
||||
{
|
||||
labelCampaignDescription = std::make_shared<CLabel>(26, 132, FONT_SMALL, TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[38]);
|
||||
}
|
||||
else
|
||||
{
|
||||
background = std::make_shared<CPicture>("GSELPOP1.bmp", 0, 0);
|
||||
parent->addChild(background.get());
|
||||
auto it = vstd::find(parent->children, this); //our position among parent children
|
||||
parent->children.insert(it, background.get()); //put BG before us
|
||||
parent->children.pop_back();
|
||||
pos.w = background->pos.w;
|
||||
pos.h = background->pos.h;
|
||||
iconsMapSizes = std::make_shared<CAnimImage>("SCNRMPSZ", 4, 0, 318, 22); //let it be custom size (frame 4) by default
|
||||
|
||||
iconDifficulty = std::make_shared<CToggleGroup>(0);
|
||||
{
|
||||
static const char * difButns[] = {"GSPBUT3.DEF", "GSPBUT4.DEF", "GSPBUT5.DEF", "GSPBUT6.DEF", "GSPBUT7.DEF"};
|
||||
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
auto button = new CToggleButton(Point(110 + i * 32, 450), difButns[i], CGI->generaltexth->zelp[24 + i]);
|
||||
|
||||
iconDifficulty->addToggle(i, button);
|
||||
if(SEL->screenType != ESelectionScreen::newGame)
|
||||
button->block(true);
|
||||
}
|
||||
}
|
||||
|
||||
flagbox = std::make_shared<CFlagBox>(Rect(24, 400, 335, 23));
|
||||
labelMapDiff = std::make_shared<CLabel>(33, 430, FONT_SMALL, TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[494]);
|
||||
labelPlayerDifficulty = std::make_shared<CLabel>(133, 430, FONT_SMALL, TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[492] + ":");
|
||||
labelRating = std::make_shared<CLabel>(290, 430, FONT_SMALL, TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[218] + ":");
|
||||
labelScenarioName = std::make_shared<CLabel>(26, 22, FONT_SMALL, TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[495]);
|
||||
labelScenarioDescription = std::make_shared<CLabel>(26, 132, FONT_SMALL, TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[496]);
|
||||
labelVictoryCondition = std::make_shared<CLabel>(26, 283, FONT_SMALL, TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[497]);
|
||||
labelLossCondition = std::make_shared<CLabel>(26, 339, FONT_SMALL, TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[498]);
|
||||
iconsVictoryCondition = std::make_shared<CAnimImage>("SCNRVICT", 0, 0, 24, 302);
|
||||
iconsLossCondition = std::make_shared<CAnimImage>("SCNRLOSS", 0, 0, 24, 359);
|
||||
|
||||
labelVictoryConditionText = std::make_shared<CLabel>(60, 307, FONT_SMALL, TOPLEFT, Colors::WHITE);
|
||||
labelLossConditionText = std::make_shared<CLabel>(60, 366, FONT_SMALL, TOPLEFT, Colors::WHITE);
|
||||
|
||||
labelDifficulty = std::make_shared<CLabel>(62, 472, FONT_SMALL, CENTER, Colors::WHITE);
|
||||
labelDifficultyPercent = std::make_shared<CLabel>(311, 472, FONT_SMALL, CENTER, Colors::WHITE);
|
||||
|
||||
labelGroupPlayersAssigned = std::make_shared<CLabelGroup>(FONT_SMALL, TOPLEFT, Colors::WHITE);
|
||||
labelGroupPlayersUnassigned = std::make_shared<CLabelGroup>(FONT_SMALL, TOPLEFT, Colors::WHITE);
|
||||
}
|
||||
setChat(false);
|
||||
}
|
||||
|
||||
void InfoCard::changeSelection()
|
||||
{
|
||||
if(!SEL->getMapInfo())
|
||||
return;
|
||||
|
||||
labelSaveDate->setText(SEL->getMapInfo()->date);
|
||||
mapName->setText(SEL->getMapInfo()->getName());
|
||||
mapDescription->setText(SEL->getMapInfo()->getDescription());
|
||||
|
||||
mapDescription->label->scrollTextTo(0);
|
||||
if(mapDescription->slider)
|
||||
mapDescription->slider->moveToMin();
|
||||
|
||||
if(SEL->screenType == ESelectionScreen::campaignList)
|
||||
return;
|
||||
|
||||
iconsMapSizes->setFrame(SEL->getMapInfo()->getMapSizeIconId());
|
||||
const CMapHeader * header = SEL->getMapInfo()->mapHeader.get();
|
||||
iconsVictoryCondition->setFrame(header->victoryIconIndex);
|
||||
labelVictoryConditionText->setText(header->victoryMessage);
|
||||
iconsLossCondition->setFrame(header->defeatIconIndex);
|
||||
labelLossConditionText->setText(header->defeatMessage);
|
||||
flagbox->recreate();
|
||||
labelDifficulty->setText(CGI->generaltexth->arraytxt[142 + SEL->getMapInfo()->mapHeader->difficulty]);
|
||||
iconDifficulty->setSelected(SEL->getCurrentDifficulty());
|
||||
const std::array<std::string, 5> difficultyPercent = {"80%", "100%", "130%", "160%", "200%"};
|
||||
labelDifficultyPercent->setText(difficultyPercent[SEL->getCurrentDifficulty()]);
|
||||
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
// FIXME: We recreate them each time because CLabelGroup don't use smart pointers
|
||||
labelGroupPlayersAssigned = std::make_shared<CLabelGroup>(FONT_SMALL, TOPLEFT, Colors::WHITE);
|
||||
labelGroupPlayersUnassigned = std::make_shared<CLabelGroup>(FONT_SMALL, TOPLEFT, Colors::WHITE);
|
||||
if(!showChat)
|
||||
{
|
||||
labelGroupPlayersAssigned->disable();
|
||||
labelGroupPlayersUnassigned->disable();
|
||||
}
|
||||
for(auto & p : CSH->playerNames)
|
||||
{
|
||||
const auto pset = CSH->si->getPlayersSettings(p.first);
|
||||
int pid = p.first;
|
||||
if(pset)
|
||||
{
|
||||
auto name = boost::str(boost::format("%s (%d-%d %s)") % p.second.name % p.second.connection % pid % pset->color.getStr());
|
||||
labelGroupPlayersAssigned->add(24, 285 + labelGroupPlayersAssigned->currentSize()*graphics->fonts[FONT_SMALL]->getLineHeight(), name);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto name = boost::str(boost::format("%s (%d-%d)") % p.second.name % p.second.connection % pid);
|
||||
labelGroupPlayersUnassigned->add(193, 285 + labelGroupPlayersUnassigned->currentSize()*graphics->fonts[FONT_SMALL]->getLineHeight(), name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InfoCard::toggleChat()
|
||||
{
|
||||
setChat(!showChat);
|
||||
}
|
||||
|
||||
void InfoCard::setChat(bool activateChat)
|
||||
{
|
||||
if(showChat == activateChat)
|
||||
return;
|
||||
|
||||
if(activateChat)
|
||||
{
|
||||
if(SEL->screenType == ESelectionScreen::campaignList)
|
||||
{
|
||||
labelCampaignDescription->disable();
|
||||
}
|
||||
else
|
||||
{
|
||||
labelScenarioDescription->disable();
|
||||
labelVictoryCondition->disable();
|
||||
labelLossCondition->disable();
|
||||
iconsVictoryCondition->disable();
|
||||
labelVictoryConditionText->disable();
|
||||
iconsLossCondition->disable();
|
||||
labelLossConditionText->disable();
|
||||
labelGroupPlayersAssigned->enable();
|
||||
labelGroupPlayersUnassigned->enable();
|
||||
}
|
||||
mapDescription->disable();
|
||||
chat->enable();
|
||||
playerListBg->enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
mapDescription->enable();
|
||||
chat->disable();
|
||||
playerListBg->disable();
|
||||
|
||||
if(SEL->screenType == ESelectionScreen::campaignList)
|
||||
{
|
||||
labelCampaignDescription->enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
labelScenarioDescription->enable();
|
||||
labelVictoryCondition->enable();
|
||||
labelLossCondition->enable();
|
||||
iconsVictoryCondition->enable();
|
||||
iconsLossCondition->enable();
|
||||
labelVictoryConditionText->enable();
|
||||
labelLossConditionText->enable();
|
||||
labelGroupPlayersAssigned->disable();
|
||||
labelGroupPlayersUnassigned->disable();
|
||||
}
|
||||
}
|
||||
|
||||
showChat = activateChat;
|
||||
GH.totalRedraw();
|
||||
}
|
||||
|
||||
CChatBox::CChatBox(const Rect & rect)
|
||||
: CIntObject(KEYBOARD | TEXTINPUT)
|
||||
{
|
||||
OBJ_CONSTRUCTION;
|
||||
pos += rect;
|
||||
captureAllKeys = true;
|
||||
type |= REDRAW_PARENT;
|
||||
|
||||
const int height = graphics->fonts[FONT_SMALL]->getLineHeight();
|
||||
inputBox = std::make_shared<CTextInput>(Rect(0, rect.h - height, rect.w, height));
|
||||
inputBox->removeUsedEvents(KEYBOARD);
|
||||
chatHistory = std::make_shared<CTextBox>("", Rect(0, 0, rect.w, rect.h - height), 1);
|
||||
|
||||
chatHistory->label->color = Colors::GREEN;
|
||||
}
|
||||
|
||||
void CChatBox::keyPressed(const SDL_KeyboardEvent & key)
|
||||
{
|
||||
if(key.keysym.sym == SDLK_RETURN && key.state == SDL_PRESSED && inputBox->text.size())
|
||||
{
|
||||
CSH->sendMessage(inputBox->text);
|
||||
inputBox->setText("");
|
||||
}
|
||||
else
|
||||
inputBox->keyPressed(key);
|
||||
}
|
||||
|
||||
void CChatBox::addNewMessage(const std::string & text)
|
||||
{
|
||||
CCS->soundh->playSound("CHAT");
|
||||
chatHistory->setText(chatHistory->label->text + text + "\n");
|
||||
if(chatHistory->slider)
|
||||
chatHistory->slider->moveToMax();
|
||||
}
|
||||
|
||||
CFlagBox::CFlagBox(const Rect & rect)
|
||||
: CIntObject(RCLICK)
|
||||
{
|
||||
pos += rect;
|
||||
pos.w = rect.w;
|
||||
pos.h = rect.h;
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
labelAllies = std::make_shared<CLabel>(0, 0, FONT_SMALL, EAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[390] + ":");
|
||||
labelEnemies = std::make_shared<CLabel>(133, 0, FONT_SMALL, EAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[391] + ":");
|
||||
|
||||
iconsTeamFlags = std::make_shared<CAnimation>("ITGFLAGS.DEF");
|
||||
iconsTeamFlags->preload();
|
||||
}
|
||||
|
||||
void CFlagBox::recreate()
|
||||
{
|
||||
flagsAllies.clear();
|
||||
flagsEnemies.clear();
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
const int alliesX = 5 + labelAllies->getWidth();
|
||||
const int enemiesX = 5 + 133 + labelEnemies->getWidth();
|
||||
for(auto i = CSH->si->playerInfos.cbegin(); i != CSH->si->playerInfos.cend(); i++)
|
||||
{
|
||||
auto flag = std::make_shared<CAnimImage>(iconsTeamFlags, i->first.getNum(), 0);
|
||||
if(i->first == CSH->myFirstColor() || CSH->getPlayerTeamId(i->first) == CSH->getPlayerTeamId(CSH->myFirstColor()))
|
||||
{
|
||||
flag->moveTo(Point(pos.x + alliesX + flagsAllies.size()*flag->pos.w, pos.y));
|
||||
flagsAllies.push_back(flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
flag->moveTo(Point(pos.x + enemiesX + flagsEnemies.size()*flag->pos.w, pos.y));
|
||||
flagsEnemies.push_back(flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CFlagBox::clickRight(tribool down, bool previousState)
|
||||
{
|
||||
if(down && SEL->getMapInfo())
|
||||
GH.pushInt(new CFlagBoxTooltipBox(iconsTeamFlags));
|
||||
}
|
||||
|
||||
CFlagBox::CFlagBoxTooltipBox::CFlagBoxTooltipBox(std::shared_ptr<CAnimation> icons)
|
||||
: CWindowObject(BORDERED | RCLICK_POPUP | SHADOW_DISABLED, "DIBOXBCK")
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
pos.w = 256;
|
||||
pos.h = 90 + 50 * SEL->getMapInfo()->mapHeader->howManyTeams;
|
||||
|
||||
labelTeamAlignment = std::make_shared<CLabel>(128, 30, FONT_MEDIUM, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[657]);
|
||||
labelGroupTeams = std::make_shared<CLabelGroup>(FONT_SMALL, EAlignment::CENTER, Colors::WHITE);
|
||||
for(int i = 0; i < SEL->getMapInfo()->mapHeader->howManyTeams; i++)
|
||||
{
|
||||
std::vector<ui8> flags;
|
||||
labelGroupTeams->add(128, 65 + 50 * i, boost::str(boost::format(CGI->generaltexth->allTexts[656]) % (i+1)));
|
||||
|
||||
for(int j = 0; j < PlayerColor::PLAYER_LIMIT_I; j++)
|
||||
{
|
||||
if((SEL->getPlayerInfo(j).canHumanPlay || SEL->getPlayerInfo(j).canComputerPlay)
|
||||
&& SEL->getPlayerInfo(j).team == TeamID(i))
|
||||
{
|
||||
flags.push_back(j);
|
||||
}
|
||||
}
|
||||
|
||||
int curx = 128 - 9 * flags.size();
|
||||
for(auto & flag : flags)
|
||||
{
|
||||
iconsFlags.push_back(std::make_shared<CAnimImage>(icons, flag, 0, curx, 75 + 50 * i));
|
||||
curx += 18;
|
||||
}
|
||||
}
|
||||
background->scaleTo(Point(pos.w, pos.h));
|
||||
center();
|
||||
}
|
146
client/lobby/CSelectionBase.h
Normal file
146
client/lobby/CSelectionBase.h
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* CSelectionBase.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
|
||||
class CButton;
|
||||
class CTextBox;
|
||||
class CTextInput;
|
||||
class CAnimImage;
|
||||
class CToggleGroup;
|
||||
class RandomMapTab;
|
||||
class OptionsTab;
|
||||
class SelectionTab;
|
||||
class InfoCard;
|
||||
class CChatBox;
|
||||
class CMapInfo;
|
||||
struct StartInfo;
|
||||
struct PlayerInfo;
|
||||
class CLabel;
|
||||
class CFlagBox;
|
||||
class CLabelGroup;
|
||||
|
||||
class ISelectionScreenInfo
|
||||
{
|
||||
public:
|
||||
ESelectionScreen screenType;
|
||||
|
||||
ISelectionScreenInfo(ESelectionScreen ScreenType = ESelectionScreen::unknown);
|
||||
virtual ~ISelectionScreenInfo();
|
||||
virtual const CMapInfo * getMapInfo() = 0;
|
||||
virtual const StartInfo * getStartInfo() = 0;
|
||||
|
||||
virtual int getCurrentDifficulty();
|
||||
virtual PlayerInfo getPlayerInfo(int color);
|
||||
|
||||
};
|
||||
|
||||
/// The actual map selection screen which consists of the options and selection tab
|
||||
class CSelectionBase : public CWindowObject, public ISelectionScreenInfo
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<InfoCard> card;
|
||||
|
||||
std::shared_ptr<CButton> buttonSelect;
|
||||
std::shared_ptr<CButton> buttonRMG;
|
||||
std::shared_ptr<CButton> buttonOptions;
|
||||
std::shared_ptr<CButton> buttonStart;
|
||||
std::shared_ptr<CButton> buttonBack;
|
||||
|
||||
std::shared_ptr<SelectionTab> tabSel;
|
||||
std::shared_ptr<OptionsTab> tabOpt;
|
||||
std::shared_ptr<RandomMapTab> tabRand;
|
||||
std::shared_ptr<CIntObject> curTab;
|
||||
|
||||
CSelectionBase(ESelectionScreen type);
|
||||
virtual void toggleTab(std::shared_ptr<CIntObject> tab);
|
||||
};
|
||||
|
||||
class InfoCard : public CIntObject
|
||||
{
|
||||
std::shared_ptr<CPicture> playerListBg;
|
||||
std::shared_ptr<CPicture> background;
|
||||
|
||||
std::shared_ptr<CAnimImage> iconsVictoryCondition;
|
||||
std::shared_ptr<CAnimImage> iconsLossCondition;
|
||||
std::shared_ptr<CAnimImage> iconsMapSizes;
|
||||
|
||||
std::shared_ptr<CLabel> labelSaveDate;
|
||||
std::shared_ptr<CLabel> labelScenarioName;
|
||||
std::shared_ptr<CLabel> labelScenarioDescription;
|
||||
std::shared_ptr<CLabel> labelVictoryCondition;
|
||||
std::shared_ptr<CLabel> labelLossCondition;
|
||||
std::shared_ptr<CLabel> labelMapDiff;
|
||||
std::shared_ptr<CLabel> labelPlayerDifficulty;
|
||||
std::shared_ptr<CLabel> labelRating;
|
||||
std::shared_ptr<CLabel> labelCampaignDescription;
|
||||
|
||||
std::shared_ptr<CLabel> mapName;
|
||||
std::shared_ptr<CTextBox> mapDescription;
|
||||
std::shared_ptr<CLabel> labelDifficulty;
|
||||
std::shared_ptr<CLabel> labelDifficultyPercent;
|
||||
std::shared_ptr<CLabel> labelVictoryConditionText;
|
||||
std::shared_ptr<CLabel> labelLossConditionText;
|
||||
|
||||
std::shared_ptr<CLabelGroup> labelGroupPlayersAssigned;
|
||||
std::shared_ptr<CLabelGroup> labelGroupPlayersUnassigned;
|
||||
public:
|
||||
|
||||
bool showChat;
|
||||
std::shared_ptr<CChatBox> chat;
|
||||
std::shared_ptr<CFlagBox> flagbox;
|
||||
|
||||
std::shared_ptr<CToggleGroup> iconDifficulty;
|
||||
|
||||
InfoCard();
|
||||
void changeSelection();
|
||||
void toggleChat();
|
||||
void setChat(bool activateChat);
|
||||
};
|
||||
|
||||
class CChatBox : public CIntObject
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<CTextBox> chatHistory;
|
||||
std::shared_ptr<CTextInput> inputBox;
|
||||
|
||||
CChatBox(const Rect & rect);
|
||||
|
||||
void keyPressed(const SDL_KeyboardEvent & key) override;
|
||||
|
||||
void addNewMessage(const std::string & text);
|
||||
};
|
||||
|
||||
class CFlagBox : public CIntObject
|
||||
{
|
||||
std::shared_ptr<CAnimation> iconsTeamFlags;
|
||||
std::shared_ptr<CLabel> labelAllies;
|
||||
std::shared_ptr<CLabel> labelEnemies;
|
||||
std::vector<std::shared_ptr<CAnimImage>> flagsAllies;
|
||||
std::vector<std::shared_ptr<CAnimImage>> flagsEnemies;
|
||||
|
||||
public:
|
||||
CFlagBox(const Rect & rect);
|
||||
void recreate();
|
||||
void clickRight(tribool down, bool previousState) override;
|
||||
void showTeamsPopup();
|
||||
|
||||
class CFlagBoxTooltipBox : public CWindowObject
|
||||
{
|
||||
std::shared_ptr<CLabel> labelTeamAlignment;
|
||||
std::shared_ptr<CLabelGroup> labelGroupTeams;
|
||||
std::vector<std::shared_ptr<CAnimImage>> iconsFlags;
|
||||
public:
|
||||
CFlagBoxTooltipBox(std::shared_ptr<CAnimation> icons);
|
||||
};
|
||||
};
|
||||
|
||||
extern ISelectionScreenInfo * SEL;
|
537
client/lobby/OptionsTab.cpp
Normal file
537
client/lobby/OptionsTab.cpp
Normal file
@ -0,0 +1,537 @@
|
||||
/*
|
||||
* OptionsTab.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CSelectionBase.h"
|
||||
#include "OptionsTab.h"
|
||||
|
||||
#include "../CBitmapHandler.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../windows/GUIClasses.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
|
||||
#include "../../lib/NetPacksLobby.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CArtHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
#include "../../lib/mapping/CMapInfo.h"
|
||||
|
||||
OptionsTab::OptionsTab()
|
||||
{
|
||||
recActions = 0;
|
||||
OBJ_CONSTRUCTION;
|
||||
background = std::make_shared<CPicture>("ADVOPTBK", 0, 6);
|
||||
pos = background->pos;
|
||||
labelTitle = std::make_shared<CLabel>(222, 30, FONT_BIG, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[515]);
|
||||
labelSubTitle = std::make_shared<CMultiLineLabel>(Rect(60, 44, 320, graphics->fonts[EFonts::FONT_SMALL]->getLineHeight()*2), EFonts::FONT_SMALL, EAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[516]);
|
||||
|
||||
labelPlayerNameAndHandicap = std::make_shared<CMultiLineLabel>(Rect(58, 86, 100, graphics->fonts[EFonts::FONT_SMALL]->getLineHeight()*2), EFonts::FONT_SMALL, EAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[517]);
|
||||
labelStartingTown = std::make_shared<CMultiLineLabel>(Rect(163, 86, 70, graphics->fonts[EFonts::FONT_SMALL]->getLineHeight()*2), EFonts::FONT_SMALL, EAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[518]);
|
||||
labelStartingHero = std::make_shared<CMultiLineLabel>(Rect(239, 86, 70, graphics->fonts[EFonts::FONT_SMALL]->getLineHeight()*2), EFonts::FONT_SMALL, EAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[519]);
|
||||
labelStartingBonus = std::make_shared<CMultiLineLabel>(Rect(315, 86, 70, graphics->fonts[EFonts::FONT_SMALL]->getLineHeight()*2), EFonts::FONT_SMALL, EAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[520]);
|
||||
if(SEL->screenType == ESelectionScreen::newGame || SEL->screenType == ESelectionScreen::loadGame || SEL->screenType == ESelectionScreen::scenarioInfo)
|
||||
{
|
||||
sliderTurnDuration = std::make_shared<CSlider>(Point(55, 551), 194, std::bind(&IServerAPI::setTurnLength, CSH, _1), 1, GameConstants::POSSIBLE_TURNTIME.size(), GameConstants::POSSIBLE_TURNTIME.size(), true, CSlider::BLUE);
|
||||
labelPlayerTurnDuration = std::make_shared<CLabel>(222, 538, FONT_SMALL, EAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[521]);
|
||||
labelTurnDurationValue = std::make_shared<CLabel>(319, 559, FONT_SMALL, EAlignment::CENTER, Colors::WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
void OptionsTab::recreate()
|
||||
{
|
||||
entries.clear();
|
||||
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
for(auto & pInfo : SEL->getStartInfo()->playerInfos)
|
||||
{
|
||||
entries.insert(std::make_pair(pInfo.first, std::make_shared<PlayerOptionsEntry>(pInfo.second)));
|
||||
}
|
||||
|
||||
if(sliderTurnDuration)
|
||||
{
|
||||
sliderTurnDuration->moveTo(vstd::find_pos(GameConstants::POSSIBLE_TURNTIME, SEL->getStartInfo()->turnTime));
|
||||
labelTurnDurationValue->setText(CGI->generaltexth->turnDurations[sliderTurnDuration->getValue()]);
|
||||
}
|
||||
}
|
||||
|
||||
size_t OptionsTab::CPlayerSettingsHelper::getImageIndex()
|
||||
{
|
||||
enum EBonusSelection //frames of bonuses file
|
||||
{
|
||||
WOOD_ORE = 0, CRYSTAL = 1, GEM = 2,
|
||||
MERCURY = 3, SULFUR = 5, GOLD = 8,
|
||||
ARTIFACT = 9, RANDOM = 10,
|
||||
WOOD = 0, ORE = 0, MITHRIL = 10, // resources unavailable in bonuses file
|
||||
|
||||
TOWN_RANDOM = 38, TOWN_NONE = 39, // Special frames in ITPA
|
||||
HERO_RANDOM = 163, HERO_NONE = 164 // Special frames in PortraitsSmall
|
||||
};
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case TOWN:
|
||||
switch(settings.castle)
|
||||
{
|
||||
case PlayerSettings::NONE:
|
||||
return TOWN_NONE;
|
||||
case PlayerSettings::RANDOM:
|
||||
return TOWN_RANDOM;
|
||||
default:
|
||||
return CGI->townh->factions[settings.castle]->town->clientInfo.icons[true][false] + 2;
|
||||
}
|
||||
|
||||
case HERO:
|
||||
switch(settings.hero)
|
||||
{
|
||||
case PlayerSettings::NONE:
|
||||
return HERO_NONE;
|
||||
case PlayerSettings::RANDOM:
|
||||
return HERO_RANDOM;
|
||||
default:
|
||||
{
|
||||
if(settings.heroPortrait >= 0)
|
||||
return settings.heroPortrait;
|
||||
return CGI->heroh->heroes[settings.hero]->imageIndex;
|
||||
}
|
||||
}
|
||||
|
||||
case BONUS:
|
||||
{
|
||||
switch(settings.bonus)
|
||||
{
|
||||
case PlayerSettings::RANDOM:
|
||||
return RANDOM;
|
||||
case PlayerSettings::ARTIFACT:
|
||||
return ARTIFACT;
|
||||
case PlayerSettings::GOLD:
|
||||
return GOLD;
|
||||
case PlayerSettings::RESOURCE:
|
||||
{
|
||||
switch(CGI->townh->factions[settings.castle]->town->primaryRes)
|
||||
{
|
||||
case Res::WOOD_AND_ORE:
|
||||
return WOOD_ORE;
|
||||
case Res::WOOD:
|
||||
return WOOD;
|
||||
case Res::MERCURY:
|
||||
return MERCURY;
|
||||
case Res::ORE:
|
||||
return ORE;
|
||||
case Res::SULFUR:
|
||||
return SULFUR;
|
||||
case Res::CRYSTAL:
|
||||
return CRYSTAL;
|
||||
case Res::GEMS:
|
||||
return GEM;
|
||||
case Res::GOLD:
|
||||
return GOLD;
|
||||
case Res::MITHRIL:
|
||||
return MITHRIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string OptionsTab::CPlayerSettingsHelper::getImageName()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case OptionsTab::TOWN:
|
||||
return "ITPA";
|
||||
case OptionsTab::HERO:
|
||||
return "PortraitsSmall";
|
||||
case OptionsTab::BONUS:
|
||||
return "SCNRSTAR";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string OptionsTab::CPlayerSettingsHelper::getName()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case TOWN:
|
||||
{
|
||||
switch(settings.castle)
|
||||
{
|
||||
case PlayerSettings::NONE:
|
||||
return CGI->generaltexth->allTexts[523];
|
||||
case PlayerSettings::RANDOM:
|
||||
return CGI->generaltexth->allTexts[522];
|
||||
default:
|
||||
return CGI->townh->factions[settings.castle]->name;
|
||||
}
|
||||
}
|
||||
case HERO:
|
||||
{
|
||||
switch(settings.hero)
|
||||
{
|
||||
case PlayerSettings::NONE:
|
||||
return CGI->generaltexth->allTexts[523];
|
||||
case PlayerSettings::RANDOM:
|
||||
return CGI->generaltexth->allTexts[522];
|
||||
default:
|
||||
{
|
||||
if(!settings.heroName.empty())
|
||||
return settings.heroName;
|
||||
return CGI->heroh->heroes[settings.hero]->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
case BONUS:
|
||||
{
|
||||
switch(settings.bonus)
|
||||
{
|
||||
case PlayerSettings::RANDOM:
|
||||
return CGI->generaltexth->allTexts[522];
|
||||
default:
|
||||
return CGI->generaltexth->arraytxt[214 + settings.bonus];
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
std::string OptionsTab::CPlayerSettingsHelper::getTitle()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case OptionsTab::TOWN:
|
||||
return (settings.castle < 0) ? CGI->generaltexth->allTexts[103] : CGI->generaltexth->allTexts[80];
|
||||
case OptionsTab::HERO:
|
||||
return (settings.hero < 0) ? CGI->generaltexth->allTexts[101] : CGI->generaltexth->allTexts[77];
|
||||
case OptionsTab::BONUS:
|
||||
{
|
||||
switch(settings.bonus)
|
||||
{
|
||||
case PlayerSettings::RANDOM:
|
||||
return CGI->generaltexth->allTexts[86]; //{Random Bonus}
|
||||
case PlayerSettings::ARTIFACT:
|
||||
return CGI->generaltexth->allTexts[83]; //{Artifact Bonus}
|
||||
case PlayerSettings::GOLD:
|
||||
return CGI->generaltexth->allTexts[84]; //{Gold Bonus}
|
||||
case PlayerSettings::RESOURCE:
|
||||
return CGI->generaltexth->allTexts[85]; //{Resource Bonus}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
std::string OptionsTab::CPlayerSettingsHelper::getSubtitle()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case TOWN:
|
||||
return getName();
|
||||
case HERO:
|
||||
{
|
||||
if(settings.hero >= 0)
|
||||
return getName() + " - " + CGI->heroh->heroes[settings.hero]->heroClass->name;
|
||||
return getName();
|
||||
}
|
||||
|
||||
case BONUS:
|
||||
{
|
||||
switch(settings.bonus)
|
||||
{
|
||||
case PlayerSettings::GOLD:
|
||||
return CGI->generaltexth->allTexts[87]; //500-1000
|
||||
case PlayerSettings::RESOURCE:
|
||||
{
|
||||
switch(CGI->townh->factions[settings.castle]->town->primaryRes)
|
||||
{
|
||||
case Res::MERCURY:
|
||||
return CGI->generaltexth->allTexts[694];
|
||||
case Res::SULFUR:
|
||||
return CGI->generaltexth->allTexts[695];
|
||||
case Res::CRYSTAL:
|
||||
return CGI->generaltexth->allTexts[692];
|
||||
case Res::GEMS:
|
||||
return CGI->generaltexth->allTexts[693];
|
||||
case Res::WOOD_AND_ORE:
|
||||
return CGI->generaltexth->allTexts[89]; //At the start of the game, 5-10 wood and 5-10 ore are added to your Kingdom's resource pool
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string OptionsTab::CPlayerSettingsHelper::getDescription()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case TOWN:
|
||||
return CGI->generaltexth->allTexts[104];
|
||||
case HERO:
|
||||
return CGI->generaltexth->allTexts[102];
|
||||
case BONUS:
|
||||
{
|
||||
switch(settings.bonus)
|
||||
{
|
||||
case PlayerSettings::RANDOM:
|
||||
return CGI->generaltexth->allTexts[94]; //Gold, wood and ore, or an artifact is randomly chosen as your starting bonus
|
||||
case PlayerSettings::ARTIFACT:
|
||||
return CGI->generaltexth->allTexts[90]; //An artifact is randomly chosen and equipped to your starting hero
|
||||
case PlayerSettings::GOLD:
|
||||
return CGI->generaltexth->allTexts[92]; //At the start of the game, 500-1000 gold is added to your Kingdom's resource pool
|
||||
case PlayerSettings::RESOURCE:
|
||||
{
|
||||
switch(CGI->townh->factions[settings.castle]->town->primaryRes)
|
||||
{
|
||||
case Res::MERCURY:
|
||||
return CGI->generaltexth->allTexts[690];
|
||||
case Res::SULFUR:
|
||||
return CGI->generaltexth->allTexts[691];
|
||||
case Res::CRYSTAL:
|
||||
return CGI->generaltexth->allTexts[688];
|
||||
case Res::GEMS:
|
||||
return CGI->generaltexth->allTexts[689];
|
||||
case Res::WOOD_AND_ORE:
|
||||
return CGI->generaltexth->allTexts[93]; //At the start of the game, 5-10 wood and 5-10 ore are added to your Kingdom's resource pool
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
OptionsTab::CPlayerOptionTooltipBox::CPlayerOptionTooltipBox(CPlayerSettingsHelper & helper)
|
||||
: CWindowObject(BORDERED | RCLICK_POPUP), CPlayerSettingsHelper(helper)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
int value = PlayerSettings::NONE;
|
||||
|
||||
switch(CPlayerSettingsHelper::type)
|
||||
{
|
||||
break;
|
||||
case TOWN:
|
||||
value = settings.castle;
|
||||
break;
|
||||
case HERO:
|
||||
value = settings.hero;
|
||||
break;
|
||||
case BONUS:
|
||||
value = settings.bonus;
|
||||
}
|
||||
|
||||
if(value == PlayerSettings::RANDOM)
|
||||
genBonusWindow();
|
||||
else if(CPlayerSettingsHelper::type == BONUS)
|
||||
genBonusWindow();
|
||||
else if(CPlayerSettingsHelper::type == HERO)
|
||||
genHeroWindow();
|
||||
else if(CPlayerSettingsHelper::type == TOWN)
|
||||
genTownWindow();
|
||||
|
||||
center();
|
||||
}
|
||||
|
||||
void OptionsTab::CPlayerOptionTooltipBox::genHeader()
|
||||
{
|
||||
backgroundTexture = std::make_shared<CFilledTexture>("DIBOXBCK", pos);
|
||||
updateShadow();
|
||||
|
||||
labelTitle = std::make_shared<CLabel>(pos.w / 2 + 8, 21, FONT_MEDIUM, CENTER, Colors::YELLOW, getTitle());
|
||||
labelSubTitle = std::make_shared<CLabel>(pos.w / 2, 88, FONT_SMALL, CENTER, Colors::WHITE, getSubtitle());
|
||||
image = std::make_shared<CAnimImage>(getImageName(), getImageIndex(), 0, pos.w / 2 - 24, 45);
|
||||
}
|
||||
|
||||
void OptionsTab::CPlayerOptionTooltipBox::genTownWindow()
|
||||
{
|
||||
pos = Rect(0, 0, 228, 290);
|
||||
genHeader();
|
||||
labelAssociatedCreatures = std::make_shared<CLabel>(pos.w / 2 + 8, 122, FONT_MEDIUM, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[79]);
|
||||
|
||||
std::vector<CComponent *> components;
|
||||
const CTown * town = CGI->townh->factions[settings.castle]->town;
|
||||
for(auto & elem : town->creatures)
|
||||
{
|
||||
if(!elem.empty())
|
||||
components.push_back(new CComponent(CComponent::creature, elem.front(), 0, CComponent::tiny));
|
||||
}
|
||||
boxAssociatedCreatures = std::make_shared<CComponentBox>(components, Rect(10, 140, pos.w - 20, 140));
|
||||
}
|
||||
|
||||
void OptionsTab::CPlayerOptionTooltipBox::genHeroWindow()
|
||||
{
|
||||
pos = Rect(0, 0, 292, 226);
|
||||
genHeader();
|
||||
labelHeroSpeciality = std::make_shared<CLabel>(pos.w / 2 + 4, 117, FONT_MEDIUM, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[78]);
|
||||
|
||||
imageSpeciality = std::make_shared<CAnimImage>("UN44", CGI->heroh->heroes[settings.hero]->imageIndex, 0, pos.w / 2 - 22, 134);
|
||||
labelSpecialityName = std::make_shared<CLabel>(pos.w / 2, 188, FONT_SMALL, CENTER, Colors::WHITE, CGI->heroh->heroes[settings.hero]->specName);
|
||||
}
|
||||
|
||||
void OptionsTab::CPlayerOptionTooltipBox::genBonusWindow()
|
||||
{
|
||||
pos = Rect(0, 0, 228, 162);
|
||||
genHeader();
|
||||
|
||||
textBonusDescription = std::make_shared<CTextBox>(getDescription(), Rect(10, 100, pos.w - 20, 70), 0, FONT_SMALL, CENTER, Colors::WHITE);
|
||||
}
|
||||
|
||||
OptionsTab::SelectedBox::SelectedBox(Point position, PlayerSettings & settings, SelType type)
|
||||
: CIntObject(RCLICK, position), CPlayerSettingsHelper(settings, type)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
image = std::make_shared<CAnimImage>(getImageName(), getImageIndex());
|
||||
subtitle = std::make_shared<CLabel>(23, 39, FONT_TINY, CENTER, Colors::WHITE, getName());
|
||||
|
||||
pos = image->pos;
|
||||
}
|
||||
|
||||
void OptionsTab::SelectedBox::update()
|
||||
{
|
||||
image->setFrame(getImageIndex());
|
||||
subtitle->setText(getName());
|
||||
}
|
||||
|
||||
void OptionsTab::SelectedBox::clickRight(tribool down, bool previousState)
|
||||
{
|
||||
if(down)
|
||||
{
|
||||
// cases when we do not need to display a message
|
||||
if(settings.castle == -2 && CPlayerSettingsHelper::type == TOWN)
|
||||
return;
|
||||
if(settings.hero == -2 && !SEL->getPlayerInfo(settings.color.getNum()).hasCustomMainHero() && CPlayerSettingsHelper::type == HERO)
|
||||
return;
|
||||
|
||||
GH.pushInt(new CPlayerOptionTooltipBox(*this));
|
||||
}
|
||||
}
|
||||
|
||||
OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S)
|
||||
: pi(SEL->getPlayerInfo(S.color.getNum())), s(S)
|
||||
{
|
||||
OBJ_CONSTRUCTION;
|
||||
defActions |= SHARE_POS;
|
||||
|
||||
int serial = 0;
|
||||
for(int g = 0; g < s.color.getNum(); ++g)
|
||||
{
|
||||
auto itred = SEL->getPlayerInfo(g);
|
||||
if(itred.canComputerPlay || itred.canHumanPlay)
|
||||
serial++;
|
||||
}
|
||||
|
||||
pos.x += 54;
|
||||
pos.y += 122 + serial * 50;
|
||||
|
||||
assert(CSH->mi && CSH->mi->mapHeader);
|
||||
const PlayerInfo & p = SEL->getPlayerInfo(s.color.getNum());
|
||||
assert(p.canComputerPlay || p.canHumanPlay); //someone must be able to control this player
|
||||
if(p.canHumanPlay && p.canComputerPlay)
|
||||
whoCanPlay = HUMAN_OR_CPU;
|
||||
else if(p.canComputerPlay)
|
||||
whoCanPlay = CPU;
|
||||
else
|
||||
whoCanPlay = HUMAN;
|
||||
|
||||
static const char * flags[] =
|
||||
{
|
||||
"AOFLGBR.DEF", "AOFLGBB.DEF", "AOFLGBY.DEF", "AOFLGBG.DEF",
|
||||
"AOFLGBO.DEF", "AOFLGBP.DEF", "AOFLGBT.DEF", "AOFLGBS.DEF"
|
||||
};
|
||||
static const char * bgs[] =
|
||||
{
|
||||
"ADOPRPNL.bmp", "ADOPBPNL.bmp", "ADOPYPNL.bmp", "ADOPGPNL.bmp",
|
||||
"ADOPOPNL.bmp", "ADOPPPNL.bmp", "ADOPTPNL.bmp", "ADOPSPNL.bmp"
|
||||
};
|
||||
|
||||
background = std::make_shared<CPicture>(BitmapHandler::loadBitmap(bgs[s.color.getNum()]), 0, 0, true);
|
||||
labelPlayerName = std::make_shared<CLabel>(55, 10, EFonts::FONT_SMALL, EAlignment::CENTER, Colors::WHITE, s.name);
|
||||
labelWhoCanPlay = std::make_shared<CMultiLineLabel>(Rect(6, 23, 45, graphics->fonts[EFonts::FONT_TINY]->getLineHeight()*2), EFonts::FONT_TINY, EAlignment::CENTER, Colors::WHITE, CGI->generaltexth->arraytxt[206 + whoCanPlay]);
|
||||
|
||||
if(SEL->screenType == ESelectionScreen::newGame)
|
||||
{
|
||||
buttonTownLeft = std::make_shared<CButton>(Point(107, 5), "ADOPLFA.DEF", CGI->generaltexth->zelp[132], std::bind(&IServerAPI::setPlayerOption, CSH, LobbyChangePlayerOption::TOWN, -1, s.color));
|
||||
buttonTownRight = std::make_shared<CButton>(Point(168, 5), "ADOPRTA.DEF", CGI->generaltexth->zelp[133], std::bind(&IServerAPI::setPlayerOption, CSH, LobbyChangePlayerOption::TOWN, +1, s.color));
|
||||
buttonHeroLeft = std::make_shared<CButton>(Point(183, 5), "ADOPLFA.DEF", CGI->generaltexth->zelp[148], std::bind(&IServerAPI::setPlayerOption, CSH, LobbyChangePlayerOption::HERO, -1, s.color));
|
||||
buttonHeroRight = std::make_shared<CButton>(Point(244, 5), "ADOPRTA.DEF", CGI->generaltexth->zelp[149], std::bind(&IServerAPI::setPlayerOption, CSH, LobbyChangePlayerOption::HERO, +1, s.color));
|
||||
buttonBonusLeft = std::make_shared<CButton>(Point(259, 5), "ADOPLFA.DEF", CGI->generaltexth->zelp[164], std::bind(&IServerAPI::setPlayerOption, CSH, LobbyChangePlayerOption::BONUS, -1, s.color));
|
||||
buttonBonusRight = std::make_shared<CButton>(Point(320, 5), "ADOPRTA.DEF", CGI->generaltexth->zelp[165], std::bind(&IServerAPI::setPlayerOption, CSH, LobbyChangePlayerOption::BONUS, +1, s.color));
|
||||
}
|
||||
|
||||
hideUnavailableButtons();
|
||||
|
||||
if(SEL->screenType != ESelectionScreen::scenarioInfo && SEL->getPlayerInfo(s.color.getNum()).canHumanPlay)
|
||||
{
|
||||
flag = std::make_shared<CButton>(Point(-43, 2), flags[s.color.getNum()], CGI->generaltexth->zelp[180], std::bind(&IServerAPI::setPlayer, CSH, s.color));
|
||||
flag->hoverable = true;
|
||||
flag->block(CSH->isGuest());
|
||||
}
|
||||
else
|
||||
flag = nullptr;
|
||||
|
||||
town = std::make_shared<SelectedBox>(Point(119, 2), s, TOWN);
|
||||
hero = std::make_shared<SelectedBox>(Point(195, 2), s, HERO);
|
||||
bonus = std::make_shared<SelectedBox>(Point(271, 2), s, BONUS);
|
||||
}
|
||||
|
||||
void OptionsTab::PlayerOptionsEntry::hideUnavailableButtons()
|
||||
{
|
||||
if(!buttonTownLeft)
|
||||
return;
|
||||
|
||||
const bool foreignPlayer = CSH->isGuest() && !CSH->isMyColor(s.color);
|
||||
|
||||
if((pi.allowedFactions.size() < 2 && !pi.isFactionRandom) || foreignPlayer)
|
||||
{
|
||||
buttonTownLeft->disable();
|
||||
buttonTownRight->disable();
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonTownLeft->enable();
|
||||
buttonTownRight->enable();
|
||||
}
|
||||
|
||||
if((pi.defaultHero() != -1 || s.castle < 0) //fixed hero
|
||||
|| foreignPlayer) //or not our player
|
||||
{
|
||||
buttonHeroLeft->disable();
|
||||
buttonHeroRight->disable();
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonHeroLeft->enable();
|
||||
buttonHeroRight->enable();
|
||||
}
|
||||
|
||||
if(foreignPlayer)
|
||||
{
|
||||
buttonBonusLeft->disable();
|
||||
buttonBonusRight->disable();
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonBonusLeft->enable();
|
||||
buttonBonusRight->enable();
|
||||
}
|
||||
}
|
127
client/lobby/OptionsTab.h
Normal file
127
client/lobby/OptionsTab.h
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* OptionsTab.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../../lib/StartInfo.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
|
||||
class CSlider;
|
||||
class CLabel;
|
||||
class CMultiLineLabel;
|
||||
class CFilledTexture;
|
||||
class CAnimImage;
|
||||
class CComponentBox;
|
||||
/// The options tab which is shown at the map selection phase.
|
||||
class OptionsTab : public CIntObject
|
||||
{
|
||||
std::shared_ptr<CPicture> background;
|
||||
std::shared_ptr<CLabel> labelTitle;
|
||||
std::shared_ptr<CMultiLineLabel> labelSubTitle;
|
||||
std::shared_ptr<CMultiLineLabel> labelPlayerNameAndHandicap;
|
||||
std::shared_ptr<CMultiLineLabel> labelStartingTown;
|
||||
std::shared_ptr<CMultiLineLabel> labelStartingHero;
|
||||
std::shared_ptr<CMultiLineLabel> labelStartingBonus;
|
||||
|
||||
std::shared_ptr<CLabel> labelPlayerTurnDuration;
|
||||
std::shared_ptr<CLabel> labelTurnDurationValue;
|
||||
|
||||
public:
|
||||
enum SelType
|
||||
{
|
||||
TOWN,
|
||||
HERO,
|
||||
BONUS
|
||||
};
|
||||
|
||||
struct CPlayerSettingsHelper
|
||||
{
|
||||
const PlayerSettings & settings;
|
||||
const SelType type;
|
||||
|
||||
CPlayerSettingsHelper(const PlayerSettings & settings, SelType type)
|
||||
: settings(settings), type(type)
|
||||
{}
|
||||
|
||||
/// visible image settings
|
||||
size_t getImageIndex();
|
||||
std::string getImageName();
|
||||
|
||||
std::string getName(); /// name visible in options dialog
|
||||
std::string getTitle(); /// title in popup box
|
||||
std::string getSubtitle(); /// popup box subtitle
|
||||
std::string getDescription(); /// popup box description, not always present
|
||||
};
|
||||
|
||||
class CPlayerOptionTooltipBox : public CWindowObject, public CPlayerSettingsHelper
|
||||
{
|
||||
std::shared_ptr<CFilledTexture> backgroundTexture;
|
||||
std::shared_ptr<CLabel> labelTitle;
|
||||
std::shared_ptr<CLabel> labelSubTitle;
|
||||
std::shared_ptr<CAnimImage> image;
|
||||
|
||||
std::shared_ptr<CLabel> labelAssociatedCreatures;
|
||||
std::shared_ptr<CComponentBox> boxAssociatedCreatures;
|
||||
|
||||
std::shared_ptr<CLabel> labelHeroSpeciality;
|
||||
std::shared_ptr<CAnimImage> imageSpeciality;
|
||||
std::shared_ptr<CLabel> labelSpecialityName;
|
||||
|
||||
std::shared_ptr<CTextBox> textBonusDescription;
|
||||
|
||||
void genHeader();
|
||||
void genTownWindow();
|
||||
void genHeroWindow();
|
||||
void genBonusWindow();
|
||||
|
||||
public:
|
||||
CPlayerOptionTooltipBox(CPlayerSettingsHelper & helper);
|
||||
};
|
||||
|
||||
/// Image with current town/hero/bonus
|
||||
struct SelectedBox : public CIntObject, public CPlayerSettingsHelper
|
||||
{
|
||||
std::shared_ptr<CAnimImage> image;
|
||||
std::shared_ptr<CLabel> subtitle;
|
||||
|
||||
SelectedBox(Point position, PlayerSettings & settings, SelType type);
|
||||
void clickRight(tribool down, bool previousState) override;
|
||||
|
||||
void update();
|
||||
};
|
||||
|
||||
struct PlayerOptionsEntry : public CIntObject
|
||||
{
|
||||
PlayerInfo pi;
|
||||
PlayerSettings s;
|
||||
std::shared_ptr<CLabel> labelPlayerName;
|
||||
std::shared_ptr<CMultiLineLabel> labelWhoCanPlay;
|
||||
std::shared_ptr<CPicture> background;
|
||||
std::shared_ptr<CButton> buttonTownLeft;
|
||||
std::shared_ptr<CButton> buttonTownRight;
|
||||
std::shared_ptr<CButton> buttonHeroLeft;
|
||||
std::shared_ptr<CButton> buttonHeroRight;
|
||||
std::shared_ptr<CButton> buttonBonusLeft;
|
||||
std::shared_ptr<CButton> buttonBonusRight;
|
||||
std::shared_ptr<CButton> flag;
|
||||
std::shared_ptr<SelectedBox> town;
|
||||
std::shared_ptr<SelectedBox> hero;
|
||||
std::shared_ptr<SelectedBox> bonus;
|
||||
enum {HUMAN_OR_CPU, HUMAN, CPU} whoCanPlay;
|
||||
|
||||
PlayerOptionsEntry(const PlayerSettings & S);
|
||||
void hideUnavailableButtons();
|
||||
};
|
||||
|
||||
std::shared_ptr<CSlider> sliderTurnDuration;
|
||||
std::map<PlayerColor, std::shared_ptr<PlayerOptionsEntry>> entries;
|
||||
|
||||
OptionsTab();
|
||||
void recreate();
|
||||
};
|
300
client/lobby/RandomMapTab.cpp
Normal file
300
client/lobby/RandomMapTab.cpp
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* RandomMapTab.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "RandomMapTab.h"
|
||||
#include "CSelectionBase.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../windows/GUIClasses.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/mapping/CMapInfo.h"
|
||||
#include "../../lib/rmg/CMapGenOptions.h"
|
||||
|
||||
RandomMapTab::RandomMapTab()
|
||||
{
|
||||
recActions = 0;
|
||||
mapGenOptions = std::make_shared<CMapGenOptions>();
|
||||
OBJ_CONSTRUCTION;
|
||||
background = std::make_shared<CPicture>("RANMAPBK", 0, 6);
|
||||
|
||||
labelHeadlineBig = std::make_shared<CLabel>(222, 36, FONT_BIG, EAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[738]);
|
||||
labelHeadlineSmall = std::make_shared<CLabel>(222, 56, FONT_SMALL, EAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[739]);
|
||||
|
||||
labelMapSize = std::make_shared<CLabel>(104, 97, FONT_SMALL, EAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[752]);
|
||||
groupMapSize = std::make_shared<CToggleGroup>(0);
|
||||
groupMapSize->pos.y += 81;
|
||||
groupMapSize->pos.x += 158;
|
||||
const std::vector<std::string> mapSizeBtns = {"RANSIZS", "RANSIZM", "RANSIZL", "RANSIZX"};
|
||||
addButtonsToGroup(groupMapSize.get(), mapSizeBtns, 0, 3, 47, 198);
|
||||
groupMapSize->setSelected(1);
|
||||
groupMapSize->addCallback([&](int btnId)
|
||||
{
|
||||
auto mapSizeVal = getPossibleMapSizes();
|
||||
mapGenOptions->setWidth(mapSizeVal[btnId]);
|
||||
mapGenOptions->setHeight(mapSizeVal[btnId]);
|
||||
updateMapInfoByHost();
|
||||
});
|
||||
|
||||
buttonTwoLevels = std::make_shared<CToggleButton>(Point(346, 81), "RANUNDR", CGI->generaltexth->zelp[202]);
|
||||
buttonTwoLevels->setSelected(true);
|
||||
buttonTwoLevels->addCallback([&](bool on)
|
||||
{
|
||||
mapGenOptions->setHasTwoLevels(on);
|
||||
updateMapInfoByHost();
|
||||
});
|
||||
|
||||
labelGroupForOptions = std::make_shared<CLabelGroup>(FONT_SMALL, EAlignment::TOPLEFT, Colors::WHITE);
|
||||
// Create number defs list
|
||||
std::vector<std::string> numberDefs;
|
||||
for(int i = 0; i <= 8; ++i)
|
||||
{
|
||||
numberDefs.push_back("RANNUM" + boost::lexical_cast<std::string>(i));
|
||||
}
|
||||
|
||||
const int NUMBERS_WIDTH = 32;
|
||||
const int BTNS_GROUP_LEFT_MARGIN = 67;
|
||||
labelGroupForOptions->add(68, 133, CGI->generaltexth->allTexts[753]);
|
||||
groupMaxPlayers = std::make_shared<CToggleGroup>(0);
|
||||
groupMaxPlayers->pos.y += 153;
|
||||
groupMaxPlayers->pos.x += BTNS_GROUP_LEFT_MARGIN;
|
||||
addButtonsWithRandToGroup(groupMaxPlayers.get(), numberDefs, 1, 8, NUMBERS_WIDTH, 204, 212);
|
||||
groupMaxPlayers->addCallback([&](int btnId)
|
||||
{
|
||||
mapGenOptions->setPlayerCount(btnId);
|
||||
deactivateButtonsFrom(groupMaxTeams.get(), btnId);
|
||||
deactivateButtonsFrom(groupCompOnlyPlayers.get(), btnId);
|
||||
validatePlayersCnt(btnId);
|
||||
updateMapInfoByHost();
|
||||
});
|
||||
|
||||
labelGroupForOptions->add(68, 199, CGI->generaltexth->allTexts[754]);
|
||||
groupMaxTeams = std::make_shared<CToggleGroup>(0);
|
||||
groupMaxTeams->pos.y += 219;
|
||||
groupMaxTeams->pos.x += BTNS_GROUP_LEFT_MARGIN;
|
||||
addButtonsWithRandToGroup(groupMaxTeams.get(), numberDefs, 0, 7, NUMBERS_WIDTH, 214, 222);
|
||||
groupMaxTeams->addCallback([&](int btnId)
|
||||
{
|
||||
mapGenOptions->setTeamCount(btnId);
|
||||
updateMapInfoByHost();
|
||||
});
|
||||
|
||||
labelGroupForOptions->add(68, 265, CGI->generaltexth->allTexts[755]);
|
||||
groupCompOnlyPlayers = std::make_shared<CToggleGroup>(0);
|
||||
groupCompOnlyPlayers->pos.y += 285;
|
||||
groupCompOnlyPlayers->pos.x += BTNS_GROUP_LEFT_MARGIN;
|
||||
addButtonsWithRandToGroup(groupCompOnlyPlayers.get(), numberDefs, 0, 7, NUMBERS_WIDTH, 224, 232);
|
||||
groupCompOnlyPlayers->addCallback([&](int btnId)
|
||||
{
|
||||
mapGenOptions->setCompOnlyPlayerCount(btnId);
|
||||
deactivateButtonsFrom(groupCompOnlyTeams.get(), (btnId == 0 ? 1 : btnId));
|
||||
validateCompOnlyPlayersCnt(btnId);
|
||||
updateMapInfoByHost();
|
||||
});
|
||||
|
||||
labelGroupForOptions->add(68, 331, CGI->generaltexth->allTexts[756]);
|
||||
groupCompOnlyTeams = std::make_shared<CToggleGroup>(0);
|
||||
groupCompOnlyTeams->pos.y += 351;
|
||||
groupCompOnlyTeams->pos.x += BTNS_GROUP_LEFT_MARGIN;
|
||||
addButtonsWithRandToGroup(groupCompOnlyTeams.get(), numberDefs, 0, 6, NUMBERS_WIDTH, 234, 241);
|
||||
deactivateButtonsFrom(groupCompOnlyTeams.get(), 1);
|
||||
groupCompOnlyTeams->addCallback([&](int btnId)
|
||||
{
|
||||
mapGenOptions->setCompOnlyTeamCount(btnId);
|
||||
updateMapInfoByHost();
|
||||
});
|
||||
|
||||
labelGroupForOptions->add(68, 398, CGI->generaltexth->allTexts[757]);
|
||||
const int WIDE_BTN_WIDTH = 85;
|
||||
groupWaterContent = std::make_shared<CToggleGroup>(0);
|
||||
groupWaterContent->pos.y += 419;
|
||||
groupWaterContent->pos.x += BTNS_GROUP_LEFT_MARGIN;
|
||||
const std::vector<std::string> waterContentBtns = {"RANNONE", "RANNORM", "RANISLD"};
|
||||
addButtonsWithRandToGroup(groupWaterContent.get(), waterContentBtns, 0, 2, WIDE_BTN_WIDTH, 243, 246);
|
||||
groupWaterContent->addCallback([&](int btnId)
|
||||
{
|
||||
mapGenOptions->setWaterContent(static_cast<EWaterContent::EWaterContent>(btnId));
|
||||
updateMapInfoByHost();
|
||||
});
|
||||
|
||||
labelGroupForOptions->add(68, 465, CGI->generaltexth->allTexts[758]);
|
||||
groupMonsterStrength = std::make_shared<CToggleGroup>(0);
|
||||
groupMonsterStrength->pos.y += 485;
|
||||
groupMonsterStrength->pos.x += BTNS_GROUP_LEFT_MARGIN;
|
||||
const std::vector<std::string> monsterStrengthBtns = {"RANWEAK", "RANNORM", "RANSTRG"};
|
||||
addButtonsWithRandToGroup(groupMonsterStrength.get(), monsterStrengthBtns, 0, 2, WIDE_BTN_WIDTH, 248, 251);
|
||||
groupMonsterStrength->addCallback([&](int btnId)
|
||||
{
|
||||
if(btnId < 0)
|
||||
mapGenOptions->setMonsterStrength(EMonsterStrength::RANDOM);
|
||||
else
|
||||
mapGenOptions->setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(btnId + EMonsterStrength::GLOBAL_WEAK)); //value 2 to 4
|
||||
updateMapInfoByHost();
|
||||
});
|
||||
|
||||
buttonShowRandomMaps = std::make_shared<CButton>(Point(54, 535), "RANSHOW", CGI->generaltexth->zelp[252]);
|
||||
|
||||
updateMapInfoByHost();
|
||||
}
|
||||
|
||||
void RandomMapTab::updateMapInfoByHost()
|
||||
{
|
||||
if(CSH->isGuest())
|
||||
return;
|
||||
|
||||
// Generate header info
|
||||
mapInfo = std::make_shared<CMapInfo>();
|
||||
mapInfo->isRandomMap = true;
|
||||
mapInfo->mapHeader = make_unique<CMapHeader>();
|
||||
mapInfo->mapHeader->version = EMapFormat::SOD;
|
||||
mapInfo->mapHeader->name = CGI->generaltexth->allTexts[740];
|
||||
mapInfo->mapHeader->description = CGI->generaltexth->allTexts[741];
|
||||
mapInfo->mapHeader->difficulty = 1; // Normal
|
||||
mapInfo->mapHeader->height = mapGenOptions->getHeight();
|
||||
mapInfo->mapHeader->width = mapGenOptions->getWidth();
|
||||
mapInfo->mapHeader->twoLevel = mapGenOptions->getHasTwoLevels();
|
||||
|
||||
// Generate player information
|
||||
mapInfo->mapHeader->players.clear();
|
||||
int playersToGen = PlayerColor::PLAYER_LIMIT_I;
|
||||
if(mapGenOptions->getPlayerCount() != CMapGenOptions::RANDOM_SIZE)
|
||||
playersToGen = mapGenOptions->getPlayerCount();
|
||||
mapInfo->mapHeader->howManyTeams = playersToGen;
|
||||
|
||||
for(int i = 0; i < playersToGen; ++i)
|
||||
{
|
||||
PlayerInfo player;
|
||||
player.isFactionRandom = true;
|
||||
player.canComputerPlay = true;
|
||||
if(mapGenOptions->getCompOnlyPlayerCount() != CMapGenOptions::RANDOM_SIZE && i >= mapGenOptions->getHumanOnlyPlayerCount())
|
||||
{
|
||||
player.canHumanPlay = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
player.canHumanPlay = true;
|
||||
}
|
||||
player.team = TeamID(i);
|
||||
player.hasMainTown = true;
|
||||
player.generateHeroAtMainTown = true;
|
||||
mapInfo->mapHeader->players.push_back(player);
|
||||
}
|
||||
|
||||
mapInfoChanged(mapInfo, mapGenOptions);
|
||||
}
|
||||
|
||||
void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts)
|
||||
{
|
||||
groupMapSize->setSelected(vstd::find_pos(getPossibleMapSizes(), opts->getWidth()));
|
||||
buttonTwoLevels->setSelected(opts->getHasTwoLevels());
|
||||
groupMaxPlayers->setSelected(opts->getPlayerCount());
|
||||
groupMaxTeams->setSelected(opts->getTeamCount());
|
||||
groupCompOnlyPlayers->setSelected(opts->getCompOnlyPlayerCount());
|
||||
groupCompOnlyTeams->setSelected(opts->getCompOnlyTeamCount());
|
||||
groupWaterContent->setSelected(opts->getWaterContent());
|
||||
groupMonsterStrength->setSelected(opts->getMonsterStrength());
|
||||
}
|
||||
|
||||
void RandomMapTab::addButtonsWithRandToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex, int helpRandIndex) const
|
||||
{
|
||||
addButtonsToGroup(group, defs, nStart, nEnd, btnWidth, helpStartIndex);
|
||||
|
||||
// Buttons are relative to button group, TODO better solution?
|
||||
SObjectConstruction obj__i(group);
|
||||
const std::string RANDOM_DEF = "RANRAND";
|
||||
group->addToggle(CMapGenOptions::RANDOM_SIZE, new CToggleButton(Point(256, 0), RANDOM_DEF, CGI->generaltexth->zelp[helpRandIndex]));
|
||||
group->setSelected(CMapGenOptions::RANDOM_SIZE);
|
||||
}
|
||||
|
||||
void RandomMapTab::addButtonsToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex) const
|
||||
{
|
||||
// Buttons are relative to button group, TODO better solution?
|
||||
SObjectConstruction obj__i(group);
|
||||
int cnt = nEnd - nStart + 1;
|
||||
for(int i = 0; i < cnt; ++i)
|
||||
{
|
||||
auto button = new CToggleButton(Point(i * btnWidth, 0), defs[i + nStart], CGI->generaltexth->zelp[helpStartIndex + i]);
|
||||
// For blocked state we should use pressed image actually
|
||||
button->setImageOrder(0, 1, 1, 3);
|
||||
|
||||
group->addToggle(i + nStart, button);
|
||||
}
|
||||
}
|
||||
|
||||
void RandomMapTab::deactivateButtonsFrom(CToggleGroup * group, int startId)
|
||||
{
|
||||
logGlobal->debug("Blocking buttons from %d", startId);
|
||||
for(auto toggle : group->buttons)
|
||||
{
|
||||
if(auto button = dynamic_cast<CToggleButton *>(toggle.second))
|
||||
{
|
||||
if(startId == CMapGenOptions::RANDOM_SIZE || toggle.first < startId)
|
||||
{
|
||||
button->block(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
button->block(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RandomMapTab::validatePlayersCnt(int playersCnt)
|
||||
{
|
||||
if(playersCnt == CMapGenOptions::RANDOM_SIZE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(mapGenOptions->getTeamCount() >= playersCnt)
|
||||
{
|
||||
mapGenOptions->setTeamCount(playersCnt - 1);
|
||||
groupMaxTeams->setSelected(mapGenOptions->getTeamCount());
|
||||
}
|
||||
if(mapGenOptions->getCompOnlyPlayerCount() >= playersCnt)
|
||||
{
|
||||
mapGenOptions->setCompOnlyPlayerCount(playersCnt - 1);
|
||||
groupCompOnlyPlayers->setSelected(mapGenOptions->getCompOnlyPlayerCount());
|
||||
}
|
||||
|
||||
validateCompOnlyPlayersCnt(mapGenOptions->getCompOnlyPlayerCount());
|
||||
}
|
||||
|
||||
void RandomMapTab::validateCompOnlyPlayersCnt(int compOnlyPlayersCnt)
|
||||
{
|
||||
if(compOnlyPlayersCnt == CMapGenOptions::RANDOM_SIZE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(mapGenOptions->getCompOnlyTeamCount() >= compOnlyPlayersCnt)
|
||||
{
|
||||
int compOnlyTeamCount = compOnlyPlayersCnt == 0 ? 0 : compOnlyPlayersCnt - 1;
|
||||
mapGenOptions->setCompOnlyTeamCount(compOnlyTeamCount);
|
||||
updateMapInfoByHost();
|
||||
groupCompOnlyTeams->setSelected(compOnlyTeamCount);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> RandomMapTab::getPossibleMapSizes()
|
||||
{
|
||||
return {CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_MIDDLE, CMapHeader::MAP_SIZE_LARGE, CMapHeader::MAP_SIZE_XLARGE};
|
||||
}
|
58
client/lobby/RandomMapTab.h
Normal file
58
client/lobby/RandomMapTab.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* RandomMapTab.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CSelectionBase.h"
|
||||
|
||||
#include "../../lib/FunctionList.h"
|
||||
|
||||
class CMapGenOptions;
|
||||
class CToggleButton;
|
||||
class CLabel;
|
||||
class CLabelGroup;
|
||||
|
||||
class RandomMapTab : public CIntObject
|
||||
{
|
||||
public:
|
||||
RandomMapTab();
|
||||
|
||||
void updateMapInfoByHost();
|
||||
void setMapGenOptions(std::shared_ptr<CMapGenOptions> opts);
|
||||
|
||||
CFunctionList<void(std::shared_ptr<CMapInfo>, std::shared_ptr<CMapGenOptions>)> mapInfoChanged;
|
||||
|
||||
private:
|
||||
void addButtonsWithRandToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex, int helpRandIndex) const;
|
||||
void addButtonsToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex) const;
|
||||
void deactivateButtonsFrom(CToggleGroup * group, int startId);
|
||||
void validatePlayersCnt(int playersCnt);
|
||||
void validateCompOnlyPlayersCnt(int compOnlyPlayersCnt);
|
||||
std::vector<int> getPossibleMapSizes();
|
||||
|
||||
|
||||
std::shared_ptr<CPicture> background;
|
||||
std::shared_ptr<CLabel> labelHeadlineBig;
|
||||
std::shared_ptr<CLabel> labelHeadlineSmall;
|
||||
|
||||
std::shared_ptr<CLabel> labelMapSize;
|
||||
std::shared_ptr<CToggleGroup> groupMapSize;
|
||||
std::shared_ptr<CToggleButton> buttonTwoLevels;
|
||||
|
||||
std::shared_ptr<CLabelGroup> labelGroupForOptions;
|
||||
std::shared_ptr<CToggleGroup> groupMaxPlayers;
|
||||
std::shared_ptr<CToggleGroup> groupMaxTeams;
|
||||
std::shared_ptr<CToggleGroup> groupCompOnlyPlayers;
|
||||
std::shared_ptr<CToggleGroup> groupCompOnlyTeams;
|
||||
std::shared_ptr<CToggleGroup> groupWaterContent;
|
||||
std::shared_ptr<CToggleGroup> groupMonsterStrength;
|
||||
std::shared_ptr<CButton> buttonShowRandomMaps;
|
||||
std::shared_ptr<CMapGenOptions> mapGenOptions;
|
||||
std::shared_ptr<CMapInfo> mapInfo;
|
||||
};
|
664
client/lobby/SelectionTab.cpp
Normal file
664
client/lobby/SelectionTab.cpp
Normal file
@ -0,0 +1,664 @@
|
||||
/*
|
||||
* SelectionTab.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "SelectionTab.h"
|
||||
#include "CSelectionBase.h"
|
||||
#include "CLobbyScreen.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMessage.h"
|
||||
#include "../CBitmapHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../windows/GUIClasses.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../../lib/NetPacksLobby.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CModHandler.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/mapping/CMapInfo.h"
|
||||
#include "../../lib/serializer/Connection.h"
|
||||
|
||||
|
||||
bool mapSorter::operator()(const std::shared_ptr<CMapInfo> aaa, const std::shared_ptr<CMapInfo> bbb)
|
||||
{
|
||||
auto a = aaa->mapHeader.get();
|
||||
auto b = bbb->mapHeader.get();
|
||||
if(a && b) //if we are sorting scenarios
|
||||
{
|
||||
switch(sortBy)
|
||||
{
|
||||
case _format: //by map format (RoE, WoG, etc)
|
||||
return (a->version < b->version);
|
||||
break;
|
||||
case _loscon: //by loss conditions
|
||||
return (a->defeatMessage < b->defeatMessage);
|
||||
break;
|
||||
case _playerAm: //by player amount
|
||||
int playerAmntB, humenPlayersB, playerAmntA, humenPlayersA;
|
||||
playerAmntB = humenPlayersB = playerAmntA = humenPlayersA = 0;
|
||||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
if(a->players[i].canHumanPlay)
|
||||
{
|
||||
playerAmntA++;
|
||||
humenPlayersA++;
|
||||
}
|
||||
else if(a->players[i].canComputerPlay)
|
||||
{
|
||||
playerAmntA++;
|
||||
}
|
||||
if(b->players[i].canHumanPlay)
|
||||
{
|
||||
playerAmntB++;
|
||||
humenPlayersB++;
|
||||
}
|
||||
else if(b->players[i].canComputerPlay)
|
||||
{
|
||||
playerAmntB++;
|
||||
}
|
||||
}
|
||||
if(playerAmntB != playerAmntA)
|
||||
return (playerAmntA < playerAmntB);
|
||||
else
|
||||
return (humenPlayersA < humenPlayersB);
|
||||
break;
|
||||
case _size: //by size of map
|
||||
return (a->width < b->width);
|
||||
break;
|
||||
case _viccon: //by victory conditions
|
||||
return (a->victoryMessage < b->victoryMessage);
|
||||
break;
|
||||
case _name: //by name
|
||||
return boost::ilexicographical_compare(a->name, b->name);
|
||||
case _fileName: //by filename
|
||||
return boost::ilexicographical_compare(aaa->fileURI, bbb->fileURI);
|
||||
default:
|
||||
return boost::ilexicographical_compare(a->name, b->name);
|
||||
}
|
||||
}
|
||||
else //if we are sorting campaigns
|
||||
{
|
||||
switch(sortBy)
|
||||
{
|
||||
case _numOfMaps: //by number of maps in campaign
|
||||
return CGI->generaltexth->campaignRegionNames[aaa->campaignHeader->mapVersion].size() <
|
||||
CGI->generaltexth->campaignRegionNames[bbb->campaignHeader->mapVersion].size();
|
||||
break;
|
||||
case _name: //by name
|
||||
return boost::ilexicographical_compare(aaa->campaignHeader->name, bbb->campaignHeader->name);
|
||||
default:
|
||||
return boost::ilexicographical_compare(aaa->campaignHeader->name, bbb->campaignHeader->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SelectionTab::SelectionTab(ESelectionScreen Type)
|
||||
: CIntObject(LCLICK | WHEEL | KEYBOARD | DOUBLECLICK), callOnSelect(nullptr), tabType(Type), selectionPos(0), sortModeAscending(true)
|
||||
{
|
||||
OBJ_CONSTRUCTION;
|
||||
if(tabType != ESelectionScreen::campaignList)
|
||||
{
|
||||
sortingBy = _format;
|
||||
background = std::make_shared<CPicture>("SCSELBCK.bmp", 0, 6);
|
||||
pos = background->pos;
|
||||
inputName = std::make_shared<CTextInput>(Rect(32, 539, 350, 20), Point(-32, -25), "GSSTRIP.bmp", 0);
|
||||
inputName->filters += CTextInput::filenameFilter;
|
||||
labelMapSizes = std::make_shared<CLabel>(87, 62, FONT_SMALL, EAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[510]);
|
||||
|
||||
int sizes[] = {36, 72, 108, 144, 0};
|
||||
const char * filterIconNmes[] = {"SCSMBUT.DEF", "SCMDBUT.DEF", "SCLGBUT.DEF", "SCXLBUT.DEF", "SCALBUT.DEF"};
|
||||
for(int i = 0; i < 5; i++)
|
||||
buttonsSortBy.push_back(std::make_shared<CButton>(Point(158 + 47 * i, 46), filterIconNmes[i], CGI->generaltexth->zelp[54 + i], std::bind(&SelectionTab::filter, this, sizes[i], true)));
|
||||
|
||||
int xpos[] = {23, 55, 88, 121, 306, 339};
|
||||
const char * sortIconNames[] = {"SCBUTT1.DEF", "SCBUTT2.DEF", "SCBUTCP.DEF", "SCBUTT3.DEF", "SCBUTT4.DEF", "SCBUTT5.DEF"};
|
||||
for(int i = 0; i < 6; i++)
|
||||
{
|
||||
ESortBy criteria = (ESortBy)i;
|
||||
if(criteria == _name)
|
||||
criteria = generalSortingBy;
|
||||
|
||||
buttonsSortBy.push_back(std::make_shared<CButton>(Point(xpos[i], 86), sortIconNames[i], CGI->generaltexth->zelp[107 + i], std::bind(&SelectionTab::sortBy, this, criteria)));
|
||||
}
|
||||
}
|
||||
|
||||
int positionsToShow = 18;
|
||||
std::string tabTitle;
|
||||
switch(tabType)
|
||||
{
|
||||
case ESelectionScreen::newGame:
|
||||
generalSortingBy = ESortBy::_name;
|
||||
tabTitle = CGI->generaltexth->arraytxt[229];
|
||||
break;
|
||||
case ESelectionScreen::loadGame:
|
||||
generalSortingBy = ESortBy::_fileName;
|
||||
tabTitle = CGI->generaltexth->arraytxt[230];
|
||||
break;
|
||||
case ESelectionScreen::saveGame:
|
||||
positionsToShow = 16;
|
||||
generalSortingBy = ESortBy::_fileName;
|
||||
tabTitle = CGI->generaltexth->arraytxt[231];
|
||||
break;
|
||||
case ESelectionScreen::campaignList:
|
||||
generalSortingBy = ESortBy::_name;
|
||||
tabTitle = CGI->generaltexth->allTexts[726];
|
||||
type |= REDRAW_PARENT; // we use parent background so we need to make sure it's will be redrawn too
|
||||
pos.w = parent->pos.w;
|
||||
pos.h = parent->pos.h;
|
||||
pos.x += 3;
|
||||
pos.y += 6;
|
||||
|
||||
buttonsSortBy.push_back(std::make_shared<CButton>(Point(23, 86), "CamCusM.DEF", CButton::tooltip(), std::bind(&SelectionTab::sortBy, this, _numOfMaps)));
|
||||
buttonsSortBy.push_back(std::make_shared<CButton>(Point(55, 86), "CamCusL.DEF", CButton::tooltip(), std::bind(&SelectionTab::sortBy, this, _name)));
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
iconsMapFormats = std::make_shared<CAnimation>("SCSELC.DEF");
|
||||
iconsMapFormats->preload();
|
||||
iconsVictoryCondition = std::make_shared<CAnimation>("SCNRVICT.DEF");
|
||||
iconsVictoryCondition->preload();
|
||||
iconsLossCondition = std::make_shared<CAnimation>("SCNRLOSS.DEF");
|
||||
iconsLossCondition->preload();
|
||||
for(int i = 0; i < positionsToShow; i++)
|
||||
listItems.push_back(std::make_shared<ListItem>(Point(30, 129 + i * 25), iconsMapFormats, iconsVictoryCondition, iconsLossCondition));
|
||||
|
||||
labelTabTitle = std::make_shared<CLabel>(205, 28, FONT_MEDIUM, EAlignment::CENTER, Colors::YELLOW, tabTitle);
|
||||
slider = std::make_shared<CSlider>(Point(372, 86), tabType != ESelectionScreen::saveGame ? 480 : 430, std::bind(&SelectionTab::sliderMove, this, _1), positionsToShow, curItems.size(), 0, false, CSlider::BLUE);
|
||||
filter(0);
|
||||
}
|
||||
|
||||
void SelectionTab::toggleMode()
|
||||
{
|
||||
if(CSH->isGuest())
|
||||
{
|
||||
allItems.clear();
|
||||
curItems.clear();
|
||||
if(slider)
|
||||
slider->block(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(tabType)
|
||||
{
|
||||
case ESelectionScreen::newGame:
|
||||
inputName->disable();
|
||||
parseMaps(getFiles("Maps/", EResType::MAP));
|
||||
break;
|
||||
|
||||
case ESelectionScreen::loadGame:
|
||||
inputName->disable();
|
||||
parseSaves(getFiles("Saves/", EResType::CLIENT_SAVEGAME));
|
||||
break;
|
||||
|
||||
case ESelectionScreen::saveGame:
|
||||
parseSaves(getFiles("Saves/", EResType::CLIENT_SAVEGAME));
|
||||
inputName->enable();
|
||||
restoreLastSelection();
|
||||
break;
|
||||
|
||||
case ESelectionScreen::campaignList:
|
||||
parseCampaigns(getFiles("Maps/", EResType::CAMPAIGN));
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
if(slider)
|
||||
{
|
||||
slider->block(false);
|
||||
filter(0);
|
||||
}
|
||||
|
||||
if(CSH->campaignStateToSend)
|
||||
{
|
||||
CSH->setCampaignState(CSH->campaignStateToSend);
|
||||
CSH->campaignStateToSend.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
restoreLastSelection();
|
||||
}
|
||||
}
|
||||
slider->setAmount(curItems.size());
|
||||
updateListItems();
|
||||
redraw();
|
||||
}
|
||||
|
||||
void SelectionTab::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
if(down)
|
||||
{
|
||||
int line = getLine();
|
||||
if(line != -1)
|
||||
select(line);
|
||||
}
|
||||
}
|
||||
void SelectionTab::keyPressed(const SDL_KeyboardEvent & key)
|
||||
{
|
||||
if(key.state != SDL_PRESSED)
|
||||
return;
|
||||
|
||||
int moveBy = 0;
|
||||
switch(key.keysym.sym)
|
||||
{
|
||||
case SDLK_UP:
|
||||
moveBy = -1;
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
moveBy = +1;
|
||||
break;
|
||||
case SDLK_PAGEUP:
|
||||
moveBy = -listItems.size() + 1;
|
||||
break;
|
||||
case SDLK_PAGEDOWN:
|
||||
moveBy = +listItems.size() - 1;
|
||||
break;
|
||||
case SDLK_HOME:
|
||||
select(-slider->getValue());
|
||||
return;
|
||||
case SDLK_END:
|
||||
select(curItems.size() - slider->getValue());
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
select(selectionPos - slider->getValue() + moveBy);
|
||||
}
|
||||
|
||||
void SelectionTab::onDoubleClick()
|
||||
{
|
||||
if(getLine() != -1) //double clicked scenarios list
|
||||
{
|
||||
(static_cast<CLobbyScreen *>(parent))->buttonStart->clickLeft(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
// A new size filter (Small, Medium, ...) has been selected. Populate
|
||||
// selMaps with the relevant data.
|
||||
void SelectionTab::filter(int size, bool selectFirst)
|
||||
{
|
||||
curItems.clear();
|
||||
|
||||
if(tabType == ESelectionScreen::campaignList)
|
||||
{
|
||||
for(auto elem : allItems)
|
||||
curItems.push_back(elem);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto elem : allItems)
|
||||
{
|
||||
if(elem->mapHeader && elem->mapHeader->version && (!size || elem->mapHeader->width == size))
|
||||
curItems.push_back(elem);
|
||||
}
|
||||
}
|
||||
|
||||
if(curItems.size())
|
||||
{
|
||||
slider->block(false);
|
||||
slider->setAmount(curItems.size());
|
||||
sort();
|
||||
if(selectFirst)
|
||||
{
|
||||
slider->moveTo(0);
|
||||
callOnSelect(curItems[0]);
|
||||
selectAbs(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
slider->block(true);
|
||||
if(callOnSelect)
|
||||
callOnSelect(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionTab::sortBy(int criteria)
|
||||
{
|
||||
if(criteria == sortingBy)
|
||||
{
|
||||
sortModeAscending = !sortModeAscending;
|
||||
}
|
||||
else
|
||||
{
|
||||
sortingBy = (ESortBy)criteria;
|
||||
sortModeAscending = true;
|
||||
}
|
||||
sort();
|
||||
|
||||
selectAbs(0);
|
||||
}
|
||||
|
||||
void SelectionTab::sort()
|
||||
{
|
||||
if(sortingBy != generalSortingBy)
|
||||
std::stable_sort(curItems.begin(), curItems.end(), mapSorter(generalSortingBy));
|
||||
std::stable_sort(curItems.begin(), curItems.end(), mapSorter(sortingBy));
|
||||
|
||||
if(!sortModeAscending)
|
||||
std::reverse(curItems.begin(), curItems.end());
|
||||
|
||||
updateListItems();
|
||||
redraw();
|
||||
}
|
||||
|
||||
void SelectionTab::select(int position)
|
||||
{
|
||||
if(!curItems.size())
|
||||
return;
|
||||
|
||||
// New selection. py is the index in curItems.
|
||||
int py = position + slider->getValue();
|
||||
vstd::amax(py, 0);
|
||||
vstd::amin(py, curItems.size() - 1);
|
||||
|
||||
selectionPos = py;
|
||||
|
||||
if(position < 0)
|
||||
slider->moveBy(position);
|
||||
else if(position >= listItems.size())
|
||||
slider->moveBy(position - listItems.size() + 1);
|
||||
|
||||
rememberCurrentSelection();
|
||||
|
||||
if(inputName && inputName->active)
|
||||
{
|
||||
auto filename = *CResourceHandler::get("local")->getResourceName(ResourceID(curItems[py]->fileURI, EResType::CLIENT_SAVEGAME));
|
||||
inputName->setText(filename.stem().string());
|
||||
}
|
||||
updateListItems();
|
||||
if(callOnSelect)
|
||||
callOnSelect(curItems[py]);
|
||||
}
|
||||
|
||||
void SelectionTab::selectAbs(int position)
|
||||
{
|
||||
select(position - slider->getValue());
|
||||
}
|
||||
|
||||
void SelectionTab::sliderMove(int slidPos)
|
||||
{
|
||||
if(!slider)
|
||||
return; // ignore spurious call when slider is being created
|
||||
updateListItems();
|
||||
redraw();
|
||||
}
|
||||
|
||||
void SelectionTab::updateListItems()
|
||||
{
|
||||
// elemIdx is the index of the maps or saved game to display on line 0
|
||||
// slider->capacity contains the number of available screen lines
|
||||
// slider->positionsAmnt is the number of elements after filtering
|
||||
int elemIdx = slider->getValue();
|
||||
for(auto item : listItems)
|
||||
{
|
||||
if(elemIdx < curItems.size())
|
||||
{
|
||||
item->updateItem(curItems[elemIdx], elemIdx == selectionPos);
|
||||
elemIdx++;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->updateItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int SelectionTab::getLine()
|
||||
{
|
||||
int line = -1;
|
||||
Point clickPos(GH.current->button.x, GH.current->button.y);
|
||||
clickPos = clickPos - pos.topLeft();
|
||||
|
||||
// Ignore clicks on save name area
|
||||
int maxPosY;
|
||||
if(tabType == ESelectionScreen::saveGame)
|
||||
maxPosY = 516;
|
||||
else
|
||||
maxPosY = 564;
|
||||
|
||||
if(clickPos.y > 115 && clickPos.y < maxPosY && clickPos.x > 22 && clickPos.x < 371)
|
||||
{
|
||||
line = (clickPos.y - 115) / 25; //which line
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
void SelectionTab::selectFileName(std::string fname)
|
||||
{
|
||||
boost::to_upper(fname);
|
||||
for(int i = curItems.size() - 1; i >= 0; i--)
|
||||
{
|
||||
if(curItems[i]->fileURI == fname)
|
||||
{
|
||||
slider->moveTo(i);
|
||||
selectAbs(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
selectAbs(0);
|
||||
}
|
||||
|
||||
std::shared_ptr<CMapInfo> SelectionTab::getSelectedMapInfo() const
|
||||
{
|
||||
return curItems.empty() ? nullptr : curItems[selectionPos];
|
||||
}
|
||||
|
||||
void SelectionTab::rememberCurrentSelection()
|
||||
{
|
||||
// TODO: this can be more elegant
|
||||
if(tabType == ESelectionScreen::newGame)
|
||||
{
|
||||
Settings lastMap = settings.write["general"]["lastMap"];
|
||||
lastMap->String() = getSelectedMapInfo()->fileURI;
|
||||
}
|
||||
else if(tabType == ESelectionScreen::loadGame)
|
||||
{
|
||||
Settings lastSave = settings.write["general"]["lastSave"];
|
||||
lastSave->String() = getSelectedMapInfo()->fileURI;
|
||||
}
|
||||
else if(tabType == ESelectionScreen::campaignList)
|
||||
{
|
||||
Settings lastCampaign = settings.write["general"]["lastCampaign"];
|
||||
lastCampaign->String() = getSelectedMapInfo()->fileURI;
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionTab::restoreLastSelection()
|
||||
{
|
||||
switch(tabType)
|
||||
{
|
||||
case ESelectionScreen::newGame:
|
||||
selectFileName(settings["general"]["lastMap"].String());
|
||||
break;
|
||||
case ESelectionScreen::campaignList:
|
||||
selectFileName(settings["general"]["lastCampaign"].String());
|
||||
break;
|
||||
case ESelectionScreen::loadGame:
|
||||
case ESelectionScreen::saveGame:
|
||||
selectFileName(settings["general"]["lastSave"].String());
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionTab::parseMaps(const std::unordered_set<ResourceID> & files)
|
||||
{
|
||||
logGlobal->debug("Parsing %d maps", files.size());
|
||||
allItems.clear();
|
||||
for(auto & file : files)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto mapInfo = std::make_shared<CMapInfo>();
|
||||
mapInfo->mapInit(file.getName());
|
||||
|
||||
// ignore unsupported map versions (e.g. WoG maps without WoG)
|
||||
// but accept VCMI maps
|
||||
if((mapInfo->mapHeader->version >= EMapFormat::VCMI) || (mapInfo->mapHeader->version <= CGI->modh->settings.data["textData"]["mapVersion"].Float()))
|
||||
allItems.push_back(mapInfo);
|
||||
}
|
||||
catch(std::exception & e)
|
||||
{
|
||||
logGlobal->error("Map %s is invalid. Message: %s", file.getName(), e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionTab::parseSaves(const std::unordered_set<ResourceID> & files)
|
||||
{
|
||||
for(auto & file : files)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto mapInfo = std::make_shared<CMapInfo>();
|
||||
mapInfo->saveInit(file);
|
||||
|
||||
// Filter out other game modes
|
||||
bool isCampaign = mapInfo->scenarioOptionsOfSave->mode == StartInfo::CAMPAIGN;
|
||||
bool isMultiplayer = mapInfo->amountOfHumanPlayersInSave > 1;
|
||||
switch(CSH->getLoadMode())
|
||||
{
|
||||
case ELoadMode::SINGLE:
|
||||
if(isMultiplayer || isCampaign)
|
||||
mapInfo->mapHeader.reset();
|
||||
break;
|
||||
case ELoadMode::CAMPAIGN:
|
||||
if(!isCampaign)
|
||||
mapInfo->mapHeader.reset();
|
||||
break;
|
||||
default:
|
||||
if(!isMultiplayer)
|
||||
mapInfo->mapHeader.reset();
|
||||
break;
|
||||
}
|
||||
|
||||
allItems.push_back(mapInfo);
|
||||
}
|
||||
catch(const std::exception & e)
|
||||
{
|
||||
logGlobal->error("Error: Failed to process %s: %s", file.getName(), e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionTab::parseCampaigns(const std::unordered_set<ResourceID> & files)
|
||||
{
|
||||
allItems.reserve(files.size());
|
||||
for(auto & file : files)
|
||||
{
|
||||
auto info = std::make_shared<CMapInfo>();
|
||||
//allItems[i].date = std::asctime(std::localtime(&files[i].date));
|
||||
info->fileURI = file.getName();
|
||||
info->campaignInit();
|
||||
allItems.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<ResourceID> SelectionTab::getFiles(std::string dirURI, int resType)
|
||||
{
|
||||
boost::to_upper(dirURI);
|
||||
CResourceHandler::get()->updateFilteredFiles([&](const std::string & mount)
|
||||
{
|
||||
return boost::algorithm::starts_with(mount, dirURI);
|
||||
});
|
||||
|
||||
std::unordered_set<ResourceID> ret = CResourceHandler::get()->getFilteredFiles([&](const ResourceID & ident)
|
||||
{
|
||||
return ident.getType() == resType && boost::algorithm::starts_with(ident.getName(), dirURI);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SelectionTab::ListItem::ListItem(Point position, std::shared_ptr<CAnimation> iconsFormats, std::shared_ptr<CAnimation> iconsVictory, std::shared_ptr<CAnimation> iconsLoss)
|
||||
: CIntObject(LCLICK, position)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
labelName = std::make_shared<CLabel>(184, 0, FONT_SMALL, EAlignment::CENTER, Colors::WHITE);
|
||||
labelName->setAutoRedraw(false);
|
||||
labelAmountOfPlayers = std::make_shared<CLabel>(8, 0, FONT_SMALL, EAlignment::CENTER, Colors::WHITE);
|
||||
labelAmountOfPlayers->setAutoRedraw(false);
|
||||
labelNumberOfCampaignMaps = std::make_shared<CLabel>(8, 0, FONT_SMALL, EAlignment::CENTER, Colors::WHITE);
|
||||
labelNumberOfCampaignMaps->setAutoRedraw(false);
|
||||
labelMapSizeLetter = std::make_shared<CLabel>(41, 0, FONT_SMALL, EAlignment::CENTER, Colors::WHITE);
|
||||
labelMapSizeLetter->setAutoRedraw(false);
|
||||
// FIXME: This -12 should not be needed, but for some reason CAnimImage displaced otherwise
|
||||
iconFormat = std::make_shared<CAnimImage>(iconsFormats, 0, 0, 59, -12);
|
||||
iconVictoryCondition = std::make_shared<CAnimImage>(iconsVictory, 0, 0, 277, -12);
|
||||
iconLossCondition = std::make_shared<CAnimImage>(iconsLoss, 0, 0, 310, -12);
|
||||
}
|
||||
|
||||
void SelectionTab::ListItem::updateItem(std::shared_ptr<CMapInfo> info, bool selected)
|
||||
{
|
||||
if(!info)
|
||||
{
|
||||
labelAmountOfPlayers->disable();
|
||||
labelMapSizeLetter->disable();
|
||||
iconFormat->disable();
|
||||
iconVictoryCondition->disable();
|
||||
iconLossCondition->disable();
|
||||
labelNumberOfCampaignMaps->disable();
|
||||
labelName->disable();
|
||||
return;
|
||||
}
|
||||
|
||||
auto color = selected ? Colors::YELLOW : Colors::WHITE;
|
||||
if(info->campaignHeader)
|
||||
{
|
||||
labelAmountOfPlayers->disable();
|
||||
labelMapSizeLetter->disable();
|
||||
iconFormat->disable();
|
||||
iconVictoryCondition->disable();
|
||||
iconLossCondition->disable();
|
||||
labelNumberOfCampaignMaps->enable();
|
||||
std::ostringstream ostr(std::ostringstream::out);
|
||||
ostr << CGI->generaltexth->campaignRegionNames[info->campaignHeader->mapVersion].size();
|
||||
labelNumberOfCampaignMaps->setText(ostr.str());
|
||||
labelNumberOfCampaignMaps->setColor(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
labelNumberOfCampaignMaps->disable();
|
||||
std::ostringstream ostr(std::ostringstream::out);
|
||||
ostr << info->amountOfPlayersOnMap << "/" << info->amountOfHumanControllablePlayers;
|
||||
labelAmountOfPlayers->enable();
|
||||
labelAmountOfPlayers->setText(ostr.str());
|
||||
labelAmountOfPlayers->setColor(color);
|
||||
labelMapSizeLetter->enable();
|
||||
labelMapSizeLetter->setText(info->getMapSizeName());
|
||||
labelMapSizeLetter->setColor(color);
|
||||
iconFormat->enable();
|
||||
iconFormat->setFrame(info->getMapSizeFormatIconId().first, info->getMapSizeFormatIconId().second);
|
||||
iconVictoryCondition->enable();
|
||||
iconVictoryCondition->setFrame(info->mapHeader->victoryIconIndex, 0);
|
||||
iconLossCondition->enable();
|
||||
iconLossCondition->setFrame(info->mapHeader->defeatIconIndex, 0);
|
||||
}
|
||||
labelName->enable();
|
||||
labelName->setText(info->getNameForList());
|
||||
labelName->setColor(color);
|
||||
}
|
98
client/lobby/SelectionTab.h
Normal file
98
client/lobby/SelectionTab.h
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* SelectionTab.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CSelectionBase.h"
|
||||
|
||||
class CSlider;
|
||||
class CLabel;
|
||||
|
||||
enum ESortBy
|
||||
{
|
||||
_playerAm, _size, _format, _name, _viccon, _loscon, _numOfMaps, _fileName
|
||||
}; //_numOfMaps is for campaigns
|
||||
|
||||
/// Class which handles map sorting by different criteria
|
||||
class mapSorter
|
||||
{
|
||||
public:
|
||||
ESortBy sortBy;
|
||||
bool operator()(const std::shared_ptr<CMapInfo> aaa, const std::shared_ptr<CMapInfo> bbb);
|
||||
mapSorter(ESortBy es) : sortBy(es){};
|
||||
};
|
||||
|
||||
class SelectionTab : public CIntObject
|
||||
{
|
||||
struct ListItem : public CIntObject
|
||||
{
|
||||
std::shared_ptr<CLabel> labelAmountOfPlayers;
|
||||
std::shared_ptr<CLabel> labelNumberOfCampaignMaps;
|
||||
std::shared_ptr<CLabel> labelMapSizeLetter;
|
||||
std::shared_ptr<CAnimImage> iconFormat;
|
||||
std::shared_ptr<CAnimImage> iconVictoryCondition;
|
||||
std::shared_ptr<CAnimImage> iconLossCondition;
|
||||
std::shared_ptr<CLabel> labelName;
|
||||
|
||||
ListItem(Point position, std::shared_ptr<CAnimation> iconsFormats, std::shared_ptr<CAnimation> iconsVictory, std::shared_ptr<CAnimation> iconsLoss);
|
||||
void updateItem(std::shared_ptr<CMapInfo> info = {}, bool selected = false);
|
||||
};
|
||||
std::vector<std::shared_ptr<ListItem>> listItems;
|
||||
|
||||
std::shared_ptr<CAnimation> iconsMapFormats;
|
||||
// FIXME: CSelectionBase use them too!
|
||||
std::shared_ptr<CAnimation> iconsVictoryCondition;
|
||||
std::shared_ptr<CAnimation> iconsLossCondition;
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<CMapInfo>> allItems;
|
||||
std::vector<std::shared_ptr<CMapInfo>> curItems;
|
||||
size_t selectionPos;
|
||||
std::function<void(std::shared_ptr<CMapInfo>)> callOnSelect;
|
||||
|
||||
ESortBy sortingBy;
|
||||
ESortBy generalSortingBy;
|
||||
bool sortModeAscending;
|
||||
|
||||
std::shared_ptr<CTextInput> inputName;
|
||||
|
||||
SelectionTab(ESelectionScreen Type);
|
||||
void toggleMode();
|
||||
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void keyPressed(const SDL_KeyboardEvent & key) override;
|
||||
void onDoubleClick() override;
|
||||
|
||||
void filter(int size, bool selectFirst = false); //0 - all
|
||||
void sortBy(int criteria);
|
||||
void sort();
|
||||
void select(int position); //position: <0 - positions> position on the screen
|
||||
void selectAbs(int position); //position: absolute position in curItems vector
|
||||
void sliderMove(int slidPos);
|
||||
void updateListItems();
|
||||
int getLine();
|
||||
void selectFileName(std::string fname);
|
||||
std::shared_ptr<CMapInfo> getSelectedMapInfo() const;
|
||||
void rememberCurrentSelection();
|
||||
void restoreLastSelection();
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<CPicture> background;
|
||||
std::shared_ptr<CSlider> slider;
|
||||
std::vector<std::shared_ptr<CButton>> buttonsSortBy;
|
||||
std::shared_ptr<CLabel> labelTabTitle;
|
||||
std::shared_ptr<CLabel> labelMapSizes;
|
||||
ESelectionScreen tabType;
|
||||
|
||||
void parseMaps(const std::unordered_set<ResourceID> & files);
|
||||
void parseSaves(const std::unordered_set<ResourceID> & files);
|
||||
void parseCampaigns(const std::unordered_set<ResourceID> & files);
|
||||
std::unordered_set<ResourceID> getFiles(std::string dirURI, int resType);
|
||||
};
|
156
client/mainmenu/CCampaignScreen.cpp
Normal file
156
client/mainmenu/CCampaignScreen.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* CCampaignScreen.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
#include "CCampaignScreen.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMessage.h"
|
||||
#include "../CBitmapHandler.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CVideoHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../windows/GUIClasses.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../windows/CWindowObject.h"
|
||||
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
|
||||
#include "../../lib/CArtHandler.h"
|
||||
#include "../../lib/CBuildingHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
|
||||
#include "../../lib/CSkillHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
|
||||
#include "../../lib/mapping/CCampaignHandler.h"
|
||||
#include "../../lib/mapping/CMapService.h"
|
||||
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
|
||||
CCampaignScreen::CCampaignScreen(const JsonNode & config)
|
||||
: CWindowObject(BORDERED)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
for(const JsonNode & node : config["images"].Vector())
|
||||
images.push_back(CMainMenu::createPicture(node));
|
||||
|
||||
if(!images.empty())
|
||||
{
|
||||
images[0]->center(); // move background to center
|
||||
moveTo(images[0]->pos.topLeft()); // move everything else to center
|
||||
images[0]->moveTo(pos.topLeft()); // restore moved twice background
|
||||
pos = images[0]->pos; // fix height\width of this window
|
||||
}
|
||||
|
||||
if(!config["exitbutton"].isNull())
|
||||
{
|
||||
buttonBack = createExitButton(config["exitbutton"]);
|
||||
buttonBack->hoverable = true;
|
||||
}
|
||||
|
||||
for(const JsonNode & node : config["items"].Vector())
|
||||
campButtons.push_back(std::make_shared<CCampaignButton>(node));
|
||||
}
|
||||
|
||||
std::shared_ptr<CButton> CCampaignScreen::createExitButton(const JsonNode & button)
|
||||
{
|
||||
std::pair<std::string, std::string> help;
|
||||
if(!button["help"].isNull() && button["help"].Float() > 0)
|
||||
help = CGI->generaltexth->zelp[button["help"].Float()];
|
||||
|
||||
std::function<void()> close = std::bind(&CGuiHandler::popIntTotally, &GH, this);
|
||||
return std::make_shared<CButton>(Point(button["x"].Float(), button["y"].Float()), button["name"].String(), help, close, button["hotkey"].Float());
|
||||
}
|
||||
|
||||
CCampaignScreen::CCampaignButton::CCampaignButton(const JsonNode & config)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
pos.x += config["x"].Float();
|
||||
pos.y += config["y"].Float();
|
||||
pos.w = 200;
|
||||
pos.h = 116;
|
||||
|
||||
campFile = config["file"].String();
|
||||
video = config["video"].String();
|
||||
|
||||
status = config["open"].Bool() ? CCampaignScreen::ENABLED : CCampaignScreen::DISABLED;
|
||||
|
||||
CCampaignHeader header = CCampaignHandler::getHeader(campFile);
|
||||
hoverText = header.name;
|
||||
|
||||
if(status != CCampaignScreen::DISABLED)
|
||||
{
|
||||
addUsedEvents(LCLICK | HOVER);
|
||||
graphicsImage = std::make_shared<CPicture>(config["image"].String());
|
||||
|
||||
hoverLabel = std::make_shared<CLabel>(pos.w / 2, pos.h + 20, FONT_MEDIUM, CENTER, Colors::YELLOW, "");
|
||||
parent->addChild(hoverLabel.get());
|
||||
}
|
||||
|
||||
if(status == CCampaignScreen::COMPLETED)
|
||||
graphicsCompleted = std::make_shared<CPicture>("CAMPCHK");
|
||||
}
|
||||
|
||||
void CCampaignScreen::CCampaignButton::show(SDL_Surface * to)
|
||||
{
|
||||
if(status == CCampaignScreen::DISABLED)
|
||||
return;
|
||||
|
||||
CIntObject::show(to);
|
||||
|
||||
// Play the campaign button video when the mouse cursor is placed over the button
|
||||
if(hovered)
|
||||
{
|
||||
if(CCS->videoh->fname != video)
|
||||
CCS->videoh->open(video);
|
||||
|
||||
CCS->videoh->update(pos.x, pos.y, to, true, false); // plays sequentially frame by frame, starts at the beginning when the video is over
|
||||
}
|
||||
else if(CCS->videoh->fname == video) // When you got out of the bounds of the button then close the video
|
||||
{
|
||||
CCS->videoh->close();
|
||||
redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void CCampaignScreen::CCampaignButton::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
if(down)
|
||||
{
|
||||
CCS->videoh->close();
|
||||
CMainMenu::openCampaignLobby(campFile);
|
||||
}
|
||||
}
|
||||
|
||||
void CCampaignScreen::CCampaignButton::hover(bool on)
|
||||
{
|
||||
if(hoverLabel)
|
||||
{
|
||||
if(on)
|
||||
hoverLabel->setText(hoverText); // Shows the name of the campaign when you get into the bounds of the button
|
||||
else
|
||||
hoverLabel->setText(" ");
|
||||
}
|
||||
}
|
55
client/mainmenu/CCampaignScreen.h
Normal file
55
client/mainmenu/CCampaignScreen.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* CCampaignScreen.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class CLabel;
|
||||
class CPicture;
|
||||
class CButton;
|
||||
class SDL_Surface;
|
||||
class JsonNode;
|
||||
|
||||
class CCampaignScreen : public CWindowObject
|
||||
{
|
||||
public:
|
||||
enum CampaignStatus {DEFAULT = 0, ENABLED, DISABLED, COMPLETED}; // the status of the campaign
|
||||
|
||||
private:
|
||||
/// A button which plays a video when you move the mouse cursor over it
|
||||
class CCampaignButton : public CIntObject
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<CLabel> hoverLabel;
|
||||
std::shared_ptr<CPicture> graphicsImage;
|
||||
std::shared_ptr<CPicture> graphicsCompleted;
|
||||
CampaignStatus status;
|
||||
|
||||
std::string campFile; // the filename/resourcename of the campaign
|
||||
std::string video; // the resource name of the video
|
||||
std::string hoverText;
|
||||
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void hover(bool on) override;
|
||||
|
||||
public:
|
||||
CCampaignButton(const JsonNode & config);
|
||||
void show(SDL_Surface * to) override;
|
||||
};
|
||||
|
||||
std::vector<std::shared_ptr<CCampaignButton>> campButtons;
|
||||
std::vector<std::shared_ptr<CPicture>> images;
|
||||
std::shared_ptr<CButton> buttonBack;
|
||||
|
||||
std::shared_ptr<CButton> createExitButton(const JsonNode & button);
|
||||
|
||||
public:
|
||||
enum CampaignSet {ROE, AB, SOD, WOG};
|
||||
|
||||
CCampaignScreen(const JsonNode & config);
|
||||
};
|
566
client/mainmenu/CMainMenu.cpp
Normal file
566
client/mainmenu/CMainMenu.cpp
Normal file
@ -0,0 +1,566 @@
|
||||
/*
|
||||
* CMainMenu.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "CMainMenu.h"
|
||||
|
||||
#include "CCampaignScreen.h"
|
||||
#include "CreditsScreen.h"
|
||||
|
||||
#include "../lobby/CBonusSelection.h"
|
||||
#include "../lobby/CSelectionBase.h"
|
||||
#include "../lobby/CLobbyScreen.h"
|
||||
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/filesystem/CCompressedStream.h"
|
||||
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/JsonNode.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CVideoHandler.h"
|
||||
#include "../Graphics.h"
|
||||
#include "../../lib/serializer/Connection.h"
|
||||
#include "../../lib/serializer/CTypeList.h"
|
||||
#include "../../lib/VCMIDirs.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
#include "../windows/GUIClasses.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../../CCallback.h"
|
||||
#include "../CMessage.h"
|
||||
#include "../CBitmapHandler.h"
|
||||
#include "../Client.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../../lib/CStopWatch.h"
|
||||
#include "../../lib/NetPacksLobby.h"
|
||||
#include "../../lib/CThreadHelper.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../../lib/CRandomGenerator.h"
|
||||
#include "../../lib/CondSh.h"
|
||||
#include "../../lib/mapping/CCampaignHandler.h"
|
||||
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
CMainMenu * CMM = nullptr;
|
||||
ISelectionScreenInfo * SEL;
|
||||
|
||||
static void do_quit()
|
||||
{
|
||||
SDL_Event event;
|
||||
event.quit.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
CMenuScreen::CMenuScreen(const JsonNode & configNode)
|
||||
: CWindowObject(BORDERED), config(configNode)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
background = std::make_shared<CPicture>(config["background"].String());
|
||||
if(config["scalable"].Bool())
|
||||
{
|
||||
if(background->bg->format->palette)
|
||||
background->convertToScreenBPP();
|
||||
background->scaleTo(Point(screen->w, screen->h));
|
||||
}
|
||||
|
||||
pos = background->center();
|
||||
|
||||
for(const JsonNode & node : config["items"].Vector())
|
||||
menuNameToEntry.push_back(node["name"].String());
|
||||
|
||||
for(const JsonNode & node : config["images"].Vector())
|
||||
images.push_back(CMainMenu::createPicture(node));
|
||||
|
||||
//Hardcoded entry
|
||||
menuNameToEntry.push_back("credits");
|
||||
|
||||
tabs = std::make_shared<CTabbedInt>(std::bind(&CMenuScreen::createTab, this, _1), CTabbedInt::DestroyFunc());
|
||||
tabs->type |= REDRAW_PARENT;
|
||||
}
|
||||
|
||||
CIntObject * CMenuScreen::createTab(size_t index)
|
||||
{
|
||||
if(config["items"].Vector().size() == index)
|
||||
return new CreditsScreen();
|
||||
|
||||
return new CMenuEntry(this, config["items"].Vector()[index]);
|
||||
}
|
||||
|
||||
void CMenuScreen::show(SDL_Surface * to)
|
||||
{
|
||||
if(!config["video"].isNull())
|
||||
CCS->videoh->update(config["video"]["x"].Float() + pos.x, config["video"]["y"].Float() + pos.y, to, true, false);
|
||||
CIntObject::show(to);
|
||||
}
|
||||
|
||||
void CMenuScreen::activate()
|
||||
{
|
||||
CCS->musich->playMusic("Music/MainMenu", true);
|
||||
if(!config["video"].isNull())
|
||||
CCS->videoh->open(config["video"]["name"].String());
|
||||
CIntObject::activate();
|
||||
}
|
||||
|
||||
void CMenuScreen::deactivate()
|
||||
{
|
||||
if(!config["video"].isNull())
|
||||
CCS->videoh->close();
|
||||
|
||||
CIntObject::deactivate();
|
||||
}
|
||||
|
||||
void CMenuScreen::switchToTab(size_t index)
|
||||
{
|
||||
tabs->setActive(index);
|
||||
}
|
||||
|
||||
//funciton for std::string -> std::function conversion for main menu
|
||||
static std::function<void()> genCommand(CMenuScreen * menu, std::vector<std::string> menuType, const std::string & string)
|
||||
{
|
||||
static const std::vector<std::string> commandType = {"to", "campaigns", "start", "load", "exit", "highscores"};
|
||||
|
||||
static const std::vector<std::string> gameType = {"single", "multi", "campaign", "tutorial"};
|
||||
|
||||
std::list<std::string> commands;
|
||||
boost::split(commands, string, boost::is_any_of("\t "));
|
||||
|
||||
if(!commands.empty())
|
||||
{
|
||||
size_t index = std::find(commandType.begin(), commandType.end(), commands.front()) - commandType.begin();
|
||||
commands.pop_front();
|
||||
if(index > 3 || !commands.empty())
|
||||
{
|
||||
switch(index)
|
||||
{
|
||||
case 0: //to - switch to another tab, if such tab exists
|
||||
{
|
||||
size_t index2 = std::find(menuType.begin(), menuType.end(), commands.front()) - menuType.begin();
|
||||
if(index2 != menuType.size())
|
||||
return std::bind(&CMenuScreen::switchToTab, menu, index2);
|
||||
break;
|
||||
}
|
||||
case 1: //open campaign selection window
|
||||
{
|
||||
return std::bind(&CMainMenu::openCampaignScreen, CMM, commands.front());
|
||||
break;
|
||||
}
|
||||
case 2: //start
|
||||
{
|
||||
switch(std::find(gameType.begin(), gameType.end(), commands.front()) - gameType.begin())
|
||||
{
|
||||
case 0:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::newGame, true, nullptr, ELoadMode::NONE);
|
||||
case 1:
|
||||
return []() { GH.pushInt(new CMultiMode(ESelectionScreen::newGame)); };
|
||||
case 2:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::campaignList, true, nullptr, ELoadMode::NONE);
|
||||
case 3:
|
||||
return std::bind(CInfoWindow::showInfoDialog, "Sorry, tutorial is not implemented yet\n", (const std::vector<CComponent *> *)nullptr, false, PlayerColor(1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: //load
|
||||
{
|
||||
switch(std::find(gameType.begin(), gameType.end(), commands.front()) - gameType.begin())
|
||||
{
|
||||
case 0:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::loadGame, true, nullptr, ELoadMode::SINGLE);
|
||||
case 1:
|
||||
return []() { GH.pushInt(new CMultiMode(ESelectionScreen::loadGame)); };
|
||||
case 2:
|
||||
return std::bind(CMainMenu::openLobby, ESelectionScreen::loadGame, true, nullptr, ELoadMode::CAMPAIGN);
|
||||
case 3:
|
||||
return std::bind(CInfoWindow::showInfoDialog, "Sorry, tutorial is not implemented yet\n", (const std::vector<CComponent *> *)nullptr, false, PlayerColor(1));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4: //exit
|
||||
{
|
||||
return std::bind(CInfoWindow::showYesNoDialog, std::ref(CGI->generaltexth->allTexts[69]), (const std::vector<CComponent *> *)nullptr, do_quit, 0, false, PlayerColor(1));
|
||||
}
|
||||
break;
|
||||
case 5: //highscores
|
||||
{
|
||||
return std::bind(CInfoWindow::showInfoDialog, "Sorry, high scores menu is not implemented yet\n", (const std::vector<CComponent *> *)nullptr, false, PlayerColor(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
logGlobal->error("Failed to parse command: %s", string);
|
||||
return std::function<void()>();
|
||||
}
|
||||
|
||||
std::shared_ptr<CButton> CMenuEntry::createButton(CMenuScreen * parent, const JsonNode & button)
|
||||
{
|
||||
std::function<void()> command = genCommand(parent, parent->menuNameToEntry, button["command"].String());
|
||||
|
||||
std::pair<std::string, std::string> help;
|
||||
if(!button["help"].isNull() && button["help"].Float() > 0)
|
||||
help = CGI->generaltexth->zelp[button["help"].Float()];
|
||||
|
||||
int posx = button["x"].Float();
|
||||
if(posx < 0)
|
||||
posx = pos.w + posx;
|
||||
|
||||
int posy = button["y"].Float();
|
||||
if(posy < 0)
|
||||
posy = pos.h + posy;
|
||||
|
||||
return std::make_shared<CButton>(Point(posx, posy), button["name"].String(), help, command, button["hotkey"].Float());
|
||||
}
|
||||
|
||||
CMenuEntry::CMenuEntry(CMenuScreen * parent, const JsonNode & config)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
type |= REDRAW_PARENT;
|
||||
pos = parent->pos;
|
||||
|
||||
for(const JsonNode & node : config["images"].Vector())
|
||||
images.push_back(CMainMenu::createPicture(node));
|
||||
|
||||
for(const JsonNode & node : config["buttons"].Vector())
|
||||
{
|
||||
buttons.push_back(createButton(parent, node));
|
||||
buttons.back()->hoverable = true;
|
||||
buttons.back()->type |= REDRAW_PARENT;
|
||||
}
|
||||
}
|
||||
|
||||
CMainMenuConfig::CMainMenuConfig()
|
||||
: campaignSets(JsonNode(ResourceID("config/campaignSets.json"))), config(JsonNode(ResourceID("config/mainmenu.json")))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CMainMenuConfig & CMainMenuConfig::get()
|
||||
{
|
||||
static CMainMenuConfig config;
|
||||
return config;
|
||||
}
|
||||
|
||||
const JsonNode & CMainMenuConfig::getConfig() const
|
||||
{
|
||||
return config;
|
||||
}
|
||||
|
||||
const JsonNode & CMainMenuConfig::getCampaigns() const
|
||||
{
|
||||
return campaignSets;
|
||||
}
|
||||
|
||||
CMainMenu::CMainMenu()
|
||||
{
|
||||
pos.w = screen->w;
|
||||
pos.h = screen->h;
|
||||
|
||||
GH.defActionsDef = 63;
|
||||
CMM = this;
|
||||
menu = new CMenuScreen(CMainMenuConfig::get().getConfig()["window"]);
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
backgroundAroundMenu = std::make_shared<CFilledTexture>("DIBOXBCK", pos);
|
||||
}
|
||||
|
||||
CMainMenu::~CMainMenu()
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
|
||||
if(CMM == this)
|
||||
CMM = nullptr;
|
||||
|
||||
if(GH.curInt == this)
|
||||
GH.curInt = nullptr;
|
||||
}
|
||||
|
||||
void CMainMenu::update()
|
||||
{
|
||||
if(CMM != this) //don't update if you are not a main interface
|
||||
return;
|
||||
|
||||
if(GH.listInt.empty())
|
||||
{
|
||||
GH.pushInt(this);
|
||||
GH.pushInt(menu);
|
||||
menu->switchToTab(0);
|
||||
}
|
||||
|
||||
// Handles mouse and key input
|
||||
GH.updateTime();
|
||||
GH.handleEvents();
|
||||
|
||||
// check for null othervice crash on finishing a campaign
|
||||
// /FIXME: find out why GH.listInt is empty to begin with
|
||||
if(GH.topInt() != nullptr)
|
||||
GH.topInt()->show(screen);
|
||||
}
|
||||
|
||||
void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> * names, ELoadMode loadMode)
|
||||
{
|
||||
CSH->resetStateForLobby(screenType == ESelectionScreen::newGame ? StartInfo::NEW_GAME : StartInfo::LOAD_GAME, names);
|
||||
CSH->screenType = screenType;
|
||||
CSH->loadMode = loadMode;
|
||||
|
||||
GH.pushInt(new CSimpleJoinScreen(host));
|
||||
}
|
||||
|
||||
void CMainMenu::openCampaignLobby(const std::string & campaignFileName)
|
||||
{
|
||||
auto ourCampaign = std::make_shared<CCampaignState>(CCampaignHandler::getCampaign(campaignFileName));
|
||||
openCampaignLobby(ourCampaign);
|
||||
}
|
||||
|
||||
void CMainMenu::openCampaignLobby(std::shared_ptr<CCampaignState> campaign)
|
||||
{
|
||||
CSH->resetStateForLobby(StartInfo::CAMPAIGN);
|
||||
CSH->screenType = ESelectionScreen::campaignList;
|
||||
CSH->campaignStateToSend = campaign;
|
||||
GH.pushInt(new CSimpleJoinScreen());
|
||||
}
|
||||
|
||||
void CMainMenu::openCampaignScreen(std::string name)
|
||||
{
|
||||
if(vstd::contains(CMainMenuConfig::get().getCampaigns().Struct(), name))
|
||||
{
|
||||
GH.pushInt(new CCampaignScreen(CMainMenuConfig::get().getCampaigns()[name]));
|
||||
return;
|
||||
}
|
||||
logGlobal->error("Unknown campaign set: %s", name);
|
||||
}
|
||||
|
||||
CMainMenu * CMainMenu::create()
|
||||
{
|
||||
if(!CMM)
|
||||
CMM = new CMainMenu();
|
||||
|
||||
GH.terminate_cond->set(false);
|
||||
return CMM;
|
||||
}
|
||||
|
||||
void CMainMenu::removeFromGui()
|
||||
{
|
||||
//remove everything but main menu and background
|
||||
GH.popInts(GH.listInt.size() - 2);
|
||||
GH.popInt(GH.topInt()); //remove main menu
|
||||
GH.popInt(GH.topInt()); //remove background
|
||||
}
|
||||
|
||||
void CMainMenu::showLoadingScreen(std::function<void()> loader)
|
||||
{
|
||||
if(GH.listInt.size() && GH.listInt.front() == CMM)
|
||||
CMM->removeFromGui();
|
||||
GH.pushInt(new CLoadingScreen(loader));
|
||||
}
|
||||
|
||||
std::shared_ptr<CPicture> CMainMenu::createPicture(const JsonNode & config)
|
||||
{
|
||||
return std::make_shared<CPicture>(config["name"].String(), config["x"].Float(), config["y"].Float());
|
||||
}
|
||||
|
||||
CMultiMode::CMultiMode(ESelectionScreen ScreenType)
|
||||
: screenType(ScreenType)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
background = std::make_shared<CPicture>("MUPOPUP.bmp");
|
||||
background->convertToScreenBPP(); //so we could draw without problems
|
||||
blitAt(CPicture("MUMAP.bmp"), 16, 77, *background);
|
||||
pos = background->center(); //center, window has size of bg graphic
|
||||
|
||||
statusBar = std::make_shared<CGStatusBar>(new CPicture(Rect(7, 465, 440, 18), 0)); //226, 472
|
||||
playerName = std::make_shared<CTextInput>(Rect(19, 436, 334, 16), *background);
|
||||
playerName->setText(settings["general"]["playerName"].String());
|
||||
playerName->cb += std::bind(&CMultiMode::onNameChange, this, _1);
|
||||
|
||||
buttonHotseat = std::make_shared<CButton>(Point(373, 78), "MUBHOT.DEF", CGI->generaltexth->zelp[266], std::bind(&CMultiMode::hostTCP, this));
|
||||
buttonHost = std::make_shared<CButton>(Point(373, 78 + 57 * 1), "MUBHOST.DEF", CButton::tooltip("Host TCP/IP game", ""), std::bind(&CMultiMode::hostTCP, this));
|
||||
buttonJoin = std::make_shared<CButton>(Point(373, 78 + 57 * 2), "MUBJOIN.DEF", CButton::tooltip("Join TCP/IP game", ""), std::bind(&CMultiMode::joinTCP, this));
|
||||
buttonCancel = std::make_shared<CButton>(Point(373, 424), "MUBCANC.DEF", CGI->generaltexth->zelp[288], [&]() { GH.popIntTotally(this);}, SDLK_ESCAPE);
|
||||
}
|
||||
|
||||
void CMultiMode::hostTCP()
|
||||
{
|
||||
GH.popIntTotally(this);
|
||||
GH.pushInt(new CMultiPlayers(settings["general"]["playerName"].String(), screenType, true, ELoadMode::MULTI));
|
||||
}
|
||||
|
||||
void CMultiMode::joinTCP()
|
||||
{
|
||||
GH.popIntTotally(this);
|
||||
GH.pushInt(new CMultiPlayers(settings["general"]["playerName"].String(), screenType, false, ELoadMode::MULTI));
|
||||
}
|
||||
|
||||
void CMultiMode::onNameChange(std::string newText)
|
||||
{
|
||||
Settings name = settings.write["general"]["playerName"];
|
||||
name->String() = newText;
|
||||
}
|
||||
|
||||
CMultiPlayers::CMultiPlayers(const std::string & firstPlayer, ESelectionScreen ScreenType, bool Host, ELoadMode LoadMode)
|
||||
: loadMode(LoadMode), screenType(ScreenType), host(Host)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
background = std::make_shared<CPicture>("MUHOTSEA.bmp");
|
||||
pos = background->center(); //center, window has size of bg graphic
|
||||
|
||||
std::string text = CGI->generaltexth->allTexts[446];
|
||||
boost::replace_all(text, "\t", "\n");
|
||||
textTitle = std::make_shared<CTextBox>(text, Rect(25, 20, 315, 50), 0, FONT_BIG, CENTER, Colors::WHITE); //HOTSEAT Please enter names
|
||||
|
||||
for(int i = 0; i < inputNames.size(); i++)
|
||||
{
|
||||
inputNames[i] = std::make_shared<CTextInput>(Rect(60, 85 + i * 30, 280, 16), *background);
|
||||
inputNames[i]->cb += std::bind(&CMultiPlayers::onChange, this, _1);
|
||||
}
|
||||
|
||||
buttonOk = std::make_shared<CButton>(Point(95, 338), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CMultiPlayers::enterSelectionScreen, this), SDLK_RETURN);
|
||||
buttonCancel = std::make_shared<CButton>(Point(205, 338), "MUBCANC.DEF", CGI->generaltexth->zelp[561], std::bind(&CGuiHandler::popIntTotally, std::ref(GH), this), SDLK_ESCAPE);
|
||||
statusBar = std::make_shared<CGStatusBar>(new CPicture(Rect(7, 381, 348, 18), 0)); //226, 472
|
||||
|
||||
inputNames[0]->setText(firstPlayer, true);
|
||||
inputNames[0]->giveFocus();
|
||||
}
|
||||
|
||||
void CMultiPlayers::onChange(std::string newText)
|
||||
{
|
||||
size_t namesCount = 0;
|
||||
|
||||
for(auto & elem : inputNames)
|
||||
if(!elem->text.empty())
|
||||
namesCount++;
|
||||
}
|
||||
|
||||
void CMultiPlayers::enterSelectionScreen()
|
||||
{
|
||||
std::vector<std::string> names;
|
||||
for(auto name : inputNames)
|
||||
{
|
||||
if(name->text.length())
|
||||
names.push_back(name->text);
|
||||
}
|
||||
|
||||
Settings name = settings.write["general"]["playerName"];
|
||||
name->String() = names[0];
|
||||
|
||||
CMainMenu::openLobby(screenType, host, &names, loadMode);
|
||||
}
|
||||
|
||||
CSimpleJoinScreen::CSimpleJoinScreen(bool host)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
background = std::make_shared<CPicture>("MUDIALOG.bmp"); // address background
|
||||
pos = background->center(); //center, window has size of bg graphic (x,y = 396,278 w=232 h=212)
|
||||
|
||||
textTitle = std::make_shared<CTextBox>("", Rect(20, 20, 205, 50), 0, FONT_BIG, CENTER, Colors::WHITE);
|
||||
inputAddress = std::make_shared<CTextInput>(Rect(25, 68, 175, 16), *background.get());
|
||||
inputPort = std::make_shared<CTextInput>(Rect(25, 115, 175, 16), *background.get());
|
||||
if(host && !settings["session"]["donotstartserver"].Bool())
|
||||
{
|
||||
textTitle->setText("Connecting...");
|
||||
boost::thread(&CSimpleJoinScreen::connectThread, this, "", 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
textTitle->setText("Enter address:");
|
||||
inputAddress->cb += std::bind(&CSimpleJoinScreen::onChange, this, _1);
|
||||
inputPort->cb += std::bind(&CSimpleJoinScreen::onChange, this, _1);
|
||||
inputPort->filters += std::bind(&CTextInput::numberFilter, _1, _2, 0, 65535);
|
||||
buttonOk = std::make_shared<CButton>(Point(26, 142), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CSimpleJoinScreen::connectToServer, this), SDLK_RETURN);
|
||||
|
||||
inputAddress->giveFocus();
|
||||
}
|
||||
inputAddress->setText(settings["server"]["server"].String(), true);
|
||||
inputPort->setText(CServerHandler::getDefaultPortStr(), true);
|
||||
|
||||
buttonCancel = std::make_shared<CButton>(Point(142, 142), "MUBCANC.DEF", CGI->generaltexth->zelp[561], std::bind(&CSimpleJoinScreen::leaveScreen, this), SDLK_ESCAPE);
|
||||
statusBar = std::make_shared<CGStatusBar>(new CPicture(Rect(7, 186, 218, 18), 0));
|
||||
}
|
||||
|
||||
void CSimpleJoinScreen::connectToServer()
|
||||
{
|
||||
textTitle->setText("Connecting...");
|
||||
buttonOk->block(true);
|
||||
|
||||
boost::thread(&CSimpleJoinScreen::connectThread, this, inputAddress->text, boost::lexical_cast<ui16>(inputPort->text));
|
||||
}
|
||||
|
||||
void CSimpleJoinScreen::leaveScreen()
|
||||
{
|
||||
if(CSH->state == EClientState::CONNECTING)
|
||||
{
|
||||
textTitle->setText("Closing...");
|
||||
CSH->state = EClientState::CONNECTION_CANCELLED;
|
||||
}
|
||||
else if(GH.listInt.size() && GH.listInt.front() == this)
|
||||
{
|
||||
GH.popIntTotally(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CSimpleJoinScreen::onChange(const std::string & newText)
|
||||
{
|
||||
buttonOk->block(inputAddress->text.empty() || inputPort->text.empty());
|
||||
}
|
||||
|
||||
void CSimpleJoinScreen::connectThread(const std::string addr, const ui16 port)
|
||||
{
|
||||
setThreadName("CSimpleJoinScreen::connectThread");
|
||||
if(!addr.length())
|
||||
CSH->startLocalServerAndConnect();
|
||||
else
|
||||
CSH->justConnectToServer(addr, port);
|
||||
|
||||
if(GH.listInt.size() && GH.listInt.front() == this)
|
||||
{
|
||||
GH.popIntTotally(this);
|
||||
}
|
||||
}
|
||||
|
||||
CLoadingScreen::CLoadingScreen(std::function<void()> loader)
|
||||
: CWindowObject(BORDERED, getBackground()), loadingThread(loader)
|
||||
{
|
||||
CCS->musich->stopMusic(5000);
|
||||
}
|
||||
|
||||
CLoadingScreen::~CLoadingScreen()
|
||||
{
|
||||
loadingThread.join();
|
||||
}
|
||||
|
||||
void CLoadingScreen::showAll(SDL_Surface * to)
|
||||
{
|
||||
Rect rect(0, 0, to->w, to->h);
|
||||
SDL_FillRect(to, &rect, 0);
|
||||
|
||||
CWindowObject::showAll(to);
|
||||
}
|
||||
|
||||
std::string CLoadingScreen::getBackground()
|
||||
{
|
||||
const auto & conf = CMainMenuConfig::get().getConfig()["loading"].Vector();
|
||||
|
||||
if(conf.empty())
|
||||
{
|
||||
return "loadbar";
|
||||
}
|
||||
else
|
||||
{
|
||||
return RandomGeneratorUtil::nextItem(conf, CRandomGenerator::getDefault())->String();
|
||||
}
|
||||
}
|
183
client/mainmenu/CMainMenu.h
Normal file
183
client/mainmenu/CMainMenu.h
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* CMainMenu.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../windows/CWindowObject.h"
|
||||
#include "../../lib/JsonNode.h"
|
||||
|
||||
class CCampaignState;
|
||||
class CTextInput;
|
||||
class CGStatusBar;
|
||||
class CTextBox;
|
||||
class CTabbedInt;
|
||||
class CAnimation;
|
||||
class CButton;
|
||||
class CFilledTexture;
|
||||
|
||||
|
||||
// TODO: Find new location for these enums
|
||||
enum ESelectionScreen : ui8 {
|
||||
unknown = 0, newGame, loadGame, saveGame, scenarioInfo, campaignList
|
||||
};
|
||||
|
||||
enum ELoadMode : ui8
|
||||
{
|
||||
NONE = 0, SINGLE, MULTI, CAMPAIGN
|
||||
};
|
||||
|
||||
/// The main menu screens listed in the EState enum
|
||||
class CMenuScreen : public CWindowObject
|
||||
{
|
||||
const JsonNode & config;
|
||||
|
||||
std::shared_ptr<CTabbedInt> tabs;
|
||||
|
||||
std::shared_ptr<CPicture> background;
|
||||
std::vector<std::shared_ptr<CPicture>> images;
|
||||
|
||||
CIntObject * createTab(size_t index);
|
||||
|
||||
public:
|
||||
std::vector<std::string> menuNameToEntry;
|
||||
|
||||
CMenuScreen(const JsonNode & configNode);
|
||||
|
||||
void show(SDL_Surface * to) override;
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
void switchToTab(size_t index);
|
||||
};
|
||||
|
||||
class CMenuEntry : public CIntObject
|
||||
{
|
||||
std::vector<std::shared_ptr<CPicture>> images;
|
||||
std::vector<std::shared_ptr<CButton>> buttons;
|
||||
|
||||
std::shared_ptr<CButton> createButton(CMenuScreen * parent, const JsonNode & button);
|
||||
|
||||
public:
|
||||
CMenuEntry(CMenuScreen * parent, const JsonNode & config);
|
||||
};
|
||||
|
||||
/// Multiplayer mode
|
||||
class CMultiMode : public CIntObject
|
||||
{
|
||||
public:
|
||||
ESelectionScreen screenType;
|
||||
std::shared_ptr<CPicture> background;
|
||||
std::shared_ptr<CTextInput> playerName;
|
||||
std::shared_ptr<CButton> buttonHotseat;
|
||||
std::shared_ptr<CButton> buttonHost;
|
||||
std::shared_ptr<CButton> buttonJoin;
|
||||
std::shared_ptr<CButton> buttonCancel;
|
||||
std::shared_ptr<CGStatusBar> statusBar;
|
||||
|
||||
CMultiMode(ESelectionScreen ScreenType);
|
||||
void hostTCP();
|
||||
void joinTCP();
|
||||
|
||||
void onNameChange(std::string newText);
|
||||
};
|
||||
|
||||
/// Hot seat player window
|
||||
class CMultiPlayers : public CIntObject
|
||||
{
|
||||
bool host;
|
||||
ELoadMode loadMode;
|
||||
ESelectionScreen screenType;
|
||||
std::shared_ptr<CPicture> background;
|
||||
std::shared_ptr<CTextBox> textTitle;
|
||||
std::array<std::shared_ptr<CTextInput>, 8> inputNames;
|
||||
std::shared_ptr<CButton> buttonOk;
|
||||
std::shared_ptr<CButton> buttonCancel;
|
||||
std::shared_ptr<CGStatusBar> statusBar;
|
||||
|
||||
void onChange(std::string newText);
|
||||
void enterSelectionScreen();
|
||||
|
||||
public:
|
||||
CMultiPlayers(const std::string & firstPlayer, ESelectionScreen ScreenType, bool Host, ELoadMode LoadMode);
|
||||
};
|
||||
|
||||
/// Manages the configuration of pregame GUI elements like campaign screen, main menu, loading screen,...
|
||||
class CMainMenuConfig
|
||||
{
|
||||
public:
|
||||
static CMainMenuConfig & get();
|
||||
const JsonNode & getConfig() const;
|
||||
const JsonNode & getCampaigns() const;
|
||||
|
||||
private:
|
||||
CMainMenuConfig();
|
||||
|
||||
const JsonNode campaignSets;
|
||||
const JsonNode config;
|
||||
};
|
||||
|
||||
/// Handles background screen, loads graphics for victory/loss condition and random town or hero selection
|
||||
class CMainMenu : public CIntObject, public IUpdateable
|
||||
{
|
||||
std::shared_ptr<CFilledTexture> backgroundAroundMenu;
|
||||
|
||||
CMainMenu(); //Use CMainMenu::create
|
||||
|
||||
public:
|
||||
CMenuScreen * menu;
|
||||
|
||||
~CMainMenu();
|
||||
void update() override;
|
||||
static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> * names, ELoadMode loadMode);
|
||||
static void openCampaignLobby(const std::string & campaignFileName);
|
||||
static void openCampaignLobby(std::shared_ptr<CCampaignState> campaign);
|
||||
void openCampaignScreen(std::string name);
|
||||
|
||||
static CMainMenu * create();
|
||||
void removeFromGui();
|
||||
static void showLoadingScreen(std::function<void()> loader);
|
||||
|
||||
static std::shared_ptr<CPicture> createPicture(const JsonNode & config);
|
||||
|
||||
};
|
||||
|
||||
/// Simple window to enter the server's address.
|
||||
class CSimpleJoinScreen : public CIntObject
|
||||
{
|
||||
std::shared_ptr<CPicture> background;
|
||||
std::shared_ptr<CTextBox> textTitle;
|
||||
std::shared_ptr<CButton> buttonOk;
|
||||
std::shared_ptr<CButton> buttonCancel;
|
||||
std::shared_ptr<CGStatusBar> statusBar;
|
||||
std::shared_ptr<CTextInput> inputAddress;
|
||||
std::shared_ptr<CTextInput> inputPort;
|
||||
|
||||
void connectToServer();
|
||||
void leaveScreen();
|
||||
void onChange(const std::string & newText);
|
||||
void connectThread(const std::string addr = "", const ui16 inputPort = 0);
|
||||
|
||||
public:
|
||||
CSimpleJoinScreen(bool host = true);
|
||||
};
|
||||
|
||||
class CLoadingScreen : public CWindowObject
|
||||
{
|
||||
boost::thread loadingThread;
|
||||
|
||||
std::string getBackground();
|
||||
|
||||
public:
|
||||
CLoadingScreen(std::function<void()> loader);
|
||||
~CLoadingScreen();
|
||||
|
||||
void showAll(SDL_Surface * to) override;
|
||||
};
|
||||
|
||||
extern CMainMenu * CMM;
|
64
client/mainmenu/CPrologEpilogVideo.cpp
Normal file
64
client/mainmenu/CPrologEpilogVideo.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* CPrologEpilogVideo.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CPrologEpilogVideo.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CVideoHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
|
||||
#include "../../lib/mapping/CCampaignHandler.h"
|
||||
|
||||
|
||||
CPrologEpilogVideo::CPrologEpilogVideo(CCampaignScenario::SScenarioPrologEpilog _spe, std::function<void()> callback)
|
||||
: CWindowObject(BORDERED), spe(_spe), positionCounter(0), voiceSoundHandle(-1), exitCb(callback)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
addUsedEvents(LCLICK);
|
||||
pos = center(Rect(0, 0, 800, 600));
|
||||
updateShadow();
|
||||
|
||||
CCS->videoh->open(CCampaignHandler::prologVideoName(spe.prologVideo));
|
||||
CCS->musich->playMusic("Music/" + CCampaignHandler::prologMusicName(spe.prologMusic), true);
|
||||
// MPTODO: Custom campaign crashing on this?
|
||||
// voiceSoundHandle = CCS->soundh->playSound(CCampaignHandler::prologVoiceName(spe.prologVideo));
|
||||
|
||||
text = std::make_shared<CMultiLineLabel>(Rect(100, 500, 600, 100), EFonts::FONT_BIG, CENTER, Colors::METALLIC_GOLD, spe.prologText);
|
||||
text->scrollTextTo(-100);
|
||||
}
|
||||
|
||||
void CPrologEpilogVideo::show(SDL_Surface * to)
|
||||
{
|
||||
CSDL_Ext::fillRectBlack(to, &pos);
|
||||
//BUG: some videos are 800x600 in size while some are 800x400
|
||||
//VCMI should center them in the middle of the screen. Possible but needs modification
|
||||
//of video player API which I'd like to avoid until we'll get rid of Windows-specific player
|
||||
CCS->videoh->update(pos.x, pos.y, to, true, false);
|
||||
|
||||
//move text every 5 calls/frames; seems to be good enough
|
||||
++positionCounter;
|
||||
if(positionCounter % 5 == 0)
|
||||
text->scrollTextBy(1);
|
||||
else
|
||||
text->showAll(to); // blit text over video, if needed
|
||||
|
||||
if(text->textSize.y + 100 < positionCounter / 5)
|
||||
clickLeft(false, false);
|
||||
}
|
||||
|
||||
void CPrologEpilogVideo::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
GH.popInt(this);
|
||||
CCS->soundh->stopSound(voiceSoundHandle);
|
||||
exitCb();
|
||||
}
|
31
client/mainmenu/CPrologEpilogVideo.h
Normal file
31
client/mainmenu/CPrologEpilogVideo.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* CPrologEpilogVideo.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include "../windows/CWindowObject.h"
|
||||
#include "../../lib/mapping/CCampaignHandler.h"
|
||||
|
||||
class CMultiLineLabel;
|
||||
class SDL_Surface;
|
||||
|
||||
class CPrologEpilogVideo : public CWindowObject
|
||||
{
|
||||
CCampaignScenario::SScenarioPrologEpilog spe;
|
||||
int positionCounter;
|
||||
int voiceSoundHandle;
|
||||
std::function<void()> exitCb;
|
||||
|
||||
std::shared_ptr<CMultiLineLabel> text;
|
||||
|
||||
public:
|
||||
CPrologEpilogVideo(CCampaignScenario::SScenarioPrologEpilog _spe, std::function<void()> callback);
|
||||
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void show(SDL_Surface * to) override;
|
||||
};
|
59
client/mainmenu/CreditsScreen.cpp
Normal file
59
client/mainmenu/CreditsScreen.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* CreditsScreen.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CreditsScreen.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
|
||||
CreditsScreen::CreditsScreen()
|
||||
: positionCounter(0)
|
||||
{
|
||||
addUsedEvents(LCLICK | RCLICK);
|
||||
type |= REDRAW_PARENT;
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
pos.w = CMM->menu->pos.w;
|
||||
pos.h = CMM->menu->pos.h;
|
||||
auto textFile = CResourceHandler::get()->load(ResourceID("DATA/CREDITS.TXT"))->readAll();
|
||||
std::string text((char *)textFile.first.get(), textFile.second);
|
||||
size_t firstQuote = text.find('\"') + 1;
|
||||
text = text.substr(firstQuote, text.find('\"', firstQuote) - firstQuote);
|
||||
credits = std::make_shared<CMultiLineLabel>(Rect(pos.w - 350, 0, 350, 600), FONT_CREDITS, CENTER, Colors::WHITE, text);
|
||||
credits->scrollTextTo(-600); // move all text below the screen
|
||||
}
|
||||
|
||||
void CreditsScreen::show(SDL_Surface * to)
|
||||
{
|
||||
CIntObject::show(to);
|
||||
positionCounter++;
|
||||
if(positionCounter % 2 == 0)
|
||||
credits->scrollTextBy(1);
|
||||
|
||||
//end of credits, close this screen
|
||||
if(credits->textSize.y + 600 < positionCounter / 2)
|
||||
clickRight(false, false);
|
||||
}
|
||||
|
||||
void CreditsScreen::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
clickRight(down, previousState);
|
||||
}
|
||||
|
||||
void CreditsScreen::clickRight(tribool down, bool previousState)
|
||||
{
|
||||
CTabbedInt * menu = dynamic_cast<CTabbedInt *>(parent);
|
||||
assert(menu);
|
||||
menu->setActive(0);
|
||||
}
|
27
client/mainmenu/CreditsScreen.h
Normal file
27
client/mainmenu/CreditsScreen.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* CreditsScreen.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../windows/CWindowObject.h"
|
||||
|
||||
class CMultiLineLabel;
|
||||
class SDL_Surface;
|
||||
|
||||
class CreditsScreen : public CIntObject
|
||||
{
|
||||
int positionCounter;
|
||||
std::shared_ptr<CMultiLineLabel> credits;
|
||||
|
||||
public:
|
||||
CreditsScreen();
|
||||
void show(SDL_Surface * to) override;
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
void clickRight(tribool down, bool previousState) override;
|
||||
};
|
@ -18,7 +18,7 @@
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CPreGame.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
#include "../Graphics.h"
|
||||
#include "../CMessage.h"
|
||||
|
||||
|
@ -63,6 +63,11 @@ std::string CLabel::getText()
|
||||
return text;
|
||||
}
|
||||
|
||||
void CLabel::setAutoRedraw(bool value)
|
||||
{
|
||||
autoRedraw = value;
|
||||
}
|
||||
|
||||
void CLabel::setText(const std::string &Txt)
|
||||
{
|
||||
text = Txt;
|
||||
@ -75,6 +80,23 @@ void CLabel::setText(const std::string &Txt)
|
||||
}
|
||||
}
|
||||
|
||||
void CLabel::setColor(const SDL_Color & Color)
|
||||
{
|
||||
color = Color;
|
||||
if(autoRedraw)
|
||||
{
|
||||
if(bg || !parent)
|
||||
redraw();
|
||||
else
|
||||
parent->redraw();
|
||||
}
|
||||
}
|
||||
|
||||
size_t CLabel::getWidth()
|
||||
{
|
||||
return graphics->fonts[font]->getStringWidth(visibleText());;
|
||||
}
|
||||
|
||||
CMultiLineLabel::CMultiLineLabel(Rect position, EFonts Font, EAlignment Align, const SDL_Color &Color, const std::string &Text):
|
||||
CLabel(position.x, position.y, Font, Align, Color, Text),
|
||||
visibleSize(0, 0, position.w, position.h)
|
||||
@ -244,7 +266,12 @@ CLabelGroup::CLabelGroup(EFonts Font, EAlignment Align, const SDL_Color &Color):
|
||||
void CLabelGroup::add(int x, int y, const std::string &text)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL;
|
||||
new CLabel(x, y, font, align, color, text);
|
||||
labels.push_back(new CLabel(x, y, font, align, color, text));
|
||||
}
|
||||
|
||||
size_t CLabelGroup::currentSize() const
|
||||
{
|
||||
return labels.size();
|
||||
}
|
||||
|
||||
CTextBox::CTextBox(std::string Text, const Rect &rect, int SliderStyle, EFonts Font, EAlignment Align, const SDL_Color &Color):
|
||||
|
@ -47,7 +47,10 @@ public:
|
||||
bool autoRedraw; //whether control will redraw itself on setTxt
|
||||
|
||||
std::string getText();
|
||||
virtual void setAutoRedraw(bool option);
|
||||
virtual void setText(const std::string &Txt);
|
||||
virtual void setColor(const SDL_Color & Color);
|
||||
size_t getWidth();
|
||||
|
||||
CLabel(int x=0, int y=0, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT,
|
||||
const SDL_Color &Color = Colors::WHITE, const std::string &Text = "");
|
||||
@ -64,6 +67,7 @@ class CLabelGroup : public CIntObject
|
||||
public:
|
||||
CLabelGroup(EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE);
|
||||
void add(int x=0, int y=0, const std::string &text = "");
|
||||
size_t currentSize() const;
|
||||
};
|
||||
|
||||
/// Multi-line label that can display multiple lines of text
|
||||
|
@ -22,7 +22,10 @@
|
||||
#include "../CMessage.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CPreGame.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
#include "../lobby/CBonusSelection.h"
|
||||
#include "../lobby/CSavingScreen.h"
|
||||
#include "../lobby/CScenarioInfoScreen.h"
|
||||
#include "../Graphics.h"
|
||||
#include "../mapHandler.h"
|
||||
|
||||
@ -47,6 +50,7 @@
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
#include "../../lib/UnlockGuard.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "../../lib/StartInfo.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable : 4355)
|
||||
@ -935,7 +939,8 @@ void CAdvMapInt::activate()
|
||||
}
|
||||
minimap.activate();
|
||||
terrain.activate();
|
||||
LOCPLINT->cingconsole->activate();
|
||||
if(LOCPLINT)
|
||||
LOCPLINT->cingconsole->activate();
|
||||
|
||||
GH.fakeMouseMove(); //to restore the cursor
|
||||
}
|
||||
@ -1215,7 +1220,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
|
||||
return;
|
||||
case SDLK_s:
|
||||
if(isActive() && key.type == SDL_KEYUP)
|
||||
GH.pushInt(new CSavingScreen(CPlayerInterface::howManyPeople > 1));
|
||||
GH.pushInt(new CSavingScreen());
|
||||
return;
|
||||
case SDLK_d:
|
||||
{
|
||||
@ -1235,7 +1240,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
|
||||
if(isActive() && LOCPLINT->ctrlPressed())
|
||||
{
|
||||
LOCPLINT->showYesNoDialog("Are you sure you want to restart game?",
|
||||
[](){ LOCPLINT->sendCustomEvent(RESTART_GAME); },
|
||||
[](){ LOCPLINT->sendCustomEvent(EUserEvent::RESTART_GAME); },
|
||||
[](){}, true);
|
||||
}
|
||||
return;
|
||||
@ -1947,14 +1952,13 @@ CAdventureOptions::CAdventureOptions():
|
||||
|
||||
void CAdventureOptions::showScenarioInfo()
|
||||
{
|
||||
auto campState = LOCPLINT->cb->getStartInfo()->campState;
|
||||
if(campState)
|
||||
if(LOCPLINT->cb->getStartInfo()->campState)
|
||||
{
|
||||
GH.pushInt(new CBonusSelection(campState));
|
||||
GH.pushInt(new CBonusSelection());
|
||||
}
|
||||
else
|
||||
{
|
||||
GH.pushInt(new CScenarioInfo(LOCPLINT->cb->getMapHeader(), LOCPLINT->cb->getStartInfo()));
|
||||
GH.pushInt(new CScenarioInfoScreen());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,10 +21,10 @@
|
||||
#include "../CMessage.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CPreGame.h"
|
||||
#include "../CVideoHandler.h"
|
||||
#include "../Graphics.h"
|
||||
#include "../mapHandler.h"
|
||||
#include "../CServerHandler.h"
|
||||
|
||||
#include "../battle/CBattleInterfaceClasses.h"
|
||||
#include "../battle/CBattleInterface.h"
|
||||
@ -38,6 +38,8 @@
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
|
||||
#include "../lobby/CSavingScreen.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||
@ -487,6 +489,13 @@ CSystemOptionsWindow::CSystemOptionsWindow():
|
||||
restart = new CButton (Point(246, 357), "SORSTRT", CGI->generaltexth->zelp[323], [&](){ brestartf(); }, SDLK_r);
|
||||
restart->setImageOrder(1, 0, 2, 3);
|
||||
|
||||
if(CSH->isGuest())
|
||||
{
|
||||
load->block(true);
|
||||
save->block(true);
|
||||
restart->block(true);
|
||||
}
|
||||
|
||||
mainMenu = new CButton (Point(357, 357), "SOMAIN.DEF", CGI->generaltexth->zelp[320], [&](){ bmainmenuf(); }, SDLK_m);
|
||||
mainMenu->setImageOrder(1, 0, 2, 3);
|
||||
|
||||
@ -593,7 +602,7 @@ void CSystemOptionsWindow::setGameRes(int index)
|
||||
|
||||
void CSystemOptionsWindow::bquitf()
|
||||
{
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], [this](){ closeAndPushEvent(SDL_USEREVENT, FORCE_QUIT); }, 0);
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], [this](){ closeAndPushEvent(SDL_USEREVENT, EUserEvent::FORCE_QUIT); }, 0);
|
||||
}
|
||||
|
||||
void CSystemOptionsWindow::breturnf()
|
||||
@ -603,7 +612,7 @@ void CSystemOptionsWindow::breturnf()
|
||||
|
||||
void CSystemOptionsWindow::bmainmenuf()
|
||||
{
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], [this](){ closeAndPushEvent(SDL_USEREVENT, RETURN_TO_MAIN_MENU); }, 0);
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], [this](){ closeAndPushEvent(SDL_USEREVENT, EUserEvent::RETURN_TO_MAIN_MENU); }, 0);
|
||||
}
|
||||
|
||||
void CSystemOptionsWindow::bloadf()
|
||||
@ -615,12 +624,12 @@ void CSystemOptionsWindow::bloadf()
|
||||
void CSystemOptionsWindow::bsavef()
|
||||
{
|
||||
GH.popIntTotally(this);
|
||||
GH.pushInt(new CSavingScreen(CPlayerInterface::howManyPeople > 1));
|
||||
GH.pushInt(new CSavingScreen());
|
||||
}
|
||||
|
||||
void CSystemOptionsWindow::brestartf()
|
||||
{
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [this](){ closeAndPushEvent(SDL_USEREVENT, RESTART_GAME); }, 0);
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [this](){ closeAndPushEvent(SDL_USEREVENT, EUserEvent::RESTART_GAME); }, 0);
|
||||
}
|
||||
|
||||
void CSystemOptionsWindow::closeAndPushEvent(int eventType, int code)
|
||||
|
@ -46,6 +46,18 @@
|
||||
"saveRandomMaps" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
},
|
||||
"lastMap" : {
|
||||
"type":"string",
|
||||
"default" : "Maps/Arrogance"
|
||||
},
|
||||
"lastSave" : {
|
||||
"type":"string",
|
||||
"default" : "NEWGAME"
|
||||
},
|
||||
"lastCampaign" : {
|
||||
"type":"string",
|
||||
"default" : ""
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -64,8 +64,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static CApplier<CBaseForGSApply> *applierGs = nullptr;
|
||||
|
||||
void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst) const
|
||||
{
|
||||
int type = txt.first, ser = txt.second;
|
||||
@ -673,9 +671,9 @@ int CGameState::getDate(Date::EDateType mode) const
|
||||
CGameState::CGameState()
|
||||
{
|
||||
gs = this;
|
||||
applierGs = new CApplier<CBaseForGSApply>();
|
||||
registerTypesClientPacks1(*applierGs);
|
||||
registerTypesClientPacks2(*applierGs);
|
||||
applier = std::make_shared<CApplier<CBaseForGSApply>>();
|
||||
registerTypesClientPacks1(*applier);
|
||||
registerTypesClientPacks2(*applier);
|
||||
//objCaller = new CObjectCallersHandler();
|
||||
globalEffects.setDescription("Global effects");
|
||||
globalEffects.setNodeType(CBonusSystemNode::GLOBAL_EFFECTS);
|
||||
@ -686,10 +684,6 @@ CGameState::~CGameState()
|
||||
{
|
||||
map.dellNull();
|
||||
curB.dellNull();
|
||||
//delete scenarioOps; //TODO: fix for loading ind delete
|
||||
//delete initialOpts;
|
||||
delete applierGs;
|
||||
//delete objCaller;
|
||||
|
||||
for(auto ptr : hpool.heroesPool) // clean hero pool
|
||||
ptr.second.dellNull();
|
||||
@ -709,7 +703,7 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, bool allow
|
||||
initNewGame(mapService, allowSavingRandomMap);
|
||||
break;
|
||||
case StartInfo::CAMPAIGN:
|
||||
initCampaign(mapService);
|
||||
initCampaign();
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("Wrong mode: %d", static_cast<int>(scenarioOps->mode));
|
||||
@ -765,6 +759,13 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, bool allow
|
||||
}
|
||||
}
|
||||
|
||||
void CGameState::updateOnLoad(StartInfo * si)
|
||||
{
|
||||
scenarioOps->playerInfos = si->playerInfos;
|
||||
for(auto & i : si->playerInfos)
|
||||
gs->players[i.first].human = i.second.isControlledByHuman();
|
||||
}
|
||||
|
||||
void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRandomMap)
|
||||
{
|
||||
if(scenarioOps->createRandomMap())
|
||||
@ -815,7 +816,7 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
|
||||
playerSettings.compOnly = !playerInfo.canHumanPlay;
|
||||
playerSettings.team = playerInfo.team;
|
||||
playerSettings.castle = playerInfo.defaultCastle();
|
||||
if(playerSettings.playerID == PlayerSettings::PLAYER_AI && playerSettings.name.empty())
|
||||
if(playerSettings.isControlledByAI() && playerSettings.name.empty())
|
||||
{
|
||||
playerSettings.name = VLC->generaltexth->allTexts[468];
|
||||
}
|
||||
@ -837,19 +838,10 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
|
||||
}
|
||||
}
|
||||
|
||||
void CGameState::initCampaign(const IMapService * mapService)
|
||||
void CGameState::initCampaign()
|
||||
{
|
||||
logGlobal->info("Open campaign map file: %d", scenarioOps->campState->currentMap.get());
|
||||
auto campaign = scenarioOps->campState;
|
||||
assert(vstd::contains(campaign->camp->mapPieces, *scenarioOps->campState->currentMap));
|
||||
|
||||
std::string scenarioName = scenarioOps->mapname.substr(0, scenarioOps->mapname.find('.'));
|
||||
boost::to_lower(scenarioName);
|
||||
scenarioName += ':' + boost::lexical_cast<std::string>(*campaign->currentMap);
|
||||
|
||||
std::string & mapContent = campaign->camp->mapPieces[*campaign->currentMap];
|
||||
auto buffer = reinterpret_cast<const ui8 *>(mapContent.data());
|
||||
map = mapService->loadMap(buffer, mapContent.size(), scenarioName).release();
|
||||
map = scenarioOps->campState->getMap();
|
||||
}
|
||||
|
||||
void CGameState::checkMapChecksum()
|
||||
@ -962,13 +954,11 @@ void CGameState::initPlayerStates()
|
||||
for(auto & elem : scenarioOps->playerInfos)
|
||||
{
|
||||
PlayerState & p = players[elem.first];
|
||||
//std::pair<PlayerColor, PlayerState> ins(elem.first,PlayerState());
|
||||
p.color=elem.first;
|
||||
p.human = elem.second.playerID;
|
||||
p.human = elem.second.isControlledByHuman();
|
||||
p.team = map->players[elem.first.getNum()].team;
|
||||
teams[p.team].id = p.team;//init team
|
||||
teams[p.team].players.insert(elem.first);//add player to team
|
||||
//players.insert(ins);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1091,13 +1081,26 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
|
||||
auto bonus = campaignState->getBonusForCurrentMap();
|
||||
if (bonus && bonus->type == CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO)
|
||||
{
|
||||
crossoverHeroes.heroesFromAnyPreviousScenarios = crossoverHeroes.heroesFromPreviousScenario = campaignState->camp->scenarios[bonus->info2].crossoverHeroes;
|
||||
std::vector<CGHeroInstance *> heroes;
|
||||
for(auto & node : campaignState->camp->scenarios[bonus->info2].crossoverHeroes)
|
||||
{
|
||||
auto h = CCampaignState::crossoverDeserialize(node);
|
||||
heroes.push_back(h);
|
||||
}
|
||||
crossoverHeroes.heroesFromAnyPreviousScenarios = crossoverHeroes.heroesFromPreviousScenario = heroes;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!campaignState->mapsConquered.empty())
|
||||
{
|
||||
crossoverHeroes.heroesFromPreviousScenario = campaignState->camp->scenarios[campaignState->mapsConquered.back()].crossoverHeroes;
|
||||
std::vector<CGHeroInstance *> heroes;
|
||||
for(auto & node : campaignState->camp->scenarios[campaignState->mapsConquered.back()].crossoverHeroes)
|
||||
{
|
||||
auto h = CCampaignState::crossoverDeserialize(node);
|
||||
heroes.push_back(h);
|
||||
}
|
||||
crossoverHeroes.heroesFromAnyPreviousScenarios = crossoverHeroes.heroesFromPreviousScenario = heroes;
|
||||
crossoverHeroes.heroesFromPreviousScenario = heroes;
|
||||
|
||||
for(auto mapNr : campaignState->mapsConquered)
|
||||
{
|
||||
@ -1108,15 +1111,22 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
|
||||
// remove heroes which didn't reached the end of the scenario, but were available at the start
|
||||
for(auto hero : lostCrossoverHeroes)
|
||||
{
|
||||
vstd::erase_if(crossoverHeroes.heroesFromAnyPreviousScenarios,
|
||||
CGObjectInstanceBySubIdFinder(hero));
|
||||
// auto hero = CCampaignState::crossoverDeserialize(node);
|
||||
vstd::erase_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h)
|
||||
{
|
||||
return hero->subID == h->subID;
|
||||
});
|
||||
}
|
||||
|
||||
// now add heroes which completed the scenario
|
||||
for(auto hero : scenario.crossoverHeroes)
|
||||
for(auto node : scenario.crossoverHeroes)
|
||||
{
|
||||
auto hero = CCampaignState::crossoverDeserialize(node);
|
||||
// add new heroes and replace old heroes with newer ones
|
||||
auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, CGObjectInstanceBySubIdFinder(hero));
|
||||
auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h)
|
||||
{
|
||||
return hero->subID == h->subID;
|
||||
});
|
||||
if (it != crossoverHeroes.heroesFromAnyPreviousScenarios.end())
|
||||
{
|
||||
// replace old hero with newer one
|
||||
@ -1306,7 +1316,7 @@ void CGameState::initStartingResources()
|
||||
for(auto it = scenarioOps->playerInfos.cbegin();
|
||||
it != scenarioOps->playerInfos.cend(); ++it)
|
||||
{
|
||||
if(it->second.playerID != PlayerSettings::PLAYER_AI)
|
||||
if(it->second.isControlledByHuman())
|
||||
ret.push_back(&it->second);
|
||||
}
|
||||
|
||||
@ -1955,7 +1965,7 @@ PlayerRelations::PlayerRelations CGameState::getPlayerRelations( PlayerColor col
|
||||
void CGameState::apply(CPack *pack)
|
||||
{
|
||||
ui16 typ = typeList.getTypeID(pack);
|
||||
applierGs->getApplier(typ)->applyOnGS(this,pack);
|
||||
applier->getApplier(typ)->applyOnGS(this, pack);
|
||||
}
|
||||
|
||||
void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out)
|
||||
@ -2806,7 +2816,7 @@ void CGameState::replaceHeroesPlaceholders(const std::vector<CGameState::Campaig
|
||||
map->objects[heroToPlace->id.getNum()] = heroToPlace;
|
||||
map->addBlockVisTiles(heroToPlace);
|
||||
|
||||
scenarioOps->campState->getCurrentScenario().placedCrossoverHeroes.push_back(heroToPlace);
|
||||
scenarioOps->campState->getCurrentScenario().placedCrossoverHeroes.push_back(CCampaignState::crossoverSerialize(heroToPlace));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,9 @@ namespace boost
|
||||
class shared_mutex;
|
||||
}
|
||||
|
||||
template<typename T> class CApplier;
|
||||
class CBaseForGSApply;
|
||||
|
||||
struct DLL_LINKAGE SThievesGuildInfo
|
||||
{
|
||||
std::vector<PlayerColor> playerColors; //colors of players that are in-game
|
||||
@ -153,6 +156,7 @@ public:
|
||||
virtual ~CGameState();
|
||||
|
||||
void init(const IMapService * mapService, StartInfo * si, bool allowSavingRandomMap = false);
|
||||
void updateOnLoad(StartInfo * si);
|
||||
|
||||
ConstTransitivePtr<StartInfo> scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized)
|
||||
PlayerColor currentPlayer; //ID of player currently having turn
|
||||
@ -246,7 +250,7 @@ private:
|
||||
// ----- initialization -----
|
||||
|
||||
void initNewGame(const IMapService * mapService, bool allowSavingRandomMap);
|
||||
void initCampaign(const IMapService * mapService);
|
||||
void initCampaign();
|
||||
void checkMapChecksum();
|
||||
void initGrailPosition();
|
||||
void initRandomFactionsForPlayers();
|
||||
@ -291,6 +295,7 @@ private:
|
||||
int pickNextHeroType(PlayerColor owner); // picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly
|
||||
|
||||
// ---- data -----
|
||||
std::shared_ptr<CApplier<CBaseForGSApply>> applier;
|
||||
CRandomGenerator rand;
|
||||
|
||||
friend class CCallback;
|
||||
|
@ -74,7 +74,7 @@ set(lib_SRCS
|
||||
registerTypes/TypesMapObjects1.cpp
|
||||
registerTypes/TypesMapObjects2.cpp
|
||||
registerTypes/TypesMapObjects3.cpp
|
||||
registerTypes/TypesPregamePacks.cpp
|
||||
registerTypes/TypesLobbyPacks.cpp
|
||||
registerTypes/TypesServerPacks.cpp
|
||||
|
||||
rmg/CMapGenerator.cpp
|
||||
@ -152,6 +152,7 @@ set(lib_SRCS
|
||||
JsonNode.cpp
|
||||
LogicalExpression.cpp
|
||||
NetPacksLib.cpp
|
||||
StartInfo.cpp
|
||||
ResourceSet.cpp
|
||||
VCMIDirs.cpp
|
||||
VCMI_Lib.cpp
|
||||
@ -335,6 +336,7 @@ set(lib_HEADERS
|
||||
LogicalExpression.h
|
||||
NetPacksBase.h
|
||||
NetPacks.h
|
||||
NetPacksLobby.h
|
||||
ResourceSet.h
|
||||
ScopeGuard.h
|
||||
StartInfo.h
|
||||
|
@ -51,6 +51,8 @@ namespace GameConstants
|
||||
const ui32 BASE_MOVEMENT_COST = 100; //default cost for non-diagonal movement
|
||||
|
||||
const int HERO_PORTRAIT_SHIFT = 30;// 2 special frames + some extra portraits
|
||||
|
||||
const std::array<int, 11> POSSIBLE_TURNTIME = {1, 2, 4, 6, 8, 10, 15, 20, 25, 30, 0};
|
||||
}
|
||||
|
||||
class CArtifact;
|
||||
|
@ -28,6 +28,9 @@
|
||||
#include "CGameState.h"
|
||||
#include "mapping/CMap.h"
|
||||
#include "CPlayerState.h"
|
||||
#include "CSkillHandler.h"
|
||||
|
||||
#include "serializer/Connection.h"
|
||||
|
||||
void CPrivilegedInfoCallback::getFreeTiles(std::vector<int3> & tiles) const
|
||||
{
|
||||
|
@ -91,7 +91,7 @@ public:
|
||||
virtual void setManaPoints(ObjectInstanceID hid, int val)=0;
|
||||
virtual void giveHero(ObjectInstanceID id, PlayerColor player)=0;
|
||||
virtual void changeObjPos(ObjectInstanceID objid, int3 newPos, ui8 flags)=0;
|
||||
virtual void sendAndApply(CPackForClient * info)=0;
|
||||
virtual void sendAndApply(CPackForClient * pack) = 0;
|
||||
virtual void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)=0; //when two heroes meet on adventure map
|
||||
virtual void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) = 0;
|
||||
virtual void changeFogOfWar(std::unordered_set<int3, ShashInt3> &tiles, PlayerColor player, bool hide) = 0;
|
||||
|
272
lib/NetPacks.h
272
lib/NetPacks.h
@ -22,16 +22,15 @@
|
||||
|
||||
#include "spells/ViewSpellInt.h"
|
||||
|
||||
class CCampaignState;
|
||||
class CClient;
|
||||
class CGameState;
|
||||
class CGameHandler;
|
||||
class CArtifact;
|
||||
class CSelectionScreen;
|
||||
class CGObjectInstance;
|
||||
class CArtifactInstance;
|
||||
struct StackLocation;
|
||||
struct ArtSlotInfo;
|
||||
struct QuestInfo;
|
||||
class CMapInfo;
|
||||
struct StartInfo;
|
||||
class IBattleState;
|
||||
|
||||
struct Query : public CPackForClient
|
||||
@ -438,18 +437,6 @@ struct RemoveBonus : public CPackForClient
|
||||
}
|
||||
};
|
||||
|
||||
struct UpdateCampaignState : public CPackForClient
|
||||
{
|
||||
UpdateCampaignState(){}
|
||||
std::shared_ptr<CCampaignState> camp;
|
||||
void applyCl(CClient *cl);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & camp;
|
||||
}
|
||||
};
|
||||
|
||||
struct SetCommanderProperty : public CPackForClient
|
||||
{
|
||||
enum ECommanderProperty {ALIVE, BONUS, SECONDARY_SKILL, EXPERIENCE, SPECIAL_SKILL};
|
||||
@ -495,16 +482,6 @@ struct AddQuest : public CPackForClient
|
||||
}
|
||||
};
|
||||
|
||||
struct PrepareForAdvancingCampaign : public CPackForClient
|
||||
{
|
||||
PrepareForAdvancingCampaign(){}
|
||||
|
||||
void applyCl(CClient *cl);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct UpdateArtHandlerLists : public CPackForClient
|
||||
{
|
||||
UpdateArtHandlerLists(){}
|
||||
@ -1889,30 +1866,18 @@ struct CommitPackage : public CPackForServer
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & packToCommit;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct CloseServer : public CPackForServer
|
||||
{
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{}
|
||||
};
|
||||
|
||||
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);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{}
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
struct DismissHero : public CPackForServer
|
||||
@ -1924,6 +1889,7 @@ struct DismissHero : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & hid;
|
||||
}
|
||||
};
|
||||
@ -1939,6 +1905,7 @@ struct MoveHero : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & dest;
|
||||
h & hid;
|
||||
h & transit;
|
||||
@ -1956,6 +1923,7 @@ struct CastleTeleportHero : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & dest;
|
||||
h & hid;
|
||||
}
|
||||
@ -1974,6 +1942,7 @@ struct ArrangeStacks : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & what;
|
||||
h & p1;
|
||||
h & p2;
|
||||
@ -1993,6 +1962,7 @@ struct DisbandCreature : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & pos;
|
||||
h & id;
|
||||
}
|
||||
@ -2008,6 +1978,7 @@ struct BuildStructure : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & tid;
|
||||
h & bid;
|
||||
}
|
||||
@ -2033,6 +2004,7 @@ struct RecruitCreatures : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & tid;
|
||||
h & dst;
|
||||
h & crid;
|
||||
@ -2052,6 +2024,7 @@ struct UpgradeCreature : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & pos;
|
||||
h & id;
|
||||
h & cid;
|
||||
@ -2067,6 +2040,7 @@ struct GarrisonHeroSwap : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & tid;
|
||||
}
|
||||
};
|
||||
@ -2079,6 +2053,7 @@ struct ExchangeArtifacts : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & src;
|
||||
h & dst;
|
||||
}
|
||||
@ -2097,6 +2072,7 @@ struct AssembleArtifacts : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & heroID;
|
||||
h & artifactSlot;
|
||||
h & assemble;
|
||||
@ -2114,6 +2090,7 @@ struct BuyArtifact : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & hid;
|
||||
h & aid;
|
||||
}
|
||||
@ -2135,6 +2112,7 @@ struct TradeOnMarketplace : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & marketId;
|
||||
h & heroId;
|
||||
h & mode;
|
||||
@ -2154,6 +2132,7 @@ struct SetFormation : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & hid;
|
||||
h & formation;
|
||||
}
|
||||
@ -2170,6 +2149,7 @@ struct HireHero : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & hid;
|
||||
h & tid;
|
||||
h & player;
|
||||
@ -2184,6 +2164,7 @@ struct BuildBoat : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & objid;
|
||||
}
|
||||
|
||||
@ -2200,6 +2181,7 @@ struct QueryReply : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & qid;
|
||||
h & player;
|
||||
h & reply;
|
||||
@ -2215,6 +2197,7 @@ struct MakeAction : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & ba;
|
||||
}
|
||||
};
|
||||
@ -2228,6 +2211,7 @@ struct MakeCustomAction : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & ba;
|
||||
}
|
||||
};
|
||||
@ -2240,6 +2224,7 @@ struct DigWithHero : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & id;
|
||||
}
|
||||
};
|
||||
@ -2254,6 +2239,7 @@ struct CastAdvSpell : public CPackForServer
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & hid;
|
||||
h & sid;
|
||||
h & pos;
|
||||
@ -2262,43 +2248,73 @@ struct CastAdvSpell : public CPackForServer
|
||||
|
||||
/***********************************************************************************************************/
|
||||
|
||||
struct SaveGame : public CPackForClient, public CPackForServer
|
||||
struct SaveGame : public CPackForServer
|
||||
{
|
||||
SaveGame(){};
|
||||
SaveGame(const std::string &Fname) :fname(Fname){};
|
||||
std::string fname;
|
||||
|
||||
void applyCl(CClient *cl);
|
||||
void applyGs(CGameState *gs){};
|
||||
bool applyGh(CGameHandler *gh);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & fname;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Eventually we should re-merge both SaveGame and PlayerMessage
|
||||
struct SaveGameClient : public CPackForClient
|
||||
{
|
||||
SaveGameClient(){};
|
||||
SaveGameClient(const std::string &Fname) :fname(Fname){};
|
||||
std::string fname;
|
||||
|
||||
void applyCl(CClient *cl);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & fname;
|
||||
}
|
||||
};
|
||||
|
||||
struct PlayerMessage : public CPackForClient, public CPackForServer
|
||||
struct PlayerMessage : public CPackForServer
|
||||
{
|
||||
PlayerMessage(){};
|
||||
PlayerMessage(PlayerColor Player, const std::string &Text, ObjectInstanceID obj)
|
||||
:player(Player),text(Text), currObj(obj)
|
||||
PlayerMessage(const std::string &Text, ObjectInstanceID obj)
|
||||
: text(Text), currObj(obj)
|
||||
{};
|
||||
void applyCl(CClient *cl);
|
||||
void applyGs(CGameState *gs){};
|
||||
bool applyGh(CGameHandler *gh);
|
||||
|
||||
PlayerColor player;
|
||||
std::string text;
|
||||
ObjectInstanceID currObj; // optional parameter that specifies current object. For cheats :)
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & text;
|
||||
h & player;
|
||||
h & currObj;
|
||||
}
|
||||
};
|
||||
|
||||
struct PlayerMessageClient : public CPackForClient
|
||||
{
|
||||
PlayerMessageClient(){};
|
||||
PlayerMessageClient(PlayerColor Player, const std::string &Text)
|
||||
: player(Player), text(Text)
|
||||
{}
|
||||
void applyCl(CClient *cl);
|
||||
|
||||
PlayerColor player;
|
||||
std::string text;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & player;
|
||||
h & text;
|
||||
}
|
||||
};
|
||||
|
||||
struct CenterView : public CPackForClient
|
||||
{
|
||||
CenterView():focusTime(0){};
|
||||
@ -2315,159 +2331,3 @@ struct CenterView : public CPackForClient
|
||||
h & focusTime;
|
||||
}
|
||||
};
|
||||
|
||||
/***********************************************************************************************************/
|
||||
|
||||
struct CPackForSelectionScreen : public CPack
|
||||
{
|
||||
void apply(CSelectionScreen *selScreen) {} // implemented in CPreGame.cpp
|
||||
};
|
||||
|
||||
class CPregamePackToPropagate : public CPackForSelectionScreen
|
||||
{};
|
||||
|
||||
class CPregamePackToHost : public CPackForSelectionScreen
|
||||
{};
|
||||
|
||||
struct ChatMessage : public CPregamePackToPropagate
|
||||
{
|
||||
std::string playerName, message;
|
||||
|
||||
void apply(CSelectionScreen *selScreen);
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & playerName;
|
||||
h & message;
|
||||
}
|
||||
};
|
||||
|
||||
struct QuitMenuWithoutStarting : public CPregamePackToPropagate
|
||||
{
|
||||
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{}
|
||||
};
|
||||
|
||||
struct PlayerJoined : public CPregamePackToHost
|
||||
{
|
||||
std::string playerName;
|
||||
ui8 connectionID;
|
||||
|
||||
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & playerName;
|
||||
h & connectionID;
|
||||
}
|
||||
};
|
||||
|
||||
struct ELF_VISIBILITY SelectMap : public CPregamePackToPropagate
|
||||
{
|
||||
const CMapInfo *mapInfo;
|
||||
bool free;//local flag, do not serialize
|
||||
|
||||
DLL_LINKAGE SelectMap(const CMapInfo &src);
|
||||
DLL_LINKAGE SelectMap();
|
||||
DLL_LINKAGE ~SelectMap();
|
||||
|
||||
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & mapInfo;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct ELF_VISIBILITY UpdateStartOptions : public CPregamePackToPropagate
|
||||
{
|
||||
StartInfo *options;
|
||||
bool free;//local flag, do not serialize
|
||||
|
||||
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
|
||||
|
||||
DLL_LINKAGE UpdateStartOptions(StartInfo &src);
|
||||
DLL_LINKAGE UpdateStartOptions();
|
||||
DLL_LINKAGE ~UpdateStartOptions();
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & options;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct PregameGuiAction : public CPregamePackToPropagate
|
||||
{
|
||||
enum {NO_TAB, OPEN_OPTIONS, OPEN_SCENARIO_LIST, OPEN_RANDOM_MAP_OPTIONS}
|
||||
action;
|
||||
|
||||
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & action;
|
||||
}
|
||||
};
|
||||
|
||||
struct RequestOptionsChange : public CPregamePackToHost
|
||||
{
|
||||
enum EWhat {TOWN, HERO, BONUS};
|
||||
ui8 what;
|
||||
si8 direction; //-1 or +1
|
||||
ui8 playerID;
|
||||
|
||||
RequestOptionsChange(ui8 What, si8 Dir, ui8 Player)
|
||||
:what(What), direction(Dir), playerID(Player)
|
||||
{}
|
||||
RequestOptionsChange()
|
||||
:what(0), direction(0), playerID(0)
|
||||
{}
|
||||
|
||||
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & what;
|
||||
h & direction;
|
||||
h & playerID;
|
||||
}
|
||||
};
|
||||
|
||||
struct PlayerLeft : public CPregamePackToPropagate
|
||||
{
|
||||
ui8 playerID;
|
||||
|
||||
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & playerID;
|
||||
}
|
||||
};
|
||||
|
||||
struct PlayersNames : public CPregamePackToPropagate
|
||||
{
|
||||
public:
|
||||
std::map<ui8, std::string> playerNames;
|
||||
|
||||
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & playerNames;
|
||||
}
|
||||
};
|
||||
|
||||
struct StartWithCurrentSettings : public CPregamePackToPropagate
|
||||
{
|
||||
public:
|
||||
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
//h & playerNames;
|
||||
}
|
||||
};
|
||||
|
@ -20,7 +20,6 @@ class CArmedInstance;
|
||||
class CArtifactSet;
|
||||
class CBonusSystemNode;
|
||||
struct ArtSlotInfo;
|
||||
class BattleInfo;
|
||||
|
||||
#include "ConstTransitivePtr.h"
|
||||
#include "GameConstants.h"
|
||||
@ -28,7 +27,9 @@ class BattleInfo;
|
||||
|
||||
struct DLL_LINKAGE CPack
|
||||
{
|
||||
CPack() {};
|
||||
std::shared_ptr<CConnection> c; // Pointer to connection that pack received from
|
||||
|
||||
CPack() : c(nullptr) {};
|
||||
virtual ~CPack() {};
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
@ -54,12 +55,11 @@ struct CPackForClient : public CPack
|
||||
|
||||
struct CPackForServer : public CPack
|
||||
{
|
||||
PlayerColor player;
|
||||
CConnection *c;
|
||||
mutable PlayerColor player;
|
||||
mutable si32 requestID;
|
||||
CGameState* GS(CGameHandler *gh);
|
||||
CPackForServer():
|
||||
player(PlayerColor::NEUTRAL),
|
||||
c(nullptr)
|
||||
player(PlayerColor::NEUTRAL)
|
||||
{
|
||||
}
|
||||
|
||||
@ -69,6 +69,12 @@ struct CPackForServer : public CPack
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & player;
|
||||
h & requestID;
|
||||
}
|
||||
|
||||
protected:
|
||||
void throwNotAllowedAction();
|
||||
void throwOnWrongOwner(CGameHandler * gh, ObjectInstanceID id);
|
||||
|
@ -56,40 +56,6 @@ DLL_LINKAGE void SetSecSkill::applyGs(CGameState *gs)
|
||||
hero->setSecSkillLevel(which, val, abs);
|
||||
}
|
||||
|
||||
DLL_LINKAGE SelectMap::SelectMap(const CMapInfo &src)
|
||||
{
|
||||
mapInfo = &src;
|
||||
free = false;
|
||||
}
|
||||
DLL_LINKAGE SelectMap::SelectMap()
|
||||
{
|
||||
mapInfo = nullptr;
|
||||
free = true;
|
||||
}
|
||||
|
||||
DLL_LINKAGE SelectMap::~SelectMap()
|
||||
{
|
||||
if(free)
|
||||
delete mapInfo;
|
||||
}
|
||||
|
||||
DLL_LINKAGE UpdateStartOptions::UpdateStartOptions(StartInfo &src)
|
||||
{
|
||||
options = &src;
|
||||
free = false;
|
||||
}
|
||||
DLL_LINKAGE UpdateStartOptions::UpdateStartOptions()
|
||||
{
|
||||
options = nullptr;
|
||||
free = true;
|
||||
}
|
||||
|
||||
DLL_LINKAGE UpdateStartOptions::~UpdateStartOptions()
|
||||
{
|
||||
if(free)
|
||||
delete options;
|
||||
}
|
||||
|
||||
DLL_LINKAGE void SetCommanderProperty::applyGs(CGameState *gs)
|
||||
{
|
||||
CCommanderInstance * commander = gs->getHero(heroid)->commander;
|
||||
@ -346,8 +312,43 @@ DLL_LINKAGE void ChangeObjectVisitors::applyGs(CGameState *gs)
|
||||
DLL_LINKAGE void PlayerEndsGame::applyGs(CGameState *gs)
|
||||
{
|
||||
PlayerState *p = gs->getPlayer(player);
|
||||
if(victoryLossCheckResult.victory()) p->status = EPlayerStatus::WINNER;
|
||||
else p->status = EPlayerStatus::LOSER;
|
||||
if(victoryLossCheckResult.victory())
|
||||
{
|
||||
p->status = EPlayerStatus::WINNER;
|
||||
|
||||
// TODO: Campaign-specific code might as well go somewhere else
|
||||
if(p->human && gs->scenarioOps->campState)
|
||||
{
|
||||
std::vector<CGHeroInstance *> crossoverHeroes;
|
||||
for (CGHeroInstance * hero : gs->map->heroesOnMap)
|
||||
{
|
||||
if (hero->tempOwner == player)
|
||||
{
|
||||
// keep all heroes from the winning player
|
||||
crossoverHeroes.push_back(hero);
|
||||
}
|
||||
else if (vstd::contains(gs->scenarioOps->campState->getCurrentScenario().keepHeroes, HeroTypeID(hero->subID)))
|
||||
{
|
||||
// keep hero whether lost or won (like Xeron in AB campaign)
|
||||
crossoverHeroes.push_back(hero);
|
||||
}
|
||||
}
|
||||
// keep lost heroes which are in heroes pool
|
||||
for (auto & heroPair : gs->hpool.heroesPool)
|
||||
{
|
||||
if (vstd::contains(gs->scenarioOps->campState->getCurrentScenario().keepHeroes, HeroTypeID(heroPair.first)))
|
||||
{
|
||||
crossoverHeroes.push_back(heroPair.second.get());
|
||||
}
|
||||
}
|
||||
|
||||
gs->scenarioOps->campState->setCurrentMapAsConquered(crossoverHeroes);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p->status = EPlayerStatus::LOSER;
|
||||
}
|
||||
}
|
||||
|
||||
DLL_LINKAGE void RemoveBonus::applyGs(CGameState *gs)
|
||||
|
311
lib/NetPacksLobby.h
Normal file
311
lib/NetPacksLobby.h
Normal file
@ -0,0 +1,311 @@
|
||||
/*
|
||||
* NetPacksLobby.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "NetPacksBase.h"
|
||||
|
||||
#include "StartInfo.h"
|
||||
|
||||
class CCampaignState;
|
||||
class CLobbyScreen;
|
||||
class CServerHandler;
|
||||
class CMapInfo;
|
||||
struct StartInfo;
|
||||
class CMapGenOptions;
|
||||
struct ClientPlayer;
|
||||
class CVCMIServer;
|
||||
|
||||
struct CPackForLobby : public CPack
|
||||
{
|
||||
bool checkClientPermissions(CVCMIServer * srv) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void applyOnServerAfterAnnounce(CVCMIServer * srv) {}
|
||||
|
||||
bool applyOnLobbyHandler(CServerHandler * handler)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler) {}
|
||||
};
|
||||
|
||||
struct CLobbyPackToPropagate : public CPackForLobby
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
struct CLobbyPackToServer : public CPackForLobby
|
||||
{
|
||||
bool checkClientPermissions(CVCMIServer * srv) const;
|
||||
void applyOnServerAfterAnnounce(CVCMIServer * srv);
|
||||
};
|
||||
|
||||
struct LobbyClientConnected : public CLobbyPackToPropagate
|
||||
{
|
||||
// Set by client before sending pack to server
|
||||
std::string uuid;
|
||||
std::vector<std::string> names;
|
||||
StartInfo::EMode mode;
|
||||
// Changed by server before announcing pack
|
||||
int clientId;
|
||||
int hostClientId;
|
||||
|
||||
LobbyClientConnected()
|
||||
: mode(StartInfo::INVALID), clientId(-1), hostClientId(-1)
|
||||
{}
|
||||
|
||||
bool checkClientPermissions(CVCMIServer * srv) const;
|
||||
bool applyOnLobbyHandler(CServerHandler * handler);
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
void applyOnServerAfterAnnounce(CVCMIServer * srv);
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & uuid;
|
||||
h & names;
|
||||
h & mode;
|
||||
|
||||
h & clientId;
|
||||
h & hostClientId;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbyClientDisconnected : public CLobbyPackToPropagate
|
||||
{
|
||||
int clientId;
|
||||
bool shutdownServer;
|
||||
|
||||
LobbyClientDisconnected() : shutdownServer(false) {}
|
||||
bool checkClientPermissions(CVCMIServer * srv) const;
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
void applyOnServerAfterAnnounce(CVCMIServer * srv);
|
||||
bool applyOnLobbyHandler(CServerHandler * handler);
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & clientId;
|
||||
h & shutdownServer;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbyChatMessage : public CLobbyPackToPropagate
|
||||
{
|
||||
std::string playerName, message;
|
||||
|
||||
bool checkClientPermissions(CVCMIServer * srv) const;
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & playerName;
|
||||
h & message;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbyGuiAction : public CLobbyPackToPropagate
|
||||
{
|
||||
enum EAction : ui8 {
|
||||
NONE, NO_TAB, OPEN_OPTIONS, OPEN_SCENARIO_LIST, OPEN_RANDOM_MAP_OPTIONS
|
||||
} action;
|
||||
|
||||
LobbyGuiAction() : action(NONE) {}
|
||||
bool checkClientPermissions(CVCMIServer * srv) const;
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & action;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbyStartGame : public CLobbyPackToPropagate
|
||||
{
|
||||
// Set by server
|
||||
std::shared_ptr<StartInfo> initializedStartInfo;
|
||||
|
||||
LobbyStartGame() : initializedStartInfo(nullptr) {}
|
||||
bool checkClientPermissions(CVCMIServer * srv) const;
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
void applyOnServerAfterAnnounce(CVCMIServer * srv);
|
||||
bool applyOnLobbyHandler(CServerHandler * handler);
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & initializedStartInfo;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbyChangeHost : public CLobbyPackToPropagate
|
||||
{
|
||||
int newHostConnectionId;
|
||||
|
||||
LobbyChangeHost() : newHostConnectionId(-1) {}
|
||||
bool checkClientPermissions(CVCMIServer * srv) const;
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
bool applyOnServerAfterAnnounce(CVCMIServer * srv);
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & newHostConnectionId;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbyUpdateState : public CLobbyPackToPropagate
|
||||
{
|
||||
LobbyState state;
|
||||
bool hostChanged; // Used on client-side only
|
||||
|
||||
LobbyUpdateState() : hostChanged(false) {}
|
||||
bool applyOnLobbyHandler(CServerHandler * handler);
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & state;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbySetMap : public CLobbyPackToServer
|
||||
{
|
||||
std::shared_ptr<CMapInfo> mapInfo;
|
||||
std::shared_ptr<CMapGenOptions> mapGenOpts;
|
||||
|
||||
LobbySetMap() : mapInfo(nullptr), mapGenOpts(nullptr) {}
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & mapInfo;
|
||||
h & mapGenOpts;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbySetCampaign : public CLobbyPackToServer
|
||||
{
|
||||
std::shared_ptr<CCampaignState> ourCampaign;
|
||||
|
||||
LobbySetCampaign() {}
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & ourCampaign;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbySetCampaignMap : public CLobbyPackToServer
|
||||
{
|
||||
int mapId;
|
||||
|
||||
LobbySetCampaignMap() : mapId(-1) {}
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & mapId;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbySetCampaignBonus : public CLobbyPackToServer
|
||||
{
|
||||
int bonusId;
|
||||
|
||||
LobbySetCampaignBonus() : bonusId(-1) {}
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & bonusId;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbyChangePlayerOption : public CLobbyPackToServer
|
||||
{
|
||||
enum EWhat : ui8 {UNKNOWN, TOWN, HERO, BONUS};
|
||||
ui8 what;
|
||||
si8 direction; //-1 or +1
|
||||
PlayerColor color;
|
||||
|
||||
LobbyChangePlayerOption() : what(UNKNOWN), direction(0), color(PlayerColor::CANNOT_DETERMINE) {}
|
||||
bool checkClientPermissions(CVCMIServer * srv) const;
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & what;
|
||||
h & direction;
|
||||
h & color;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbySetPlayer : public CLobbyPackToServer
|
||||
{
|
||||
PlayerColor clickedColor;
|
||||
|
||||
LobbySetPlayer() : clickedColor(PlayerColor::CANNOT_DETERMINE){}
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & clickedColor;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbySetTurnTime : public CLobbyPackToServer
|
||||
{
|
||||
ui8 turnTime;
|
||||
|
||||
LobbySetTurnTime() : turnTime(0) {}
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & turnTime;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbySetDifficulty : public CLobbyPackToServer
|
||||
{
|
||||
ui8 difficulty;
|
||||
|
||||
LobbySetDifficulty() : difficulty(0) {}
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & difficulty;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbyForceSetPlayer : public CLobbyPackToServer
|
||||
{
|
||||
ui8 targetConnectedPlayer;
|
||||
PlayerColor targetPlayerColor;
|
||||
|
||||
LobbyForceSetPlayer() : targetConnectedPlayer(-1), targetPlayerColor(PlayerColor::CANNOT_DETERMINE) {}
|
||||
bool applyOnServer(CVCMIServer * srv);
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & targetConnectedPlayer;
|
||||
h & targetPlayerColor;
|
||||
}
|
||||
};
|
195
lib/StartInfo.cpp
Normal file
195
lib/StartInfo.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* StartInfo.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "StartInfo.h"
|
||||
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "rmg/CMapGenOptions.h"
|
||||
#include "mapping/CMapInfo.h"
|
||||
|
||||
PlayerSettings::PlayerSettings()
|
||||
: bonus(RANDOM), castle(NONE), hero(RANDOM), heroPortrait(RANDOM), color(0), handicap(NO_HANDICAP), team(0), compOnly(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool PlayerSettings::isControlledByAI() const
|
||||
{
|
||||
return !connectedPlayerIDs.size();
|
||||
}
|
||||
|
||||
bool PlayerSettings::isControlledByHuman() const
|
||||
{
|
||||
return connectedPlayerIDs.size();
|
||||
}
|
||||
|
||||
PlayerSettings & StartInfo::getIthPlayersSettings(PlayerColor no)
|
||||
{
|
||||
if(playerInfos.find(no) != playerInfos.end())
|
||||
return playerInfos[no];
|
||||
logGlobal->error("Cannot find info about player %s. Throwing...", no.getStr());
|
||||
throw std::runtime_error("Cannot find info about player");
|
||||
}
|
||||
const PlayerSettings & StartInfo::getIthPlayersSettings(PlayerColor no) const
|
||||
{
|
||||
return const_cast<StartInfo &>(*this).getIthPlayersSettings(no);
|
||||
}
|
||||
|
||||
PlayerSettings * StartInfo::getPlayersSettings(const ui8 connectedPlayerId)
|
||||
{
|
||||
for(auto & elem : playerInfos)
|
||||
{
|
||||
if(vstd::contains(elem.second.connectedPlayerIDs, connectedPlayerId))
|
||||
return &elem.second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string StartInfo::getCampaignName() const
|
||||
{
|
||||
if(campState->camp->header.name.length())
|
||||
return campState->camp->header.name;
|
||||
else
|
||||
return VLC->generaltexth->allTexts[508];
|
||||
}
|
||||
|
||||
void LobbyInfo::verifyStateBeforeStart(bool ignoreNoHuman) const
|
||||
{
|
||||
if(!mi)
|
||||
throw ExceptionMapMissing();
|
||||
|
||||
//there must be at least one human player before game can be started
|
||||
std::map<PlayerColor, PlayerSettings>::const_iterator i;
|
||||
for(i = si->playerInfos.cbegin(); i != si->playerInfos.cend(); i++)
|
||||
if(i->second.isControlledByHuman())
|
||||
break;
|
||||
|
||||
if(i == si->playerInfos.cend() && !ignoreNoHuman)
|
||||
throw ExceptionNoHuman();
|
||||
|
||||
if(si->mapGenOptions && si->mode == StartInfo::NEW_GAME)
|
||||
{
|
||||
if(!si->mapGenOptions->checkOptions())
|
||||
throw ExceptionNoTemplate();
|
||||
}
|
||||
}
|
||||
|
||||
bool LobbyInfo::isClientHost(int clientId) const
|
||||
{
|
||||
return clientId == hostClientId;
|
||||
}
|
||||
|
||||
std::set<PlayerColor> LobbyInfo::getAllClientPlayers(int clientId)
|
||||
{
|
||||
std::set<PlayerColor> players;
|
||||
for(auto & elem : si->playerInfos)
|
||||
{
|
||||
if(isClientHost(clientId) && elem.second.isControlledByAI())
|
||||
players.insert(elem.first);
|
||||
|
||||
for(ui8 id : elem.second.connectedPlayerIDs)
|
||||
{
|
||||
if(vstd::contains(getConnectedPlayerIdsForClient(clientId), id))
|
||||
players.insert(elem.first);
|
||||
}
|
||||
}
|
||||
if(isClientHost(clientId))
|
||||
players.insert(PlayerColor::NEUTRAL);
|
||||
|
||||
return players;
|
||||
}
|
||||
|
||||
std::vector<ui8> LobbyInfo::getConnectedPlayerIdsForClient(int clientId) const
|
||||
{
|
||||
std::vector<ui8> ids;
|
||||
|
||||
for(auto & pair : playerNames)
|
||||
{
|
||||
if(pair.second.connection == clientId)
|
||||
{
|
||||
for(auto & elem : si->playerInfos)
|
||||
{
|
||||
if(vstd::contains(elem.second.connectedPlayerIDs, pair.first))
|
||||
ids.push_back(pair.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
std::set<PlayerColor> LobbyInfo::clientHumanColors(int clientId)
|
||||
{
|
||||
std::set<PlayerColor> players;
|
||||
for(auto & elem : si->playerInfos)
|
||||
{
|
||||
for(ui8 id : elem.second.connectedPlayerIDs)
|
||||
{
|
||||
if(vstd::contains(getConnectedPlayerIdsForClient(clientId), id))
|
||||
{
|
||||
players.insert(elem.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return players;
|
||||
}
|
||||
|
||||
|
||||
PlayerColor LobbyInfo::clientFirstColor(int clientId) const
|
||||
{
|
||||
for(auto & pair : si->playerInfos)
|
||||
{
|
||||
if(isClientColor(clientId, pair.first))
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
return PlayerColor::CANNOT_DETERMINE;
|
||||
}
|
||||
|
||||
bool LobbyInfo::isClientColor(int clientId, PlayerColor color) const
|
||||
{
|
||||
if(si->playerInfos.find(color) != si->playerInfos.end())
|
||||
{
|
||||
for(ui8 id : si->playerInfos.find(color)->second.connectedPlayerIDs)
|
||||
{
|
||||
if(playerNames.find(id) != playerNames.end())
|
||||
{
|
||||
if(playerNames.find(id)->second.connection == clientId)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ui8 LobbyInfo::clientFirstId(int clientId) const
|
||||
{
|
||||
for(auto & pair : playerNames)
|
||||
{
|
||||
if(pair.second.connection == clientId)
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PlayerInfo & LobbyInfo::getPlayerInfo(int color)
|
||||
{
|
||||
return mi->mapHeader->players[color];
|
||||
}
|
||||
|
||||
TeamID LobbyInfo::getPlayerTeamId(PlayerColor color)
|
||||
{
|
||||
if(color < PlayerColor::PLAYER_LIMIT)
|
||||
return getPlayerInfo(color.getNum()).team;
|
||||
else
|
||||
return TeamID::NO_TEAM;
|
||||
}
|
119
lib/StartInfo.h
119
lib/StartInfo.h
@ -13,9 +13,13 @@
|
||||
|
||||
class CMapGenOptions;
|
||||
class CCampaignState;
|
||||
class CMapInfo;
|
||||
struct PlayerInfo;
|
||||
class PlayerColor;
|
||||
class SharedMemory;
|
||||
|
||||
/// Struct which describes the name, the color, the starting bonus of a player
|
||||
struct PlayerSettings
|
||||
struct DLL_LINKAGE PlayerSettings
|
||||
{
|
||||
enum { PLAYER_AI = 0 }; // for use in playerID
|
||||
|
||||
@ -39,7 +43,7 @@ struct PlayerSettings
|
||||
TeamID team;
|
||||
|
||||
std::string name;
|
||||
ui8 playerID; //0 - AI, non-0 serves as player id
|
||||
std::set<ui8> connectedPlayerIDs; //Empty - AI, or connectrd player ids
|
||||
bool compOnly; //true if this player is a computer only player; required for RMG
|
||||
template <typename Handler>
|
||||
void serialize(Handler &h, const int version)
|
||||
@ -52,20 +56,30 @@ struct PlayerSettings
|
||||
h & color;
|
||||
h & handicap;
|
||||
h & name;
|
||||
h & playerID;
|
||||
if(version < 787)
|
||||
{
|
||||
ui8 oldConnectedId = 0;
|
||||
h & oldConnectedId;
|
||||
if(oldConnectedId)
|
||||
{
|
||||
connectedPlayerIDs.insert(oldConnectedId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
h & connectedPlayerIDs;
|
||||
}
|
||||
h & team;
|
||||
h & compOnly;
|
||||
}
|
||||
|
||||
PlayerSettings() : bonus(RANDOM), castle(NONE), hero(RANDOM), heroPortrait(RANDOM),
|
||||
color(0), handicap(NO_HANDICAP), team(0), playerID(PLAYER_AI), compOnly(false)
|
||||
{
|
||||
|
||||
}
|
||||
PlayerSettings();
|
||||
bool isControlledByAI() const;
|
||||
bool isControlledByHuman() const;
|
||||
};
|
||||
|
||||
/// Struct which describes the difficulty, the turn time,.. of a heroes match.
|
||||
struct StartInfo
|
||||
struct DLL_LINKAGE StartInfo
|
||||
{
|
||||
enum EMode {NEW_GAME, LOAD_GAME, CAMPAIGN, INVALID = 255};
|
||||
|
||||
@ -85,26 +99,12 @@ struct StartInfo
|
||||
|
||||
std::shared_ptr<CCampaignState> campState;
|
||||
|
||||
PlayerSettings & getIthPlayersSettings(PlayerColor no)
|
||||
{
|
||||
if(playerInfos.find(no) != playerInfos.end())
|
||||
return playerInfos[no];
|
||||
logGlobal->error("Cannot find info about player %s. Throwing...", no.getStr());
|
||||
throw std::runtime_error("Cannot find info about player");
|
||||
}
|
||||
const PlayerSettings & getIthPlayersSettings(PlayerColor no) const
|
||||
{
|
||||
return const_cast<StartInfo&>(*this).getIthPlayersSettings(no);
|
||||
}
|
||||
PlayerSettings & getIthPlayersSettings(PlayerColor no);
|
||||
const PlayerSettings & getIthPlayersSettings(PlayerColor no) const;
|
||||
PlayerSettings * getPlayersSettings(const ui8 connectedPlayerId);
|
||||
|
||||
PlayerSettings *getPlayersSettings(const ui8 nameID)
|
||||
{
|
||||
for(auto it=playerInfos.begin(); it != playerInfos.end(); ++it)
|
||||
if(it->second.playerID == nameID)
|
||||
return &it->second;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
// TODO: Must be client-side
|
||||
std::string getCampaignName() const;
|
||||
|
||||
template <typename Handler>
|
||||
void serialize(Handler &h, const int version)
|
||||
@ -127,3 +127,66 @@ struct StartInfo
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
struct ClientPlayer
|
||||
{
|
||||
int connection;
|
||||
std::string name;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & connection;
|
||||
h & name;
|
||||
}
|
||||
};
|
||||
|
||||
struct LobbyState
|
||||
{
|
||||
std::shared_ptr<StartInfo> si;
|
||||
std::shared_ptr<CMapInfo> mi;
|
||||
std::map<ui8, ClientPlayer> playerNames; // id of player <-> player name; 0 is reserved as ID of AI "players"
|
||||
int hostClientId;
|
||||
// TODO: Campaign-only and we don't really need either of them.
|
||||
// Before start both go into CCampaignState that is part of StartInfo
|
||||
int campaignMap;
|
||||
int campaignBonus;
|
||||
|
||||
LobbyState() : si(new StartInfo()), hostClientId(-1), campaignMap(-1), campaignBonus(-1) {}
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & si;
|
||||
h & mi;
|
||||
h & playerNames;
|
||||
h & hostClientId;
|
||||
h & campaignMap;
|
||||
h & campaignBonus;
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE LobbyInfo : public LobbyState
|
||||
{
|
||||
boost::mutex stateMutex;
|
||||
std::string uuid;
|
||||
std::shared_ptr<SharedMemory> shm;
|
||||
|
||||
LobbyInfo() {}
|
||||
|
||||
void verifyStateBeforeStart(bool ignoreNoHuman = false) const;
|
||||
|
||||
bool isClientHost(int clientId) const;
|
||||
std::set<PlayerColor> getAllClientPlayers(int clientId);
|
||||
std::vector<ui8> getConnectedPlayerIdsForClient(int clientId) const;
|
||||
|
||||
// Helpers for lobby state access
|
||||
std::set<PlayerColor> clientHumanColors(int clientId);
|
||||
PlayerColor clientFirstColor(int clientId) const;
|
||||
bool isClientColor(int clientId, PlayerColor color) const;
|
||||
ui8 clientFirstId(int clientId) const; // Used by chat only!
|
||||
PlayerInfo & getPlayerInfo(int color);
|
||||
TeamID getPlayerTeamId(PlayerColor color);
|
||||
};
|
||||
|
||||
class ExceptionMapMissing : public std::exception {};
|
||||
class ExceptionNoHuman : public std::exception {};
|
||||
class ExceptionNoTemplate : public std::exception {};
|
||||
|
@ -39,6 +39,8 @@ public:
|
||||
|
||||
class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public spells::Caster
|
||||
{
|
||||
// We serialize heroes into JSON for crossover
|
||||
friend class CCampaignState;
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -468,7 +468,7 @@ void CGEvent::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
if(!(availableFor & (1 << h->tempOwner.getNum())))
|
||||
return;
|
||||
if(cb->getPlayerSettings(h->tempOwner)->playerID)
|
||||
if(cb->getPlayerSettings(h->tempOwner)->isControlledByHuman())
|
||||
{
|
||||
if(humanActivate)
|
||||
activated(h);
|
||||
|
@ -1350,6 +1350,12 @@ void CGTownInstance::afterAddToMap(CMap * map)
|
||||
map->towns.push_back(this);
|
||||
}
|
||||
|
||||
void CGTownInstance::reset()
|
||||
{
|
||||
CGTownInstance::merchantArtifacts.clear();
|
||||
CGTownInstance::universitySkills.clear();
|
||||
}
|
||||
|
||||
void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
{
|
||||
CGObjectInstance::serializeJsonOwner(handler);
|
||||
|
@ -291,6 +291,7 @@ public:
|
||||
std::string getObjectName() const override;
|
||||
|
||||
void afterAddToMap(CMap * map) override;
|
||||
static void reset();
|
||||
protected:
|
||||
void setPropertyDer(ui8 what, ui32 val) override;
|
||||
void serializeJsonOptions(JsonSerializeFormat & handler) override;
|
||||
|
@ -23,8 +23,12 @@
|
||||
#include "../CHeroHandler.h"
|
||||
#include "CMapService.h"
|
||||
#include "CMap.h"
|
||||
#include "CMapInfo.h"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
// For hero crossover
|
||||
#include "serializer/CSerializer.h"
|
||||
#include "serializer/JsonDeserializer.h"
|
||||
#include "serializer/JsonSerializer.h"
|
||||
|
||||
CCampaignHeader::CCampaignHeader()
|
||||
: version(0), mapVersion(0), difficultyChoosenByPlayer(0), music(0), filename(), loadFromLod(0)
|
||||
@ -373,25 +377,42 @@ bool CCampaignScenario::isNotVoid() const
|
||||
return mapName.size() > 0;
|
||||
}
|
||||
|
||||
const CGHeroInstance * CCampaignScenario::strongestHero( PlayerColor owner ) const
|
||||
const CGHeroInstance * CCampaignScenario::strongestHero(PlayerColor owner)
|
||||
{
|
||||
using boost::adaptors::filtered;
|
||||
std::function<bool(CGHeroInstance*)> isOwned = [=](const CGHeroInstance *h){ return h->tempOwner == owner; };
|
||||
auto ownedHeroes = crossoverHeroes | filtered(isOwned);
|
||||
std::function<bool(JsonNode & node)> isOwned = [owner](JsonNode & node)
|
||||
{
|
||||
auto h = CCampaignState::crossoverDeserialize(node);
|
||||
bool result = h->tempOwner == owner;
|
||||
vstd::clear_pointer(h);
|
||||
return result;
|
||||
};
|
||||
auto ownedHeroes = crossoverHeroes | boost::adaptors::filtered(isOwned);
|
||||
|
||||
auto i = vstd::maxElementByFun(ownedHeroes,
|
||||
[](const CGHeroInstance * h) {return h->getHeroStrength();});
|
||||
return i == ownedHeroes.end() ? nullptr : *i;
|
||||
auto i = vstd::maxElementByFun(ownedHeroes, [](JsonNode & node)
|
||||
{
|
||||
auto h = CCampaignState::crossoverDeserialize(node);
|
||||
double result = h->getHeroStrength();
|
||||
vstd::clear_pointer(h);
|
||||
return result;
|
||||
});
|
||||
return i == ownedHeroes.end() ? nullptr : CCampaignState::crossoverDeserialize(*i);
|
||||
}
|
||||
|
||||
std::vector<CGHeroInstance *> CCampaignScenario::getLostCrossoverHeroes() const
|
||||
std::vector<CGHeroInstance *> CCampaignScenario::getLostCrossoverHeroes()
|
||||
{
|
||||
std::vector<CGHeroInstance *> lostCrossoverHeroes;
|
||||
if(conquered)
|
||||
{
|
||||
for(auto hero : placedCrossoverHeroes)
|
||||
for(auto node2 : placedCrossoverHeroes)
|
||||
{
|
||||
auto it = range::find_if(crossoverHeroes, CGObjectInstanceBySubIdFinder(hero));
|
||||
auto hero = CCampaignState::crossoverDeserialize(node2);
|
||||
auto it = range::find_if(crossoverHeroes, [hero](JsonNode node)
|
||||
{
|
||||
auto h = CCampaignState::crossoverDeserialize(node);
|
||||
bool result = hero->subID == h->subID;
|
||||
vstd::clear_pointer(h);
|
||||
return result;
|
||||
});
|
||||
if(it == crossoverHeroes.end())
|
||||
{
|
||||
lostCrossoverHeroes.push_back(hero);
|
||||
@ -401,9 +422,25 @@ std::vector<CGHeroInstance *> CCampaignScenario::getLostCrossoverHeroes() const
|
||||
return lostCrossoverHeroes;
|
||||
}
|
||||
|
||||
void CCampaignState::setCurrentMapAsConquered( const std::vector<CGHeroInstance*> & heroes )
|
||||
std::vector<JsonNode> CCampaignScenario::update787(std::vector<CGHeroInstance *> & heroes)
|
||||
{
|
||||
camp->scenarios[*currentMap].crossoverHeroes = heroes;
|
||||
static_assert(MINIMAL_SERIALIZATION_VERSION < 787, "No longer needed CCampaignScenario::update787");
|
||||
std::vector<JsonNode> heroesNew;
|
||||
for(auto hero : heroes)
|
||||
{
|
||||
heroesNew.push_back(CCampaignState::crossoverSerialize(hero));
|
||||
}
|
||||
return heroesNew;
|
||||
}
|
||||
|
||||
void CCampaignState::setCurrentMapAsConquered(const std::vector<CGHeroInstance *> & heroes)
|
||||
{
|
||||
camp->scenarios[*currentMap].crossoverHeroes.clear();
|
||||
for(CGHeroInstance * hero : heroes)
|
||||
{
|
||||
camp->scenarios[*currentMap].crossoverHeroes.push_back(crossoverSerialize(hero));
|
||||
}
|
||||
|
||||
mapsConquered.push_back(*currentMap);
|
||||
mapsRemaining -= *currentMap;
|
||||
camp->scenarios[*currentMap].conquered = true;
|
||||
@ -449,6 +486,63 @@ CCampaignState::CCampaignState( std::unique_ptr<CCampaign> _camp ) : camp(std::m
|
||||
}
|
||||
}
|
||||
|
||||
CMap * CCampaignState::getMap(int scenarioId) const
|
||||
{
|
||||
// FIXME: there is certainly better way to handle maps inside campaigns
|
||||
if(scenarioId == -1)
|
||||
scenarioId = currentMap.get();
|
||||
std::string scenarioName = camp->header.filename.substr(0, camp->header.filename.find('.'));
|
||||
boost::to_lower(scenarioName);
|
||||
scenarioName += ':' + boost::lexical_cast<std::string>(scenarioId);
|
||||
std::string & mapContent = camp->mapPieces.find(scenarioId)->second;
|
||||
auto buffer = reinterpret_cast<const ui8 *>(mapContent.data());
|
||||
CMapService mapService;
|
||||
return mapService.loadMap(buffer, mapContent.size(), scenarioName).release();
|
||||
}
|
||||
|
||||
std::unique_ptr<CMapHeader> CCampaignState::getHeader(int scenarioId) const
|
||||
{
|
||||
if(scenarioId == -1)
|
||||
scenarioId = currentMap.get();
|
||||
|
||||
std::string scenarioName = camp->header.filename.substr(0, camp->header.filename.find('.'));
|
||||
boost::to_lower(scenarioName);
|
||||
scenarioName += ':' + boost::lexical_cast<std::string>(scenarioId);
|
||||
std::string & mapContent = camp->mapPieces.find(scenarioId)->second;
|
||||
auto buffer = reinterpret_cast<const ui8 *>(mapContent.data());
|
||||
CMapService mapService;
|
||||
return mapService.loadMapHeader(buffer, mapContent.size(), scenarioName);
|
||||
}
|
||||
|
||||
std::shared_ptr<CMapInfo> CCampaignState::getMapInfo(int scenarioId) const
|
||||
{
|
||||
if(scenarioId == -1)
|
||||
scenarioId = currentMap.get();
|
||||
|
||||
auto mapInfo = std::make_shared<CMapInfo>();
|
||||
mapInfo->fileURI = camp->header.filename;
|
||||
mapInfo->mapHeader = getHeader(scenarioId);
|
||||
mapInfo->countPlayers();
|
||||
return mapInfo;
|
||||
}
|
||||
|
||||
JsonNode CCampaignState::crossoverSerialize(CGHeroInstance * hero)
|
||||
{
|
||||
JsonNode node;
|
||||
JsonSerializer handler(nullptr, node);
|
||||
hero->serializeJsonOptions(handler);
|
||||
return node;
|
||||
}
|
||||
|
||||
CGHeroInstance * CCampaignState::crossoverDeserialize(JsonNode & node)
|
||||
{
|
||||
JsonDeserializer handler(nullptr, node);
|
||||
auto hero = new CGHeroInstance();
|
||||
hero->ID = Obj::HERO;
|
||||
hero->serializeJsonOptions(handler);
|
||||
return hero;
|
||||
}
|
||||
|
||||
std::string CCampaignHandler::prologVideoName(ui8 index)
|
||||
{
|
||||
JsonNode config(ResourceID(std::string("CONFIG/campaignMedia"), EResType::TEXT));
|
||||
|
@ -14,6 +14,10 @@
|
||||
struct StartInfo;
|
||||
class CGHeroInstance;
|
||||
class CBinaryReader;
|
||||
class CMap;
|
||||
class CMapHeader;
|
||||
class CMapInfo;
|
||||
class JsonNode;
|
||||
|
||||
namespace CampaignVersion
|
||||
{
|
||||
@ -134,13 +138,15 @@ public:
|
||||
|
||||
CScenarioTravel travelOptions;
|
||||
std::vector<HeroTypeID> keepHeroes; // contains list of heroes which should be kept for next scenario (doesn't matter if they lost)
|
||||
std::vector<CGHeroInstance *> crossoverHeroes; // contains all heroes with the same state when the campaign scenario was finished
|
||||
std::vector<CGHeroInstance *> placedCrossoverHeroes; // contains all placed crossover heroes defined by hero placeholders when the scenario was started
|
||||
std::vector<JsonNode> crossoverHeroes; // contains all heroes with the same state when the campaign scenario was finished
|
||||
std::vector<JsonNode> placedCrossoverHeroes; // contains all placed crossover heroes defined by hero placeholders when the scenario was started
|
||||
|
||||
const CGHeroInstance * strongestHero(PlayerColor owner) const;
|
||||
void loadPreconditionRegions(ui32 regions);
|
||||
bool isNotVoid() const;
|
||||
std::vector<CGHeroInstance *> getLostCrossoverHeroes() const; /// returns a list of crossover heroes which started the scenario, but didn't complete it
|
||||
// FIXME: due to usage of JsonNode I can't make these methods const
|
||||
const CGHeroInstance * strongestHero(PlayerColor owner);
|
||||
std::vector<CGHeroInstance *> getLostCrossoverHeroes(); /// returns a list of crossover heroes which started the scenario, but didn't complete it
|
||||
std::vector<JsonNode> update787(std::vector<CGHeroInstance *> & heroes);
|
||||
|
||||
CCampaignScenario();
|
||||
|
||||
@ -157,8 +163,19 @@ public:
|
||||
h & prolog;
|
||||
h & epilog;
|
||||
h & travelOptions;
|
||||
h & crossoverHeroes;
|
||||
h & placedCrossoverHeroes;
|
||||
if(formatVersion < 787)
|
||||
{
|
||||
std::vector<CGHeroInstance *> crossoverHeroesOld, placedCrossoverHeroesOld;
|
||||
h & crossoverHeroesOld;
|
||||
h & placedCrossoverHeroesOld;
|
||||
crossoverHeroes = update787(crossoverHeroesOld);
|
||||
placedCrossoverHeroes = update787(placedCrossoverHeroesOld);
|
||||
}
|
||||
else
|
||||
{
|
||||
h & crossoverHeroes;
|
||||
h & placedCrossoverHeroes;
|
||||
}
|
||||
h & keepHeroes;
|
||||
}
|
||||
};
|
||||
@ -192,13 +209,19 @@ public:
|
||||
|
||||
std::map<ui8, ui8> chosenCampaignBonuses;
|
||||
|
||||
//void initNewCampaign(const StartInfo &si);
|
||||
void setCurrentMapAsConquered(const std::vector<CGHeroInstance*> & heroes);
|
||||
boost::optional<CScenarioTravel::STravelBonus> getBonusForCurrentMap() const;
|
||||
const CCampaignScenario & getCurrentScenario() const;
|
||||
CCampaignScenario & getCurrentScenario();
|
||||
ui8 currentBonusID() const;
|
||||
|
||||
CMap * getMap(int scenarioId = -1) const;
|
||||
std::unique_ptr<CMapHeader> getHeader(int scenarioId = -1) const;
|
||||
std::shared_ptr<CMapInfo> getMapInfo(int scenarioId = -1) const;
|
||||
|
||||
static JsonNode crossoverSerialize(CGHeroInstance * hero);
|
||||
static CGHeroInstance * crossoverDeserialize(JsonNode & node);
|
||||
|
||||
CCampaignState();
|
||||
CCampaignState(std::unique_ptr<CCampaign> _camp);
|
||||
~CCampaignState(){};
|
||||
|
@ -184,11 +184,6 @@ bool TerrainTile::isWater() const
|
||||
return terType == ETerrainType::WATER;
|
||||
}
|
||||
|
||||
const int CMapHeader::MAP_SIZE_SMALL = 36;
|
||||
const int CMapHeader::MAP_SIZE_MIDDLE = 72;
|
||||
const int CMapHeader::MAP_SIZE_LARGE = 108;
|
||||
const int CMapHeader::MAP_SIZE_XLARGE = 144;
|
||||
|
||||
void CMapHeader::setupEvents()
|
||||
{
|
||||
EventCondition victoryCondition(EventCondition::STANDARD_WIN);
|
||||
@ -269,6 +264,8 @@ CMap::~CMap()
|
||||
|
||||
for(auto quest : quests)
|
||||
quest.dellNull();
|
||||
|
||||
resetStaticData();
|
||||
}
|
||||
|
||||
void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
|
||||
@ -647,3 +644,11 @@ CMapEditManager * CMap::getEditManager()
|
||||
if(!editManager) editManager = make_unique<CMapEditManager>(this);
|
||||
return editManager.get();
|
||||
}
|
||||
|
||||
void CMap::resetStaticData()
|
||||
{
|
||||
CGKeys::reset();
|
||||
CGMagi::reset();
|
||||
CGObelisk::reset();
|
||||
CGTownInstance::reset();
|
||||
}
|
||||
|
@ -288,10 +288,11 @@ class DLL_LINKAGE CMapHeader
|
||||
{
|
||||
void setupEvents();
|
||||
public:
|
||||
static const int MAP_SIZE_SMALL;
|
||||
static const int MAP_SIZE_MIDDLE;
|
||||
static const int MAP_SIZE_LARGE;
|
||||
static const int MAP_SIZE_XLARGE;
|
||||
|
||||
static const int MAP_SIZE_SMALL = 36;
|
||||
static const int MAP_SIZE_MIDDLE = 72;
|
||||
static const int MAP_SIZE_LARGE = 108;
|
||||
static const int MAP_SIZE_XLARGE = 144;
|
||||
|
||||
CMapHeader();
|
||||
virtual ~CMapHeader();
|
||||
@ -382,6 +383,8 @@ public:
|
||||
/// Sets the victory/loss condition objectives ??
|
||||
void checkForObjectives();
|
||||
|
||||
void resetStaticData();
|
||||
|
||||
ui32 checksum;
|
||||
std::vector<Rumor> rumors;
|
||||
std::vector<DisposedHero> disposedHeroes;
|
||||
|
@ -15,54 +15,23 @@
|
||||
#include "../GameConstants.h"
|
||||
#include "CMapService.h"
|
||||
|
||||
void CMapInfo::countPlayers()
|
||||
{
|
||||
actualHumanPlayers = playerAmnt = humanPlayers = 0;
|
||||
for(int i=0; i<PlayerColor::PLAYER_LIMIT_I; i++)
|
||||
{
|
||||
if(mapHeader->players[i].canHumanPlay)
|
||||
{
|
||||
playerAmnt++;
|
||||
humanPlayers++;
|
||||
}
|
||||
else if(mapHeader->players[i].canComputerPlay)
|
||||
{
|
||||
playerAmnt++;
|
||||
}
|
||||
}
|
||||
#include "../filesystem/Filesystem.h"
|
||||
#include "../serializer/CMemorySerializer.h"
|
||||
#include "../CGeneralTextHandler.h"
|
||||
#include "../rmg/CMapGenOptions.h"
|
||||
#include "../CCreatureHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CModHandler.h"
|
||||
|
||||
if(scenarioOpts)
|
||||
for (auto i = scenarioOpts->playerInfos.cbegin(); i != scenarioOpts->playerInfos.cend(); i++)
|
||||
if(i->second.playerID != PlayerSettings::PLAYER_AI)
|
||||
actualHumanPlayers++;
|
||||
}
|
||||
|
||||
CMapInfo::CMapInfo() : scenarioOpts(nullptr), playerAmnt(0), humanPlayers(0),
|
||||
actualHumanPlayers(0), isRandomMap(false)
|
||||
CMapInfo::CMapInfo()
|
||||
: scenarioOptionsOfSave(nullptr), amountOfPlayersOnMap(0), amountOfHumanControllablePlayers(0), amountOfHumanPlayersInSave(0), isRandomMap(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#define STEAL(x) x = std::move(tmp.x)
|
||||
|
||||
CMapInfo::CMapInfo(CMapInfo && tmp):
|
||||
scenarioOpts(nullptr), playerAmnt(0), humanPlayers(0),
|
||||
actualHumanPlayers(0), isRandomMap(false)
|
||||
{
|
||||
std::swap(scenarioOpts, tmp.scenarioOpts);
|
||||
STEAL(mapHeader);
|
||||
STEAL(campaignHeader);
|
||||
STEAL(fileURI);
|
||||
STEAL(date);
|
||||
STEAL(playerAmnt);
|
||||
STEAL(humanPlayers);
|
||||
STEAL(actualHumanPlayers);
|
||||
STEAL(isRandomMap);
|
||||
}
|
||||
|
||||
CMapInfo::~CMapInfo()
|
||||
{
|
||||
vstd::clear_pointer(scenarioOpts);
|
||||
vstd::clear_pointer(scenarioOptionsOfSave);
|
||||
}
|
||||
|
||||
void CMapInfo::mapInit(const std::string & fname)
|
||||
@ -73,23 +42,143 @@ void CMapInfo::mapInit(const std::string & fname)
|
||||
countPlayers();
|
||||
}
|
||||
|
||||
void CMapInfo::saveInit(ResourceID file)
|
||||
{
|
||||
CLoadFile lf(*CResourceHandler::get()->getResourceName(file), MINIMAL_SERIALIZATION_VERSION);
|
||||
lf.checkMagicBytes(SAVEGAME_MAGIC);
|
||||
|
||||
mapHeader = make_unique<CMapHeader>();
|
||||
lf >> *(mapHeader.get()) >> scenarioOptionsOfSave;
|
||||
fileURI = file.getName();
|
||||
countPlayers();
|
||||
std::time_t time = boost::filesystem::last_write_time(*CResourceHandler::get()->getResourceName(file));
|
||||
date = std::asctime(std::localtime(&time));
|
||||
// We absolutely not need this data for lobby and server will read it from save
|
||||
// FIXME: actually we don't want them in CMapHeader!
|
||||
mapHeader->triggeredEvents.clear();
|
||||
}
|
||||
|
||||
void CMapInfo::campaignInit()
|
||||
{
|
||||
campaignHeader = std::unique_ptr<CCampaignHeader>(new CCampaignHeader(CCampaignHandler::getHeader(fileURI)));
|
||||
}
|
||||
|
||||
CMapInfo & CMapInfo::operator=(CMapInfo &&tmp)
|
||||
void CMapInfo::countPlayers()
|
||||
{
|
||||
STEAL(mapHeader);
|
||||
STEAL(campaignHeader);
|
||||
STEAL(scenarioOpts);
|
||||
STEAL(fileURI);
|
||||
STEAL(date);
|
||||
STEAL(playerAmnt);
|
||||
STEAL(humanPlayers);
|
||||
STEAL(actualHumanPlayers);
|
||||
STEAL(isRandomMap);
|
||||
return *this;
|
||||
for(int i=0; i<PlayerColor::PLAYER_LIMIT_I; i++)
|
||||
{
|
||||
if(mapHeader->players[i].canHumanPlay)
|
||||
{
|
||||
amountOfPlayersOnMap++;
|
||||
amountOfHumanControllablePlayers++;
|
||||
}
|
||||
else if(mapHeader->players[i].canComputerPlay)
|
||||
{
|
||||
amountOfPlayersOnMap++;
|
||||
}
|
||||
}
|
||||
|
||||
if(scenarioOptionsOfSave)
|
||||
for (auto i = scenarioOptionsOfSave->playerInfos.cbegin(); i != scenarioOptionsOfSave->playerInfos.cend(); i++)
|
||||
if(i->second.isControlledByHuman())
|
||||
amountOfHumanPlayersInSave++;
|
||||
}
|
||||
|
||||
#undef STEAL
|
||||
std::string CMapInfo::getName() const
|
||||
{
|
||||
if(campaignHeader && campaignHeader->name.length())
|
||||
return campaignHeader->name;
|
||||
else if(mapHeader && mapHeader->name.length())
|
||||
return mapHeader->name;
|
||||
else
|
||||
return VLC->generaltexth->allTexts[508];
|
||||
}
|
||||
|
||||
std::string CMapInfo::getNameForList() const
|
||||
{
|
||||
if(scenarioOptionsOfSave)
|
||||
{
|
||||
// TODO: this could be handled differently
|
||||
std::vector<std::string> path;
|
||||
boost::split(path, fileURI, boost::is_any_of("\\/"));
|
||||
return path[path.size()-1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
||||
std::string CMapInfo::getDescription() const
|
||||
{
|
||||
if(campaignHeader)
|
||||
return campaignHeader->description;
|
||||
else
|
||||
return mapHeader->description;
|
||||
}
|
||||
|
||||
int CMapInfo::getMapSizeIconId() const
|
||||
{
|
||||
if(!mapHeader)
|
||||
return 4;
|
||||
|
||||
switch(mapHeader->width)
|
||||
{
|
||||
case CMapHeader::MAP_SIZE_SMALL:
|
||||
return 0;
|
||||
case CMapHeader::MAP_SIZE_MIDDLE:
|
||||
return 1;
|
||||
case CMapHeader::MAP_SIZE_LARGE:
|
||||
return 2;
|
||||
case CMapHeader::MAP_SIZE_XLARGE:
|
||||
return 3;
|
||||
default:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<int, int> CMapInfo::getMapSizeFormatIconId() const
|
||||
{
|
||||
int frame = -1, group = 0;
|
||||
switch(mapHeader->version)
|
||||
{
|
||||
case EMapFormat::ROE:
|
||||
frame = 0;
|
||||
break;
|
||||
case EMapFormat::AB:
|
||||
frame = 1;
|
||||
break;
|
||||
case EMapFormat::SOD:
|
||||
frame = 2;
|
||||
break;
|
||||
case EMapFormat::WOG:
|
||||
frame = 3;
|
||||
break;
|
||||
case EMapFormat::VCMI:
|
||||
frame = 0;
|
||||
group = 1;
|
||||
break;
|
||||
default:
|
||||
// Unknown version. Be safe and ignore that map
|
||||
//logGlobal->warn("Warning: %s has wrong version!", currentItem->fileURI);
|
||||
break;
|
||||
}
|
||||
return std::make_pair(frame, group);
|
||||
}
|
||||
|
||||
std::string CMapInfo::getMapSizeName() const
|
||||
{
|
||||
switch(mapHeader->width)
|
||||
{
|
||||
case CMapHeader::MAP_SIZE_SMALL:
|
||||
return "S";
|
||||
case CMapHeader::MAP_SIZE_MIDDLE:
|
||||
return "M";
|
||||
case CMapHeader::MAP_SIZE_LARGE:
|
||||
return "L";
|
||||
case CMapHeader::MAP_SIZE_XLARGE:
|
||||
return "XL";
|
||||
default:
|
||||
return "C";
|
||||
}
|
||||
}
|
||||
|
@ -31,34 +31,41 @@ class DLL_LINKAGE CMapInfo
|
||||
public:
|
||||
std::unique_ptr<CMapHeader> mapHeader; //may be nullptr if campaign
|
||||
std::unique_ptr<CCampaignHeader> campaignHeader; //may be nullptr if scenario
|
||||
StartInfo * scenarioOpts; //options with which scenario has been started (used only with saved games)
|
||||
StartInfo * scenarioOptionsOfSave; // Options with which scenario has been started (used only with saved games)
|
||||
std::string fileURI;
|
||||
std::string date;
|
||||
int playerAmnt; //players in map
|
||||
int humanPlayers; //players ALLOWED to be controlled by human
|
||||
int actualHumanPlayers; // >1 if multiplayer game
|
||||
bool isRandomMap; // true if the map will be created randomly, false if not
|
||||
int amountOfPlayersOnMap;
|
||||
int amountOfHumanControllablePlayers;
|
||||
int amountOfHumanPlayersInSave;
|
||||
bool isRandomMap;
|
||||
|
||||
CMapInfo();
|
||||
CMapInfo(CMapInfo && tmp);
|
||||
virtual ~CMapInfo();
|
||||
|
||||
CMapInfo &operator=(CMapInfo &&other);
|
||||
|
||||
void mapInit(const std::string & fname);
|
||||
void saveInit(ResourceID file);
|
||||
void campaignInit();
|
||||
void countPlayers();
|
||||
// TODO: Those must be on client-side
|
||||
std::string getName() const;
|
||||
std::string getNameForList() const;
|
||||
std::string getDescription() const;
|
||||
int getMapSizeIconId() const;
|
||||
std::pair<int, int> getMapSizeFormatIconId() const;
|
||||
std::string getMapSizeName() const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int Version)
|
||||
{
|
||||
h & mapHeader;
|
||||
h & campaignHeader;
|
||||
h & scenarioOpts;
|
||||
h & scenarioOptionsOfSave;
|
||||
h & fileURI;
|
||||
h & date;
|
||||
h & playerAmnt;
|
||||
h & humanPlayers;
|
||||
h & actualHumanPlayers;
|
||||
h & amountOfPlayersOnMap;
|
||||
h & amountOfHumanControllablePlayers;
|
||||
h & amountOfHumanPlayersInSave;
|
||||
h & isRandomMap;
|
||||
}
|
||||
};
|
||||
|
@ -44,7 +44,7 @@ DEFINE_EXTERNAL_METHOD(registerTypesMapObjects2)
|
||||
DEFINE_EXTERNAL_METHOD(registerTypesClientPacks1)
|
||||
DEFINE_EXTERNAL_METHOD(registerTypesClientPacks2)
|
||||
DEFINE_EXTERNAL_METHOD(registerTypesServerPacks)
|
||||
DEFINE_EXTERNAL_METHOD(registerTypesPregamePacks)
|
||||
DEFINE_EXTERNAL_METHOD(registerTypesLobbyPacks)
|
||||
|
||||
template void registerTypes<BinaryDeserializer>(BinaryDeserializer & s);
|
||||
template void registerTypes<BinarySerializer>(BinarySerializer & s);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../NetPacks.h"
|
||||
#include "../NetPacksLobby.h"
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CPlayerState.h"
|
||||
@ -234,8 +235,6 @@ void registerTypesClientPacks1(Serializer &s)
|
||||
s.template registerType<CPackForClient, ChangeObjPos>();
|
||||
s.template registerType<CPackForClient, PlayerEndsGame>();
|
||||
s.template registerType<CPackForClient, RemoveBonus>();
|
||||
s.template registerType<CPackForClient, UpdateCampaignState>();
|
||||
s.template registerType<CPackForClient, PrepareForAdvancingCampaign>();
|
||||
s.template registerType<CPackForClient, UpdateArtHandlerLists>();
|
||||
s.template registerType<CPackForClient, UpdateMapEvents>();
|
||||
s.template registerType<CPackForClient, UpdateCastleEvents>();
|
||||
@ -312,16 +311,14 @@ void registerTypesClientPacks2(Serializer &s)
|
||||
s.template registerType<CArtifactOperationPack, AssembledArtifact>();
|
||||
s.template registerType<CArtifactOperationPack, DisassembledArtifact>();
|
||||
|
||||
s.template registerType<CPackForClient, SaveGame>();
|
||||
s.template registerType<CPackForClient, PlayerMessage>();
|
||||
s.template registerType<CPackForClient, SaveGameClient>();
|
||||
s.template registerType<CPackForClient, PlayerMessageClient>();
|
||||
}
|
||||
|
||||
template<typename Serializer>
|
||||
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>();
|
||||
@ -351,23 +348,34 @@ void registerTypesServerPacks(Serializer &s)
|
||||
}
|
||||
|
||||
template<typename Serializer>
|
||||
void registerTypesPregamePacks(Serializer &s)
|
||||
void registerTypesLobbyPacks(Serializer &s)
|
||||
{
|
||||
s.template registerType<CPack, CPackForSelectionScreen>();
|
||||
s.template registerType<CPackForSelectionScreen, CPregamePackToPropagate>();
|
||||
s.template registerType<CPackForSelectionScreen, CPregamePackToHost>();
|
||||
s.template registerType<CPack, CPackForLobby>();
|
||||
s.template registerType<CPackForLobby, CLobbyPackToPropagate>();
|
||||
s.template registerType<CPackForLobby, CLobbyPackToServer>();
|
||||
|
||||
s.template registerType<CPregamePackToPropagate, ChatMessage>();
|
||||
s.template registerType<CPregamePackToPropagate, QuitMenuWithoutStarting>();
|
||||
s.template registerType<CPregamePackToPropagate, SelectMap>();
|
||||
s.template registerType<CPregamePackToPropagate, UpdateStartOptions>();
|
||||
s.template registerType<CPregamePackToPropagate, PregameGuiAction>();
|
||||
s.template registerType<CPregamePackToPropagate, PlayerLeft>();
|
||||
s.template registerType<CPregamePackToPropagate, PlayersNames>();
|
||||
s.template registerType<CPregamePackToPropagate, StartWithCurrentSettings>();
|
||||
// Any client can sent
|
||||
s.template registerType<CLobbyPackToPropagate, LobbyClientConnected>();
|
||||
s.template registerType<CLobbyPackToPropagate, LobbyClientDisconnected>();
|
||||
s.template registerType<CLobbyPackToPropagate, LobbyChatMessage>();
|
||||
// Only host client send
|
||||
s.template registerType<CLobbyPackToPropagate, LobbyGuiAction>();
|
||||
s.template registerType<CLobbyPackToPropagate, LobbyStartGame>();
|
||||
s.template registerType<CLobbyPackToPropagate, LobbyChangeHost>();
|
||||
// Only server send
|
||||
s.template registerType<CLobbyPackToPropagate, LobbyUpdateState>();
|
||||
|
||||
s.template registerType<CPregamePackToHost, PlayerJoined>();
|
||||
s.template registerType<CPregamePackToHost, RequestOptionsChange>();
|
||||
// For client with permissions
|
||||
s.template registerType<CLobbyPackToServer, LobbyChangePlayerOption>();
|
||||
// Only for host client
|
||||
s.template registerType<CLobbyPackToServer, LobbySetMap>();
|
||||
s.template registerType<CLobbyPackToServer, LobbySetCampaign>();
|
||||
s.template registerType<CLobbyPackToServer, LobbySetCampaignMap>();
|
||||
s.template registerType<CLobbyPackToServer, LobbySetCampaignBonus>();
|
||||
s.template registerType<CLobbyPackToServer, LobbySetPlayer>();
|
||||
s.template registerType<CLobbyPackToServer, LobbySetTurnTime>();
|
||||
s.template registerType<CLobbyPackToServer, LobbySetDifficulty>();
|
||||
s.template registerType<CLobbyPackToServer, LobbyForceSetPlayer>();
|
||||
}
|
||||
|
||||
template<typename Serializer>
|
||||
@ -379,7 +387,7 @@ void registerTypes(Serializer &s)
|
||||
registerTypesClientPacks1(s);
|
||||
registerTypesClientPacks2(s);
|
||||
registerTypesServerPacks(s);
|
||||
registerTypesPregamePacks(s);
|
||||
registerTypesLobbyPacks(s);
|
||||
}
|
||||
|
||||
#ifndef INSTANTIATE_REGISTER_TYPES_HERE
|
||||
|
@ -1,37 +1,37 @@
|
||||
/*
|
||||
* TypesPregamePacks.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "RegisterTypes.h"
|
||||
|
||||
#include "../mapping/CMapInfo.h"
|
||||
#include "../StartInfo.h"
|
||||
#include "../CGameState.h"
|
||||
#include "../mapping/CMap.h"
|
||||
#include "../CModHandler.h"
|
||||
#include "../mapObjects/CObjectHandler.h"
|
||||
#include "../CCreatureHandler.h"
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../mapping/CCampaignHandler.h"
|
||||
#include "../NetPacks.h"
|
||||
#include "../mapObjects/CObjectClassesHandler.h"
|
||||
#include "../rmg/CMapGenOptions.h"
|
||||
|
||||
#include "../serializer/BinaryDeserializer.h"
|
||||
#include "../serializer/BinarySerializer.h"
|
||||
#include "../serializer/CTypeList.h"
|
||||
|
||||
template void registerTypesPregamePacks<BinaryDeserializer>(BinaryDeserializer & s);
|
||||
template void registerTypesPregamePacks<BinarySerializer>(BinarySerializer & s);
|
||||
template void registerTypesPregamePacks<CTypeList>(CTypeList & s);
|
||||
|
||||
/*
|
||||
* TypesLobbyPacks.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "RegisterTypes.h"
|
||||
|
||||
#include "../mapping/CMapInfo.h"
|
||||
#include "../StartInfo.h"
|
||||
#include "../CGameState.h"
|
||||
#include "../mapping/CMap.h"
|
||||
#include "../CModHandler.h"
|
||||
#include "../mapObjects/CObjectHandler.h"
|
||||
#include "../CCreatureHandler.h"
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../mapping/CCampaignHandler.h"
|
||||
#include "../NetPacks.h"
|
||||
#include "../mapObjects/CObjectClassesHandler.h"
|
||||
#include "../rmg/CMapGenOptions.h"
|
||||
|
||||
#include "../serializer/BinaryDeserializer.h"
|
||||
#include "../serializer/BinarySerializer.h"
|
||||
#include "../serializer/CTypeList.h"
|
||||
|
||||
template void registerTypesLobbyPacks<BinaryDeserializer>(BinaryDeserializer & s);
|
||||
template void registerTypesLobbyPacks<BinarySerializer>(BinarySerializer & s);
|
||||
template void registerTypesLobbyPacks<CTypeList>(CTypeList & s);
|
||||
|
@ -17,7 +17,8 @@
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CTownHandler.h"
|
||||
|
||||
CMapGenOptions::CMapGenOptions() : width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), hasTwoLevels(false),
|
||||
CMapGenOptions::CMapGenOptions()
|
||||
: width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), hasTwoLevels(true),
|
||||
playerCount(RANDOM_SIZE), teamCount(RANDOM_SIZE), compOnlyPlayerCount(RANDOM_SIZE), compOnlyTeamCount(RANDOM_SIZE), humanPlayersCount(0),
|
||||
waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM), mapTemplate(nullptr)
|
||||
{
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../ConstTransitivePtr.h"
|
||||
#include "../GameConstants.h"
|
||||
|
||||
const ui32 SERIALIZATION_VERSION = 786;
|
||||
const ui32 SERIALIZATION_VERSION = 787;
|
||||
const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
|
||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
||||
|
||||
|
@ -14,9 +14,6 @@
|
||||
#include "../mapping/CMap.h"
|
||||
#include "../CGameState.h"
|
||||
|
||||
#if BOOST_VERSION >= 106600
|
||||
#define BOOST_ASIO_ENABLE_OLD_SERVICES
|
||||
#endif
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
using namespace boost;
|
||||
@ -35,8 +32,9 @@ using namespace boost::asio::ip;
|
||||
|
||||
void CConnection::init()
|
||||
{
|
||||
boost::asio::ip::tcp::no_delay option(true);
|
||||
socket->set_option(option);
|
||||
socket->set_option(boost::asio::ip::tcp::no_delay(true));
|
||||
socket->set_option(boost::asio::socket_base::send_buffer_size(4194304));
|
||||
socket->set_option(boost::asio::socket_base::receive_buffer_size(4194304));
|
||||
|
||||
enableSmartPointerSerialization();
|
||||
disableStackSendingByID();
|
||||
@ -50,25 +48,21 @@ void CConnection::init()
|
||||
connected = true;
|
||||
std::string pom;
|
||||
//we got connection
|
||||
oser & std::string("Aiya!\n") & name & myEndianess; //identify ourselves
|
||||
iser & pom & pom & contactEndianess;
|
||||
logNetwork->info("Established connection with %s", pom);
|
||||
wmx = new boost::mutex();
|
||||
rmx = new boost::mutex();
|
||||
oser & std::string("Aiya!\n") & name & uuid & myEndianess; //identify ourselves
|
||||
iser & pom & pom & contactUuid & contactEndianess;
|
||||
logNetwork->info("Established connection with %s. UUID: %s", pom, contactUuid);
|
||||
mutexRead = std::make_shared<boost::mutex>();
|
||||
mutexWrite = std::make_shared<boost::mutex>();
|
||||
|
||||
handler = nullptr;
|
||||
receivedStop = sendStop = false;
|
||||
static int cid = 1;
|
||||
connectionID = cid++;
|
||||
iser.fileVersion = SERIALIZATION_VERSION;
|
||||
}
|
||||
|
||||
CConnection::CConnection(std::string host, ui16 port, std::string Name)
|
||||
:iser(this), oser(this), io_service(new asio::io_service), name(Name)
|
||||
CConnection::CConnection(std::string host, ui16 port, std::string Name, std::string UUID)
|
||||
: iser(this), oser(this), io_service(std::make_shared<asio::io_service>()), connectionID(0), name(Name), uuid(UUID)
|
||||
{
|
||||
int i;
|
||||
boost::system::error_code error = asio::error::host_not_found;
|
||||
socket = new tcp::socket(*io_service);
|
||||
socket = std::make_shared<tcp::socket>(*io_service);
|
||||
tcp::resolver resolver(*io_service);
|
||||
tcp::resolver::iterator end, pom, endpoint_iterator = resolver.resolve(tcp::resolver::query(host, std::to_string(port)),error);
|
||||
if(error)
|
||||
@ -114,25 +108,23 @@ connerror1:
|
||||
logNetwork->error(error.message());
|
||||
else
|
||||
logNetwork->error("No error info. ");
|
||||
delete io_service;
|
||||
//delete socket;
|
||||
throw std::runtime_error("Can't establish connection :(");
|
||||
}
|
||||
CConnection::CConnection(TSocket * Socket, std::string Name )
|
||||
:iser(this), oser(this), socket(Socket),io_service(&Socket->get_io_service()), name(Name)//, send(this), rec(this)
|
||||
CConnection::CConnection(std::shared_ptr<TSocket> Socket, std::string Name, std::string UUID)
|
||||
: iser(this), oser(this), socket(Socket), io_service(&Socket->get_io_service()), connectionID(0), name(Name), uuid(UUID)
|
||||
{
|
||||
init();
|
||||
}
|
||||
CConnection::CConnection(TAcceptor * acceptor, boost::asio::io_service *Io_service, std::string Name)
|
||||
: iser(this), oser(this), name(Name)//, send(this), rec(this)
|
||||
CConnection::CConnection(std::shared_ptr<TAcceptor> acceptor, std::shared_ptr<boost::asio::io_service> Io_service, std::string Name, std::string UUID)
|
||||
: iser(this), oser(this), connectionID(0), name(Name), uuid(UUID)
|
||||
{
|
||||
boost::system::error_code error = asio::error::host_not_found;
|
||||
socket = new tcp::socket(*io_service);
|
||||
socket = std::make_shared<tcp::socket>(*io_service);
|
||||
acceptor->accept(*socket,error);
|
||||
if (error)
|
||||
{
|
||||
logNetwork->error("Error on accepting: %s", error.message());
|
||||
delete socket;
|
||||
socket.reset();
|
||||
throw std::runtime_error("Can't establish connection :(");
|
||||
}
|
||||
init();
|
||||
@ -171,12 +163,7 @@ CConnection::~CConnection()
|
||||
if(handler)
|
||||
handler->join();
|
||||
|
||||
delete handler;
|
||||
|
||||
close();
|
||||
delete io_service;
|
||||
delete wmx;
|
||||
delete rmx;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
@ -193,7 +180,7 @@ void CConnection::close()
|
||||
if(socket)
|
||||
{
|
||||
socket->close();
|
||||
vstd::clear_pointer(socket);
|
||||
socket.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,11 +189,6 @@ bool CConnection::isOpen() const
|
||||
return socket && connected;
|
||||
}
|
||||
|
||||
bool CConnection::isHost() const
|
||||
{
|
||||
return connectionID == 1;
|
||||
}
|
||||
|
||||
void CConnection::reportState(vstd::CLoggerBase * out)
|
||||
{
|
||||
out->debug("CConnection");
|
||||
@ -219,19 +201,26 @@ void CConnection::reportState(vstd::CLoggerBase * out)
|
||||
|
||||
CPack * CConnection::retrievePack()
|
||||
{
|
||||
CPack *ret = nullptr;
|
||||
boost::unique_lock<boost::mutex> lock(*rmx);
|
||||
logNetwork->trace("Listening... ");
|
||||
iser & ret;
|
||||
logNetwork->trace("\treceived server message of type %s", (ret? typeid(*ret).name() : "nullptr"));
|
||||
return ret;
|
||||
CPack * pack = nullptr;
|
||||
boost::unique_lock<boost::mutex> lock(*mutexRead);
|
||||
iser & pack;
|
||||
logNetwork->trace("Received CPack of type %s", (pack ? typeid(*pack).name() : "nullptr"));
|
||||
if(pack == nullptr)
|
||||
{
|
||||
logNetwork->error("Received a nullptr CPack! You should check whether client and server ABI matches.");
|
||||
}
|
||||
else
|
||||
{
|
||||
pack->c = this->shared_from_this();
|
||||
}
|
||||
return pack;
|
||||
}
|
||||
|
||||
void CConnection::sendPackToServer(const CPack &pack, PlayerColor player, ui32 requestID)
|
||||
void CConnection::sendPack(const CPack * pack)
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(*wmx);
|
||||
logNetwork->trace("Sending to server a pack of type %s", typeid(pack).name());
|
||||
oser & player & requestID & &pack; //packs has to be sent as polymorphic pointers!
|
||||
boost::unique_lock<boost::mutex> lock(*mutexWrite);
|
||||
logNetwork->trace("Sending a pack of type %s", typeid(*pack).name());
|
||||
oser & pack;
|
||||
}
|
||||
|
||||
void CConnection::disableStackSendingByID()
|
||||
@ -254,16 +243,7 @@ void CConnection::enableSmartPointerSerialization()
|
||||
iser.smartPointerSerialization = oser.smartPointerSerialization = true;
|
||||
}
|
||||
|
||||
void CConnection::prepareForSendingHeroes()
|
||||
{
|
||||
iser.loadedPointers.clear();
|
||||
oser.savedPointers.clear();
|
||||
disableSmartVectorMemberSerialization();
|
||||
enableSmartPointerSerialization();
|
||||
disableStackSendingByID();
|
||||
}
|
||||
|
||||
void CConnection::enterPregameConnectionMode()
|
||||
void CConnection::enterLobbyConnectionMode()
|
||||
{
|
||||
iser.loadedPointers.clear();
|
||||
oser.savedPointers.clear();
|
||||
@ -271,6 +251,13 @@ void CConnection::enterPregameConnectionMode()
|
||||
disableSmartPointerSerialization();
|
||||
}
|
||||
|
||||
void CConnection::enterGameplayConnectionMode(CGameState * gs)
|
||||
{
|
||||
enableStackSendingByID();
|
||||
disableSmartPointerSerialization();
|
||||
addStdVecItems(gs);
|
||||
}
|
||||
|
||||
void CConnection::disableSmartVectorMemberSerialization()
|
||||
{
|
||||
CSerializer::smartVectorMembersSerialization = false;
|
||||
@ -283,7 +270,7 @@ void CConnection::enableSmartVectorMemberSerializatoin()
|
||||
|
||||
std::string CConnection::toString() const
|
||||
{
|
||||
boost::format fmt("Connection with %s (ID: %d)");
|
||||
fmt % name % connectionID;
|
||||
return fmt.str();
|
||||
boost::format fmt("Connection with %s (ID: %d UUID: %s)");
|
||||
fmt % name % connectionID % uuid;
|
||||
return fmt.str();
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ typedef boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, boost::asio::so
|
||||
/// Main class for network communication
|
||||
/// Allows establishing connection and bidirectional read-write
|
||||
class DLL_LINKAGE CConnection
|
||||
: public IBinaryReader, public IBinaryWriter
|
||||
: public IBinaryReader, public IBinaryWriter, public std::enable_shared_from_this<CConnection>
|
||||
{
|
||||
CConnection();
|
||||
|
||||
@ -60,31 +60,31 @@ public:
|
||||
BinaryDeserializer iser;
|
||||
BinarySerializer oser;
|
||||
|
||||
boost::mutex *rmx, *wmx; // read/write mutexes
|
||||
TSocket * socket;
|
||||
std::shared_ptr<boost::mutex> mutexRead;
|
||||
std::shared_ptr<boost::mutex> mutexWrite;
|
||||
std::shared_ptr<TSocket> socket;
|
||||
bool connected;
|
||||
bool myEndianess, contactEndianess; //true if little endian, if endianness is different we'll have to revert received multi-byte vars
|
||||
boost::asio::io_service *io_service;
|
||||
std::string contactUuid;
|
||||
std::shared_ptr<boost::asio::io_service> io_service;
|
||||
std::string name; //who uses this connection
|
||||
std::string uuid;
|
||||
|
||||
int connectionID;
|
||||
boost::thread *handler;
|
||||
std::shared_ptr<boost::thread> handler;
|
||||
|
||||
bool receivedStop, sendStop;
|
||||
|
||||
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
|
||||
CConnection(std::string host, ui16 port, std::string Name, std::string UUID);
|
||||
CConnection(std::shared_ptr<TAcceptor> acceptor, std::shared_ptr<boost::asio::io_service> Io_service, std::string Name, std::string UUID);
|
||||
CConnection(std::shared_ptr<TSocket> Socket, std::string Name, std::string UUID); //use immediately after accepting connection into socket
|
||||
|
||||
void close();
|
||||
bool isOpen() const;
|
||||
bool isHost() const;
|
||||
template<class T>
|
||||
CConnection &operator&(const T&);
|
||||
virtual ~CConnection();
|
||||
|
||||
CPack * retrievePack(); //gets from server next pack (allocates it with new)
|
||||
void sendPackToServer(const CPack &pack, PlayerColor player, ui32 requestID);
|
||||
CPack * retrievePack();
|
||||
void sendPack(const CPack * pack);
|
||||
|
||||
void disableStackSendingByID();
|
||||
void enableStackSendingByID();
|
||||
@ -93,8 +93,8 @@ public:
|
||||
void disableSmartVectorMemberSerialization();
|
||||
void enableSmartVectorMemberSerializatoin();
|
||||
|
||||
void prepareForSendingHeroes(); //disables sending vectorized, enables smart pointer serialization, clears saved/loaded ptr cache
|
||||
void enterPregameConnectionMode();
|
||||
void enterLobbyConnectionMode();
|
||||
void enterGameplayConnectionMode(CGameState * gs);
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
|
@ -60,7 +60,7 @@ class DLL_LINKAGE PacketSender
|
||||
{
|
||||
public:
|
||||
virtual ~PacketSender(){};
|
||||
virtual void sendAndApply(CPackForClient * info) const = 0;
|
||||
virtual void sendAndApply(CPackForClient * pack) const = 0;
|
||||
virtual void complain(const std::string & problem) const = 0;
|
||||
};
|
||||
|
||||
|
@ -50,7 +50,6 @@
|
||||
#ifndef _MSC_VER
|
||||
#include <boost/thread/xtime.hpp>
|
||||
#endif
|
||||
extern std::atomic<bool> serverShuttingDown;
|
||||
|
||||
#define COMPLAIN_RET_IF(cond, txt) do {if (cond){complain(txt); return;}} while(0)
|
||||
#define COMPLAIN_RET_FALSE_IF(cond, txt) do {if (cond){complain(txt); return false;}} while(0)
|
||||
@ -62,7 +61,7 @@ class ServerSpellCastEnvironment : public SpellCastEnvironment
|
||||
public:
|
||||
ServerSpellCastEnvironment(CGameHandler * gh);
|
||||
~ServerSpellCastEnvironment() = default;
|
||||
void sendAndApply(CPackForClient * info) const override;
|
||||
void sendAndApply(CPackForClient * pack) const override;
|
||||
CRandomGenerator & getRandomGenerator() const override;
|
||||
void complain(const std::string & problem) const override;
|
||||
const CMap * getMap() const override;
|
||||
@ -177,7 +176,7 @@ template <typename T> class CApplyOnGH;
|
||||
class CBaseForGHApply
|
||||
{
|
||||
public:
|
||||
virtual bool applyOnGH(CGameHandler *gh, CConnection *c, void *pack, PlayerColor player) const =0;
|
||||
virtual bool applyOnGH(CGameHandler * gh, void * pack) const =0;
|
||||
virtual ~CBaseForGHApply(){}
|
||||
template<typename U> static CBaseForGHApply *getApplier(const U * t=nullptr)
|
||||
{
|
||||
@ -188,11 +187,9 @@ public:
|
||||
template <typename T> class CApplyOnGH : public CBaseForGHApply
|
||||
{
|
||||
public:
|
||||
bool applyOnGH(CGameHandler *gh, CConnection *c, void *pack, PlayerColor player) const override
|
||||
bool applyOnGH(CGameHandler * gh, void * pack) const override
|
||||
{
|
||||
T *ptr = static_cast<T*>(pack);
|
||||
ptr->c = c;
|
||||
ptr->player = player;
|
||||
try
|
||||
{
|
||||
return ptr->applyGh(gh);
|
||||
@ -212,7 +209,7 @@ template <>
|
||||
class CApplyOnGH<CPack> : public CBaseForGHApply
|
||||
{
|
||||
public:
|
||||
bool applyOnGH(CGameHandler *gh, CConnection *c, void *pack, PlayerColor player) const override
|
||||
bool applyOnGH(CGameHandler * gh, void * pack) const override
|
||||
{
|
||||
logGlobal->error("Cannot apply on GH plain CPack!");
|
||||
assert(0);
|
||||
@ -220,8 +217,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static CApplier<CBaseForGHApply> *applier = nullptr;
|
||||
|
||||
static inline double distance(int3 a, int3 b)
|
||||
{
|
||||
return std::sqrt((double)(a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
|
||||
@ -1194,114 +1189,64 @@ void CGameHandler::applyBattleEffects(BattleAttack & bat, std::shared_ptr<battle
|
||||
fireShield.push_back(std::make_pair(def, fireShieldDamage));
|
||||
}
|
||||
}
|
||||
void CGameHandler::handleConnection(std::set<PlayerColor> players, CConnection &c)
|
||||
{
|
||||
setThreadName("CGameHandler::handleConnection");
|
||||
|
||||
auto handleDisconnection = [&](const std::exception & e)
|
||||
void CGameHandler::handleClientDisconnection(std::shared_ptr<CConnection> c)
|
||||
{
|
||||
for(auto playerConns : connections)
|
||||
{
|
||||
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)
|
||||
for(auto conn : playerConns.second)
|
||||
{
|
||||
if(!serverShuttingDown && playerConn.second == &c)
|
||||
if(lobby->state != EServerState::SHUTDOWN && conn == c)
|
||||
{
|
||||
vstd::erase_if_present(playerConns.second, conn);
|
||||
if(playerConns.second.size())
|
||||
continue;
|
||||
PlayerCheated pc;
|
||||
pc.player = playerConn.first;
|
||||
pc.player = playerConns.first;
|
||||
pc.losingCheatCode = true;
|
||||
sendAndApply(&pc);
|
||||
checkVictoryLossConditionsForPlayer(playerConn.first);
|
||||
checkVictoryLossConditionsForPlayer(playerConns.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGameHandler::handleReceivedPack(CPackForServer * pack)
|
||||
{
|
||||
//prepare struct informing that action was applied
|
||||
auto sendPackageResponse = [&](bool succesfullyApplied)
|
||||
{
|
||||
PackageApplied applied;
|
||||
applied.player = pack->player;
|
||||
applied.result = succesfullyApplied;
|
||||
applied.packType = typeList.getTypeID(pack);
|
||||
applied.requestID = pack->requestID;
|
||||
pack->c->sendPack(&applied);
|
||||
};
|
||||
|
||||
try
|
||||
CBaseForGHApply * apply = applier->getApplier(typeList.getTypeID(pack)); //and appropriate applier object
|
||||
if(isBlockedByQueries(pack, pack->player))
|
||||
{
|
||||
while(1)//server should never shut connection first //was: while(!end2)
|
||||
{
|
||||
CPack *pack = nullptr;
|
||||
PlayerColor player = PlayerColor::NEUTRAL;
|
||||
si32 requestID = -999;
|
||||
int packType = 0;
|
||||
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(*c.rmx);
|
||||
if(!c.connected)
|
||||
throw clientDisconnectedException();
|
||||
c >> player >> requestID >> pack; //get the package
|
||||
|
||||
if (!pack)
|
||||
{
|
||||
logGlobal->error("Received a null package marked as request %d from player %d", requestID, player);
|
||||
}
|
||||
else
|
||||
{
|
||||
packType = typeList.getTypeID(pack); //get the id of type
|
||||
|
||||
logGlobal->trace("Received client message (request %d by player %d (%s)) of type with ID=%d (%s).\n",
|
||||
requestID, player, player.getStr(), packType, typeid(*pack).name());
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
applied.packType = packType;
|
||||
applied.requestID = requestID;
|
||||
boost::unique_lock<boost::mutex> lock(*c.wmx);
|
||||
c << &applied;
|
||||
};
|
||||
CBaseForGHApply *apply = applier->getApplier(packType); //and appropriate applier object
|
||||
if(isBlockedByQueries(pack, player))
|
||||
{
|
||||
sendPackageResponse(false);
|
||||
}
|
||||
else if (apply)
|
||||
{
|
||||
const bool result = apply->applyOnGH(this, &c, pack, player);
|
||||
if (result)
|
||||
logGlobal->trace("Message %s successfully applied!", typeid(*pack).name());
|
||||
else
|
||||
complain((boost::format("Got false in applying %s... that request must have been fishy!")
|
||||
% typeid(*pack).name()).str());
|
||||
|
||||
sendPackageResponse(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->error("Message cannot be applied, cannot find applier (unregistered type)!");
|
||||
sendPackageResponse(false);
|
||||
}
|
||||
|
||||
vstd::clear_pointer(pack);
|
||||
}
|
||||
sendPackageResponse(false);
|
||||
}
|
||||
catch(boost::system::system_error &e) //for boost errors just log, not crash - probably client shut down connection
|
||||
else if(apply)
|
||||
{
|
||||
handleDisconnection(e);
|
||||
const bool result = apply->applyOnGH(this, pack);
|
||||
if(result)
|
||||
logGlobal->trace("Message %s successfully applied!", typeid(*pack).name());
|
||||
else
|
||||
complain((boost::format("Got false in applying %s... that request must have been fishy!")
|
||||
% typeid(*pack).name()).str());
|
||||
|
||||
sendPackageResponse(true);
|
||||
}
|
||||
catch(clientDisconnectedException & e)
|
||||
else
|
||||
{
|
||||
handleDisconnection(e);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
serverShuttingDown = true;
|
||||
handleException();
|
||||
throw;
|
||||
logGlobal->error("Message cannot be applied, cannot find applier (unregistered type)!");
|
||||
sendPackageResponse(false);
|
||||
}
|
||||
|
||||
logGlobal->error("Ended handling connection");
|
||||
vstd::clear_pointer(pack);
|
||||
}
|
||||
|
||||
int CGameHandler::moveStack(int stack, BattleHex dest)
|
||||
@ -1569,12 +1514,12 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
||||
return ret;
|
||||
}
|
||||
|
||||
CGameHandler::CGameHandler()
|
||||
CGameHandler::CGameHandler(CVCMIServer * lobby)
|
||||
: lobby(lobby)
|
||||
{
|
||||
QID = 1;
|
||||
//gs = nullptr;
|
||||
IObjectInterface::cb = this;
|
||||
applier = new CApplier<CBaseForGHApply>();
|
||||
applier = std::make_shared<CApplier<CBaseForGHApply>>();
|
||||
registerTypesServerPacks(*applier);
|
||||
visitObjectAfterVictory = false;
|
||||
|
||||
@ -1584,8 +1529,6 @@ CGameHandler::CGameHandler()
|
||||
CGameHandler::~CGameHandler()
|
||||
{
|
||||
delete spellEnv;
|
||||
delete applier;
|
||||
applier = nullptr;
|
||||
delete gs;
|
||||
}
|
||||
|
||||
@ -1988,16 +1931,9 @@ void CGameHandler::run(bool resume)
|
||||
LOG_TRACE_PARAMS(logGlobal, "resume=%d", resume);
|
||||
|
||||
using namespace boost::posix_time;
|
||||
for (CConnection *cc : conns)
|
||||
for (auto cc : lobby->connections)
|
||||
{
|
||||
if (!resume)
|
||||
{
|
||||
(*cc) << gs->initialOpts; // gs->scenarioOps
|
||||
}
|
||||
|
||||
std::set<PlayerColor> players;
|
||||
(*cc) >> players; //how many players will be handled at that client
|
||||
|
||||
auto players = lobby->getAllClientPlayers(cc->connectionID);
|
||||
std::stringstream sbuffer;
|
||||
sbuffer << "Connection " << cc->connectionID << " will handle " << players.size() << " player: ";
|
||||
for (PlayerColor color : players)
|
||||
@ -2005,30 +1941,15 @@ void CGameHandler::run(bool resume)
|
||||
sbuffer << color << " ";
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(gsm);
|
||||
if(!color.isSpectator()) // there can be more than one spectator
|
||||
connections[color] = cc;
|
||||
connections[color].insert(cc);
|
||||
}
|
||||
}
|
||||
logGlobal->info(sbuffer.str());
|
||||
|
||||
cc->addStdVecItems(gs);
|
||||
cc->enableStackSendingByID();
|
||||
cc->disableSmartPointerSerialization();
|
||||
}
|
||||
|
||||
for (auto & elem : conns)
|
||||
{
|
||||
std::set<PlayerColor> pom;
|
||||
for (auto j = connections.cbegin(); j!=connections.cend();j++)
|
||||
if (j->second == elem)
|
||||
pom.insert(j->first);
|
||||
|
||||
boost::thread(std::bind(&CGameHandler::handleConnection,this,pom,std::ref(*elem)));
|
||||
}
|
||||
|
||||
auto playerTurnOrder = generatePlayerTurnOrder();
|
||||
|
||||
while(!serverShuttingDown)
|
||||
while(lobby->state == EServerState::GAMEPLAY)
|
||||
{
|
||||
if (!resume) newTurn();
|
||||
|
||||
@ -2069,12 +1990,14 @@ void CGameHandler::run(bool resume)
|
||||
|
||||
//wait till turn is done
|
||||
boost::unique_lock<boost::mutex> lock(states.mx);
|
||||
while(states.players.at(playerColor).makingTurn && !serverShuttingDown)
|
||||
while(states.players.at(playerColor).makingTurn && lobby->state == EServerState::GAMEPLAY)
|
||||
{
|
||||
static time_duration p = milliseconds(100);
|
||||
states.cv.timed_wait(lock, p);
|
||||
}
|
||||
}
|
||||
if(lobby->state != EServerState::GAMEPLAY)
|
||||
break;
|
||||
}
|
||||
}
|
||||
//additional check that game is not finished
|
||||
@ -2084,11 +2007,9 @@ void CGameHandler::run(bool resume)
|
||||
if (gs->players[player].status == EPlayerStatus::INGAME)
|
||||
activePlayer = true;
|
||||
}
|
||||
if (!activePlayer)
|
||||
serverShuttingDown = true;
|
||||
if(!activePlayer)
|
||||
lobby->state = EServerState::GAMEPLAY_ENDED;
|
||||
}
|
||||
while(conns.size() && (*conns.begin())->isOpen())
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(5)); //give time client to close socket
|
||||
}
|
||||
|
||||
std::list<PlayerColor> CGameHandler::generatePlayerTurnOrder() const
|
||||
@ -2619,12 +2540,12 @@ void CGameHandler::changeSpells(const CGHeroInstance * hero, bool give, const st
|
||||
sendAndApply(&cs);
|
||||
}
|
||||
|
||||
void CGameHandler::sendMessageTo(CConnection &c, const std::string &message)
|
||||
void CGameHandler::sendMessageTo(std::shared_ptr<CConnection> c, const std::string &message)
|
||||
{
|
||||
SystemMessage sm;
|
||||
sm.text = message;
|
||||
boost::unique_lock<boost::mutex> lock(*c.wmx);
|
||||
c << &sm;
|
||||
boost::unique_lock<boost::mutex> lock(*c->mutexWrite);
|
||||
*(c.get()) << &sm;
|
||||
}
|
||||
|
||||
void CGameHandler::giveHeroBonus(GiveBonus * bonus)
|
||||
@ -2773,59 +2694,59 @@ void CGameHandler::heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)
|
||||
}
|
||||
}
|
||||
|
||||
void CGameHandler::sendToAllClients(CPackForClient * info)
|
||||
void CGameHandler::sendToAllClients(CPackForClient * pack)
|
||||
{
|
||||
logNetwork->trace("Sending to all clients a package of type %s", typeid(*info).name());
|
||||
for (auto & elem : conns)
|
||||
logNetwork->trace("\tSending to all clients: %s", typeid(*pack).name());
|
||||
for (auto c : lobby->connections)
|
||||
{
|
||||
if(!elem->isOpen())
|
||||
if(!c->isOpen())
|
||||
continue;
|
||||
|
||||
boost::unique_lock<boost::mutex> lock(*(elem)->wmx);
|
||||
*elem << info;
|
||||
c->sendPack(pack);
|
||||
}
|
||||
}
|
||||
|
||||
void CGameHandler::sendAndApply(CPackForClient * info)
|
||||
void CGameHandler::sendAndApply(CPackForClient * pack)
|
||||
{
|
||||
sendToAllClients(info);
|
||||
gs->apply(info);
|
||||
sendToAllClients(pack);
|
||||
gs->apply(pack);
|
||||
logNetwork->trace("\tApplied on gs: %s", typeid(*pack).name());
|
||||
}
|
||||
|
||||
void CGameHandler::applyAndSend(CPackForClient * info)
|
||||
void CGameHandler::applyAndSend(CPackForClient * pack)
|
||||
{
|
||||
gs->apply(info);
|
||||
sendToAllClients(info);
|
||||
gs->apply(pack);
|
||||
sendToAllClients(pack);
|
||||
}
|
||||
|
||||
void CGameHandler::sendAndApply(CGarrisonOperationPack * info)
|
||||
void CGameHandler::sendAndApply(CGarrisonOperationPack * pack)
|
||||
{
|
||||
sendAndApply(static_cast<CPackForClient*>(info));
|
||||
sendAndApply(static_cast<CPackForClient *>(pack));
|
||||
checkVictoryLossConditionsForAll();
|
||||
}
|
||||
|
||||
void CGameHandler::sendAndApply(SetResources * info)
|
||||
void CGameHandler::sendAndApply(SetResources * pack)
|
||||
{
|
||||
sendAndApply(static_cast<CPackForClient*>(info));
|
||||
checkVictoryLossConditionsForPlayer(info->player);
|
||||
sendAndApply(static_cast<CPackForClient *>(pack));
|
||||
checkVictoryLossConditionsForPlayer(pack->player);
|
||||
}
|
||||
|
||||
void CGameHandler::sendAndApply(NewStructures * info)
|
||||
void CGameHandler::sendAndApply(NewStructures * pack)
|
||||
{
|
||||
sendAndApply(static_cast<CPackForClient*>(info));
|
||||
checkVictoryLossConditionsForPlayer(getTown(info->tid)->tempOwner);
|
||||
sendAndApply(static_cast<CPackForClient *>(pack));
|
||||
checkVictoryLossConditionsForPlayer(getTown(pack->tid)->tempOwner);
|
||||
}
|
||||
|
||||
void CGameHandler::save(const std::string & filename)
|
||||
{
|
||||
logGlobal->info("Saving to %s", filename);
|
||||
logGlobal->info("Loading from %s", filename);
|
||||
const auto stem = FileInfo::GetPathStem(filename);
|
||||
const auto savefname = stem.to_string() + ".vsgm1";
|
||||
CResourceHandler::get("local")->createResource(savefname);
|
||||
|
||||
{
|
||||
logGlobal->info("Ordering clients to serialize...");
|
||||
SaveGame sg(savefname);
|
||||
SaveGameClient sg(savefname);
|
||||
sendToAllClients(&sg);
|
||||
}
|
||||
|
||||
@ -2845,34 +2766,26 @@ void CGameHandler::save(const std::string & filename)
|
||||
}
|
||||
}
|
||||
|
||||
void CGameHandler::close()
|
||||
void CGameHandler::load(const std::string & filename)
|
||||
{
|
||||
logGlobal->info("We have been requested to close.");
|
||||
serverShuttingDown = true;
|
||||
logGlobal->info("Loading from %s", filename);
|
||||
const auto stem = FileInfo::GetPathStem(filename);
|
||||
|
||||
for (auto & elem : conns)
|
||||
try
|
||||
{
|
||||
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;
|
||||
CLoadFile lf(*CResourceHandler::get("local")->getResourceName(ResourceID(stem.to_string(), EResType::SERVER_SAVEGAME)), MINIMAL_SERIALIZATION_VERSION);
|
||||
loadCommonState(lf);
|
||||
logGlobal->info("Loading server state");
|
||||
lf >> *this;
|
||||
}
|
||||
logGlobal->info("Game has been successfully loaded!");
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
logGlobal->error("Failed to load game: %s", e.what());
|
||||
}
|
||||
gs->updateOnLoad(lobby->si.get());
|
||||
}
|
||||
|
||||
bool CGameHandler::arrangeStacks(ObjectInstanceID id1, ObjectInstanceID id2, ui8 what, SlotID p1, SlotID p2, si32 val, PlayerColor player)
|
||||
@ -3014,11 +2927,11 @@ bool CGameHandler::arrangeStacks(ObjectInstanceID id1, ObjectInstanceID id2, ui8
|
||||
return true;
|
||||
}
|
||||
|
||||
PlayerColor CGameHandler::getPlayerAt(CConnection *c) const
|
||||
PlayerColor CGameHandler::getPlayerAt(std::shared_ptr<CConnection> c) const
|
||||
{
|
||||
std::set<PlayerColor> all;
|
||||
for (auto i=connections.cbegin(); i!=connections.cend(); i++)
|
||||
if (i->second == c)
|
||||
if(vstd::contains(i->second, c))
|
||||
all.insert(i->first);
|
||||
|
||||
switch(all.size())
|
||||
@ -4567,7 +4480,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
void CGameHandler::playerMessage(PlayerColor player, const std::string &message, ObjectInstanceID currObj)
|
||||
{
|
||||
bool cheated = true;
|
||||
PlayerMessage temp_message(player, message, ObjectInstanceID(-1)); // don't inform other client on selected object
|
||||
PlayerMessageClient temp_message(player, message);
|
||||
sendAndApply(&temp_message);
|
||||
|
||||
std::vector<std::string> cheat;
|
||||
@ -5304,51 +5217,9 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
|
||||
}
|
||||
}
|
||||
|
||||
if (p->human)
|
||||
if(p->human)
|
||||
{
|
||||
serverShuttingDown = true;
|
||||
|
||||
if (gs->scenarioOps->campState)
|
||||
{
|
||||
std::vector<CGHeroInstance *> crossoverHeroes;
|
||||
for (CGHeroInstance * hero : gs->map->heroesOnMap)
|
||||
{
|
||||
if (hero->tempOwner == player)
|
||||
{
|
||||
// keep all heroes from the winning player
|
||||
crossoverHeroes.push_back(hero);
|
||||
}
|
||||
else if (vstd::contains(gs->scenarioOps->campState->getCurrentScenario().keepHeroes, HeroTypeID(hero->subID)))
|
||||
{
|
||||
// keep hero whether lost or won (like Xeron in AB campaign)
|
||||
crossoverHeroes.push_back(hero);
|
||||
}
|
||||
}
|
||||
// keep lost heroes which are in heroes pool
|
||||
for (auto & heroPair : gs->hpool.heroesPool)
|
||||
{
|
||||
if (vstd::contains(gs->scenarioOps->campState->getCurrentScenario().keepHeroes, HeroTypeID(heroPair.first)))
|
||||
{
|
||||
crossoverHeroes.push_back(heroPair.second.get());
|
||||
}
|
||||
}
|
||||
|
||||
gs->scenarioOps->campState->setCurrentMapAsConquered(crossoverHeroes);
|
||||
|
||||
//Request clients to change connection mode
|
||||
PrepareForAdvancingCampaign pfac;
|
||||
sendAndApply(&pfac);
|
||||
//Change connection mode
|
||||
if (getPlayer(player)->human && getStartInfo()->campState)
|
||||
{
|
||||
for (auto connection : conns)
|
||||
connection->prepareForSendingHeroes();
|
||||
}
|
||||
|
||||
UpdateCampaignState ucs;
|
||||
ucs.camp = gs->scenarioOps->campState;
|
||||
sendAndApply(&ucs);
|
||||
}
|
||||
lobby->state = EServerState::GAMEPLAY_ENDED;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -6856,9 +6727,9 @@ ServerSpellCastEnvironment::ServerSpellCastEnvironment(CGameHandler * gh): gh(gh
|
||||
|
||||
}
|
||||
|
||||
void ServerSpellCastEnvironment::sendAndApply(CPackForClient * info) const
|
||||
void ServerSpellCastEnvironment::sendAndApply(CPackForClient * pack) const
|
||||
{
|
||||
gh->sendAndApply(info);
|
||||
gh->sendAndApply(pack);
|
||||
}
|
||||
|
||||
CRandomGenerator & ServerSpellCastEnvironment::getRandomGenerator() const
|
||||
|
@ -30,6 +30,9 @@ class IMarket;
|
||||
|
||||
class SpellCastEnvironment;
|
||||
|
||||
template<typename T> class CApplier;
|
||||
class CBaseForGHApply;
|
||||
|
||||
struct PlayerStatus
|
||||
{
|
||||
bool makingTurn;
|
||||
@ -74,6 +77,8 @@ struct CasualtiesAfterBattle
|
||||
|
||||
class CGameHandler : public IGameCallback, CBattleInfoCallback
|
||||
{
|
||||
CVCMIServer * lobby;
|
||||
std::shared_ptr<CApplier<CBaseForGHApply>> applier;
|
||||
public:
|
||||
using FireShieldInfo = std::vector<std::pair<const CStack *, int64_t>>;
|
||||
//use enums as parameters, because doMove(sth, true, false, true) is not readable
|
||||
@ -81,9 +86,8 @@ public:
|
||||
enum EVisitDest {VISIT_DEST, DONT_VISIT_DEST};
|
||||
enum ELEaveTile {LEAVING_TILE, REMAINING_ON_TILE};
|
||||
|
||||
std::map<PlayerColor, CConnection*> connections; //player color -> connection to client with interface of that player
|
||||
std::map<PlayerColor, std::set<std::shared_ptr<CConnection>>> connections; //player color -> connection to client with interface of that player
|
||||
PlayerStatuses states; //player color -> player state
|
||||
std::set<CConnection*> conns;
|
||||
|
||||
//queries stuff
|
||||
boost::recursive_mutex gsm;
|
||||
@ -111,7 +115,7 @@ public:
|
||||
void setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town);
|
||||
void setBattleResult(BattleResult::EResult resultType, int victoriusSide);
|
||||
|
||||
CGameHandler();
|
||||
CGameHandler(CVCMIServer * lobby);
|
||||
~CGameHandler();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -189,8 +193,9 @@ public:
|
||||
void commitPackage(CPackForClient *pack) override;
|
||||
|
||||
void init(StartInfo *si);
|
||||
void handleConnection(std::set<PlayerColor> players, CConnection &c);
|
||||
PlayerColor getPlayerAt(CConnection *c) const;
|
||||
void handleClientDisconnection(std::shared_ptr<CConnection> c);
|
||||
void handleReceivedPack(CPackForServer * pack);
|
||||
PlayerColor getPlayerAt(std::shared_ptr<CConnection> c) const;
|
||||
|
||||
void playerMessage(PlayerColor player, const std::string &message, ObjectInstanceID currObj);
|
||||
void updateGateState();
|
||||
@ -225,8 +230,8 @@ public:
|
||||
bool disbandCreature( ObjectInstanceID id, SlotID pos );
|
||||
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 load(const std::string &fname);
|
||||
|
||||
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
|
||||
@ -248,13 +253,13 @@ public:
|
||||
}
|
||||
|
||||
void sendMessageToAll(const std::string &message);
|
||||
void sendMessageTo(CConnection &c, const std::string &message);
|
||||
void sendToAllClients(CPackForClient * info);
|
||||
void sendAndApply(CPackForClient * info) override;
|
||||
void applyAndSend(CPackForClient * info);
|
||||
void sendAndApply(CGarrisonOperationPack * info);
|
||||
void sendAndApply(SetResources * info);
|
||||
void sendAndApply(NewStructures * info);
|
||||
void sendMessageTo(std::shared_ptr<CConnection> c, const std::string &message);
|
||||
void sendToAllClients(CPackForClient * pack);
|
||||
void sendAndApply(CPackForClient * pack) override;
|
||||
void applyAndSend(CPackForClient * pack);
|
||||
void sendAndApply(CGarrisonOperationPack * pack);
|
||||
void sendAndApply(SetResources * pack);
|
||||
void sendAndApply(NewStructures * pack);
|
||||
|
||||
struct FinishingBattleHelper
|
||||
{
|
||||
@ -308,10 +313,6 @@ private:
|
||||
void checkVictoryLossConditionsForAll();
|
||||
};
|
||||
|
||||
class clientDisconnectedException : public std::exception
|
||||
{
|
||||
|
||||
};
|
||||
class ExceptionNotAllowedAction : public std::exception
|
||||
{
|
||||
|
||||
|
@ -8,6 +8,7 @@ set(server_SRCS
|
||||
CQuery.cpp
|
||||
CVCMIServer.cpp
|
||||
NetPacksServer.cpp
|
||||
NetPacksLobbyServer.cpp
|
||||
)
|
||||
|
||||
set(server_HEADERS
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,112 +1,105 @@
|
||||
/*
|
||||
* CVCMIServer.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
class CMapInfo;
|
||||
|
||||
class CConnection;
|
||||
struct CPackForSelectionScreen;
|
||||
class CGameHandler;
|
||||
struct SharedMemory;
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace asio
|
||||
{
|
||||
namespace ip
|
||||
{
|
||||
class tcp;
|
||||
}
|
||||
|
||||
#if BOOST_VERSION >= 106600 // Boost version >= 1.66
|
||||
class io_context;
|
||||
typedef io_context io_service;
|
||||
#else
|
||||
class io_service;
|
||||
#endif
|
||||
|
||||
template <typename Protocol> class stream_socket_service;
|
||||
template <typename Protocol,typename StreamSocketService>
|
||||
class basic_stream_socket;
|
||||
|
||||
template <typename Protocol> class socket_acceptor_service;
|
||||
template <typename Protocol,typename SocketAcceptorService>
|
||||
class basic_socket_acceptor;
|
||||
}
|
||||
};
|
||||
|
||||
typedef boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, boost::asio::socket_acceptor_service<boost::asio::ip::tcp> > TAcceptor;
|
||||
typedef boost::asio::basic_stream_socket < boost::asio::ip::tcp , boost::asio::stream_socket_service<boost::asio::ip::tcp> > TSocket;
|
||||
|
||||
class CVCMIServer
|
||||
{
|
||||
ui16 port;
|
||||
boost::asio::io_service *io;
|
||||
TAcceptor * acceptor;
|
||||
SharedMemory * shared;
|
||||
|
||||
CConnection *firstConnection;
|
||||
public:
|
||||
CVCMIServer();
|
||||
~CVCMIServer();
|
||||
|
||||
void start();
|
||||
std::shared_ptr<CGameHandler> initGhFromHostingConnection(CConnection &c);
|
||||
|
||||
void newGame();
|
||||
void loadGame();
|
||||
void newPregame();
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
static void create();
|
||||
#endif
|
||||
};
|
||||
|
||||
struct StartInfo;
|
||||
class CPregameServer
|
||||
{
|
||||
public:
|
||||
CConnection *host;
|
||||
int listeningThreads;
|
||||
std::set<CConnection *> connections;
|
||||
std::list<CPackForSelectionScreen*> toAnnounce;
|
||||
boost::recursive_mutex mx;
|
||||
|
||||
TAcceptor *acceptor;
|
||||
TSocket *upcomingConnection;
|
||||
|
||||
const CMapInfo *curmap;
|
||||
StartInfo *curStartInfo;
|
||||
|
||||
CPregameServer(CConnection *Host, TAcceptor *Acceptor = nullptr);
|
||||
~CPregameServer();
|
||||
|
||||
void run();
|
||||
|
||||
void processPack(CPackForSelectionScreen * pack);
|
||||
void handleConnection(CConnection *cpc);
|
||||
void connectionAccepted(const boost::system::error_code& ec);
|
||||
void initConnection(CConnection *c);
|
||||
|
||||
void start_async_accept();
|
||||
|
||||
enum { INVALID, RUNNING, ENDING_WITHOUT_START, ENDING_AND_STARTING_GAME
|
||||
} state;
|
||||
|
||||
void announceTxt(const std::string &txt, const std::string &playerName = "system");
|
||||
void announcePack(const CPackForSelectionScreen &pack);
|
||||
|
||||
void sendPack(CConnection * pc, const CPackForSelectionScreen & pack);
|
||||
void startListeningThread(CConnection * pc);
|
||||
};
|
||||
|
||||
extern boost::program_options::variables_map cmdLineOptions;
|
||||
/*
|
||||
* CVCMIServer.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "../lib/StartInfo.h"
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
class CMapInfo;
|
||||
|
||||
struct CPackForLobby;
|
||||
class CGameHandler;
|
||||
struct SharedMemory;
|
||||
|
||||
struct StartInfo;
|
||||
struct LobbyInfo;
|
||||
class PlayerSettings;
|
||||
class PlayerColor;
|
||||
|
||||
template<typename T> class CApplier;
|
||||
class CBaseForServerApply;
|
||||
class CBaseForGHApply;
|
||||
|
||||
enum class EServerState : ui8
|
||||
{
|
||||
LOBBY,
|
||||
GAMEPLAY,
|
||||
GAMEPLAY_ENDED,
|
||||
SHUTDOWN
|
||||
};
|
||||
|
||||
class CVCMIServer : public LobbyInfo
|
||||
{
|
||||
std::atomic<bool> restartGameplay; // FIXME: this is just a hack
|
||||
std::shared_ptr<boost::asio::io_service> io;
|
||||
std::shared_ptr<TAcceptor> acceptor;
|
||||
std::shared_ptr<TSocket> upcomingConnection;
|
||||
std::list<CPackForLobby *> announceQueue;
|
||||
boost::recursive_mutex mx;
|
||||
std::shared_ptr<CApplier<CBaseForServerApply>> applier;
|
||||
|
||||
public:
|
||||
std::shared_ptr<CGameHandler> gh;
|
||||
std::atomic<EServerState> state;
|
||||
ui16 port;
|
||||
|
||||
boost::program_options::variables_map cmdLineOptions;
|
||||
std::set<std::shared_ptr<CConnection>> connections;
|
||||
std::atomic<int> currentClientId;
|
||||
std::atomic<ui8> currentPlayerId;
|
||||
std::shared_ptr<CConnection> hostClient;
|
||||
|
||||
CVCMIServer(boost::program_options::variables_map & opts);
|
||||
~CVCMIServer();
|
||||
void run();
|
||||
void prepareToStartGame();
|
||||
void startGameImmidiately();
|
||||
|
||||
void startAsyncAccept();
|
||||
void connectionAccepted(const boost::system::error_code & ec);
|
||||
void threadHandleClient(std::shared_ptr<CConnection> c);
|
||||
void threadAnnounceLobby();
|
||||
void handleReceivedPack(CPackForLobby * pack);
|
||||
|
||||
void announcePack(CPackForLobby * pack);
|
||||
bool passHost(int toConnectionId);
|
||||
|
||||
void announceTxt(const std::string & txt, const std::string & playerName = "system");
|
||||
void addToAnnounceQueue(CPackForLobby * pack);
|
||||
|
||||
void setPlayerConnectedId(PlayerSettings & pset, ui8 player) const;
|
||||
void updateStartInfoOnMapChange(std::shared_ptr<CMapInfo> mapInfo, std::shared_ptr<CMapGenOptions> mapGenOpt = {});
|
||||
|
||||
void clientConnected(std::shared_ptr<CConnection> c, std::vector<std::string> & names, std::string uuid, StartInfo::EMode mode);
|
||||
void clientDisconnected(std::shared_ptr<CConnection> c);
|
||||
|
||||
void updateAndPropagateLobbyState();
|
||||
|
||||
// Work with LobbyInfo
|
||||
void setPlayer(PlayerColor clickedColor);
|
||||
void optionNextHero(PlayerColor player, int dir); //dir == -1 or +1
|
||||
int nextAllowedHero(PlayerColor player, int min, int max, int incl, int dir);
|
||||
bool canUseThisHero(PlayerColor player, int ID);
|
||||
std::vector<int> getUsedHeroes();
|
||||
void optionNextBonus(PlayerColor player, int dir); //dir == -1 or +1
|
||||
void optionNextCastle(PlayerColor player, int dir); //dir == -1 or +
|
||||
|
||||
// Campaigns
|
||||
void setCampaignMap(int mapId);
|
||||
void setCampaignBonus(int bonusId);
|
||||
|
||||
ui8 getIdOfFirstUnallocatedPlayer() const;
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
static void create();
|
||||
#endif
|
||||
};
|
||||
|
250
server/NetPacksLobbyServer.cpp
Normal file
250
server/NetPacksLobbyServer.cpp
Normal file
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* NetPacksLobbyServer.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CVCMIServer.h"
|
||||
#include "CGameHandler.h"
|
||||
|
||||
#include "../lib/NetPacksLobby.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "../lib/StartInfo.h"
|
||||
|
||||
// Campaigns
|
||||
#include "../lib/mapping/CCampaignHandler.h"
|
||||
#include "../lib/mapping/CMapService.h"
|
||||
#include "../lib/mapping/CMapInfo.h"
|
||||
|
||||
bool CLobbyPackToServer::checkClientPermissions(CVCMIServer * srv) const
|
||||
{
|
||||
return srv->isClientHost(c->connectionID);
|
||||
}
|
||||
|
||||
void CLobbyPackToServer::applyOnServerAfterAnnounce(CVCMIServer * srv)
|
||||
{
|
||||
// Propogate options after every CLobbyPackToServer
|
||||
srv->updateAndPropagateLobbyState();
|
||||
}
|
||||
|
||||
bool LobbyClientConnected::checkClientPermissions(CVCMIServer * srv) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LobbyClientConnected::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
srv->clientConnected(c, names, uuid, mode);
|
||||
// Server need to pass some data to newly connected client
|
||||
clientId = c->connectionID;
|
||||
mode = srv->si->mode;
|
||||
hostClientId = srv->hostClientId;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LobbyClientConnected::applyOnServerAfterAnnounce(CVCMIServer * srv)
|
||||
{
|
||||
// FIXME: we need to avoid senting something to client that not yet get answer for LobbyClientConnected
|
||||
// Until UUID set we only pass LobbyClientConnected to this client
|
||||
c->uuid = uuid;
|
||||
srv->updateAndPropagateLobbyState();
|
||||
}
|
||||
|
||||
bool LobbyClientDisconnected::checkClientPermissions(CVCMIServer * srv) const
|
||||
{
|
||||
if(clientId != c->connectionID)
|
||||
return false;
|
||||
|
||||
if(shutdownServer)
|
||||
{
|
||||
if(!srv->cmdLineOptions.count("run-by-client"))
|
||||
return false;
|
||||
|
||||
if(c->uuid != srv->cmdLineOptions["uuid"].as<std::string>())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LobbyClientDisconnected::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
srv->clientDisconnected(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LobbyClientDisconnected::applyOnServerAfterAnnounce(CVCMIServer * srv)
|
||||
{
|
||||
if(c->isOpen())
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(*c->mutexWrite);
|
||||
c->close();
|
||||
c->connected = false;
|
||||
}
|
||||
|
||||
if(shutdownServer)
|
||||
{
|
||||
logNetwork->info("Client requested shutdown, server will close itself...");
|
||||
srv->state = EServerState::SHUTDOWN;
|
||||
return;
|
||||
}
|
||||
else if(srv->connections.empty())
|
||||
{
|
||||
logNetwork->error("Last connection lost, server will close itself...");
|
||||
srv->state = EServerState::SHUTDOWN;
|
||||
}
|
||||
else if(c == srv->hostClient)
|
||||
{
|
||||
auto ph = new LobbyChangeHost();
|
||||
auto newHost = *RandomGeneratorUtil::nextItem(srv->connections, CRandomGenerator::getDefault());
|
||||
ph->newHostConnectionId = newHost->connectionID;
|
||||
srv->addToAnnounceQueue(ph);
|
||||
}
|
||||
srv->updateAndPropagateLobbyState();
|
||||
}
|
||||
|
||||
bool LobbyChatMessage::checkClientPermissions(CVCMIServer * srv) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LobbySetMap::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
srv->updateStartInfoOnMapChange(mapInfo, mapGenOpts);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LobbySetCampaign::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
srv->si->mapname = ourCampaign->camp->header.filename;
|
||||
srv->si->mode = StartInfo::CAMPAIGN;
|
||||
srv->si->campState = ourCampaign;
|
||||
srv->si->turnTime = 0;
|
||||
bool isCurrentMapConquerable = ourCampaign->currentMap && ourCampaign->camp->conquerable(*ourCampaign->currentMap);
|
||||
for(int i = 0; i < ourCampaign->camp->scenarios.size(); i++)
|
||||
{
|
||||
if(ourCampaign->camp->conquerable(i))
|
||||
{
|
||||
if(!isCurrentMapConquerable || (isCurrentMapConquerable && i == *ourCampaign->currentMap))
|
||||
{
|
||||
srv->setCampaignMap(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LobbySetCampaignMap::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
srv->setCampaignMap(mapId);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LobbySetCampaignBonus::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
srv->setCampaignBonus(bonusId);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LobbyGuiAction::checkClientPermissions(CVCMIServer * srv) const
|
||||
{
|
||||
return srv->isClientHost(c->connectionID);
|
||||
}
|
||||
|
||||
bool LobbyStartGame::checkClientPermissions(CVCMIServer * srv) const
|
||||
{
|
||||
return srv->isClientHost(c->connectionID);
|
||||
}
|
||||
|
||||
bool LobbyStartGame::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
try
|
||||
{
|
||||
srv->verifyStateBeforeStart(true);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Server will prepare gamestate and we announce StartInfo to clients
|
||||
srv->prepareToStartGame();
|
||||
initializedStartInfo = std::make_shared<StartInfo>(*srv->gh->getStartInfo(true));
|
||||
return true;
|
||||
}
|
||||
|
||||
void LobbyStartGame::applyOnServerAfterAnnounce(CVCMIServer * srv)
|
||||
{
|
||||
srv->startGameImmidiately();
|
||||
}
|
||||
|
||||
bool LobbyChangeHost::checkClientPermissions(CVCMIServer * srv) const
|
||||
{
|
||||
return srv->isClientHost(c->connectionID);
|
||||
}
|
||||
|
||||
bool LobbyChangeHost::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LobbyChangeHost::applyOnServerAfterAnnounce(CVCMIServer * srv)
|
||||
{
|
||||
return srv->passHost(newHostConnectionId);
|
||||
}
|
||||
|
||||
bool LobbyChangePlayerOption::checkClientPermissions(CVCMIServer * srv) const
|
||||
{
|
||||
if(srv->isClientHost(c->connectionID))
|
||||
return true;
|
||||
|
||||
if(vstd::contains(srv->getAllClientPlayers(c->connectionID), color))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LobbyChangePlayerOption::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
switch(what)
|
||||
{
|
||||
case TOWN:
|
||||
srv->optionNextCastle(color, direction);
|
||||
break;
|
||||
case HERO:
|
||||
srv->optionNextHero(color, direction);
|
||||
break;
|
||||
case BONUS:
|
||||
srv->optionNextBonus(color, direction);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LobbySetPlayer::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
srv->setPlayer(clickedColor);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LobbySetTurnTime::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
srv->si->turnTime = turnTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LobbySetDifficulty::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
srv->si->difficulty = vstd::abetween(difficulty, 0, 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LobbyForceSetPlayer::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
srv->si->playerInfos[targetPlayerColor].connectedPlayerIDs.insert(targetConnectedPlayer);
|
||||
return true;
|
||||
}
|
@ -31,7 +31,7 @@ void CPackForServer::throwNotAllowedAction()
|
||||
if(c)
|
||||
{
|
||||
SystemMessage temp_message("You are not allowed to perform this action!");
|
||||
*c << &temp_message;
|
||||
c->sendPack(&temp_message);
|
||||
}
|
||||
logNetwork->error("Player is not allowed to perform this action!");
|
||||
throw ExceptionNotAllowedAction();
|
||||
@ -45,7 +45,7 @@ void CPackForServer::wrongPlayerMessage(CGameHandler * gh, PlayerColor expectedp
|
||||
if(c)
|
||||
{
|
||||
SystemMessage temp_message(oss.str());
|
||||
*c << &temp_message;
|
||||
c->sendPack(&temp_message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,18 +92,6 @@ bool CommitPackage::applyGh(CGameHandler * gh)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CloseServer::applyGh(CGameHandler * gh)
|
||||
{
|
||||
gh->close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LeaveGame::applyGh(CGameHandler * gh)
|
||||
{
|
||||
gh->playerLeftGame(c->connectionID);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EndTurn::applyGh(CGameHandler * gh)
|
||||
{
|
||||
PlayerColor player = GS(gh)->currentPlayer;
|
||||
@ -292,7 +280,7 @@ bool QueryReply::applyGh(CGameHandler * gh)
|
||||
auto playerToConnection = gh->connections.find(player);
|
||||
if(playerToConnection == gh->connections.end())
|
||||
throwAndCompain(gh, "No such player!");
|
||||
if(playerToConnection->second != c)
|
||||
if(!vstd::contains(playerToConnection->second, c))
|
||||
throwAndCompain(gh, "Message came from wrong connection!");
|
||||
if(qid == QueryID(-1))
|
||||
throwAndCompain(gh, "Cannot answer the query with id -1!");
|
||||
@ -312,17 +300,18 @@ bool MakeAction::applyGh(CGameHandler * gh)
|
||||
if(ba.actionType != EActionType::WALK && ba.actionType != EActionType::END_TACTIC_PHASE
|
||||
&& ba.actionType != EActionType::RETREAT && ba.actionType != EActionType::SURRENDER)
|
||||
throwNotAllowedAction();
|
||||
if(gh->connections[b->sides[b->tacticsSide].color] != c)
|
||||
if(!vstd::contains(gh->connections[b->sides[b->tacticsSide].color], c))
|
||||
throwNotAllowedAction();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto active = b->battleActiveUnit();
|
||||
if(!active) throwNotAllowedAction();
|
||||
if(!active)
|
||||
throwNotAllowedAction();
|
||||
auto unitOwner = b->battleGetOwner(active);
|
||||
if(gh->connections[unitOwner] != c) throwNotAllowedAction();
|
||||
if(!vstd::contains(gh->connections[unitOwner], c))
|
||||
throwNotAllowedAction();
|
||||
}
|
||||
|
||||
return gh->makeBattleAction(ba);
|
||||
}
|
||||
|
||||
@ -337,7 +326,7 @@ bool MakeCustomAction::applyGh(CGameHandler * gh)
|
||||
if(!active)
|
||||
throwNotAllowedAction();
|
||||
auto unitOwner = b->battleGetOwner(active);
|
||||
if(gh->connections[unitOwner] != c)
|
||||
if(!vstd::contains(gh->connections[unitOwner], c))
|
||||
throwNotAllowedAction();
|
||||
if(ba.actionType != EActionType::HERO_SPELL)
|
||||
throwNotAllowedAction();
|
||||
@ -373,7 +362,7 @@ bool PlayerMessage::applyGh(CGameHandler * gh)
|
||||
if(!player.isSpectator()) // TODO: clearly not a great way to verify permissions
|
||||
{
|
||||
throwOnWrongPlayer(gh, player);
|
||||
if(gh->getPlayerAt(c) != player)
|
||||
if(gh->getPlayerAt(this->c) != player)
|
||||
throwNotAllowedAction();
|
||||
}
|
||||
gh->playerMessage(player, text, currObj);
|
||||
|
@ -44,9 +44,9 @@ public:
|
||||
IObjectInterface::cb = nullptr;
|
||||
}
|
||||
|
||||
void sendAndApply(CPackForClient * info) const override
|
||||
void sendAndApply(CPackForClient * pack) const override
|
||||
{
|
||||
gameState->apply(info);
|
||||
gameState->apply(pack);
|
||||
}
|
||||
|
||||
void complain(const std::string & problem) const
|
||||
@ -108,7 +108,7 @@ public:
|
||||
|
||||
PlayerSettings & pset = si.playerInfos[PlayerColor(i)];
|
||||
pset.color = PlayerColor(i);
|
||||
pset.playerID = i;
|
||||
pset.connectedPlayerIDs.insert(i);
|
||||
pset.name = "Player";
|
||||
|
||||
pset.castle = pinfo.defaultCastle();
|
||||
|
@ -32,7 +32,7 @@ void GameCallbackMock::commitPackage(CPackForClient * pack)
|
||||
sendAndApply(pack);
|
||||
}
|
||||
|
||||
void GameCallbackMock::sendAndApply(CPackForClient * info)
|
||||
void GameCallbackMock::sendAndApply(CPackForClient * pack)
|
||||
{
|
||||
upperCallback->sendAndApply(info);
|
||||
upperCallback->sendAndApply(pack);
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ public:
|
||||
|
||||
///useful callback methods
|
||||
void commitPackage(CPackForClient * pack) override;
|
||||
void sendAndApply(CPackForClient * info) override;
|
||||
void sendAndApply(CPackForClient * pack) override;
|
||||
private:
|
||||
const UpperCallback * upperCallback;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user