mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-22 22:13:35 +02:00
Merge pull request #2892 from Laserlicht/highscore_menu
Highscore Menu & end video
This commit is contained in:
commit
71a1ed816a
@ -37,7 +37,6 @@
|
||||
"vcmi.radialWheel.moveUnit" : "Přesunout jednotky do jiného oddílu",
|
||||
"vcmi.radialWheel.splitUnit" : "Rozdělit jednotku do jiné pozice",
|
||||
|
||||
"vcmi.mainMenu.highscoresNotImplemented" : "Omlouvám se, menu nejvyšší skóre ještě není implementováno\n",
|
||||
"vcmi.mainMenu.serverConnecting" : "Připojování...",
|
||||
"vcmi.mainMenu.serverAddressEnter" : "Zadejte adresu:",
|
||||
"vcmi.mainMenu.serverClosing" : "Zavírání...",
|
||||
|
@ -42,7 +42,6 @@
|
||||
"vcmi.radialWheel.moveUnit" : "Move creatures to another army",
|
||||
"vcmi.radialWheel.splitUnit" : "Split creature to another slot",
|
||||
|
||||
"vcmi.mainMenu.highscoresNotImplemented" : "Sorry, high scores menu is not implemented yet\n",
|
||||
"vcmi.mainMenu.serverConnecting" : "Connecting...",
|
||||
"vcmi.mainMenu.serverAddressEnter" : "Enter address:",
|
||||
"vcmi.mainMenu.serverClosing" : "Closing...",
|
||||
|
@ -30,7 +30,6 @@
|
||||
"vcmi.capitalColors.6" : "Turquoise",
|
||||
"vcmi.capitalColors.7" : "Rose",
|
||||
|
||||
"vcmi.mainMenu.highscoresNotImplemented" : "Désolé, le menu des meilleurs scores n'est pas encore implémenté\n",
|
||||
"vcmi.mainMenu.serverConnecting" : "Connexion...",
|
||||
"vcmi.mainMenu.serverAddressEnter" : "Entrez l'adresse :",
|
||||
"vcmi.mainMenu.serverClosing" : "Fermeture...",
|
||||
|
@ -41,7 +41,6 @@
|
||||
"vcmi.radialWheel.moveUnit" : "Verschieben der Kreatur in andere Armee",
|
||||
"vcmi.radialWheel.splitUnit" : "Aufsplitten der Kreatur in anderen Slot",
|
||||
|
||||
"vcmi.mainMenu.highscoresNotImplemented" : "Die Highscores sind aktuell noch nicht implementiert\n",
|
||||
"vcmi.mainMenu.serverConnecting" : "Verbinde...",
|
||||
"vcmi.mainMenu.serverAddressEnter" : "Addresse eingeben:",
|
||||
"vcmi.mainMenu.serverClosing" : "Trenne...",
|
||||
|
@ -36,7 +36,6 @@
|
||||
"vcmi.radialWheel.moveUnit" : "Przenieś stworzenia do innej armii",
|
||||
"vcmi.radialWheel.splitUnit" : "Podziel jednostkę do wybranego miejsca",
|
||||
|
||||
"vcmi.mainMenu.highscoresNotImplemented" : "Przepraszamy, najlepsze wyniki nie zostały jeszcze zaimplementowane\n",
|
||||
"vcmi.mainMenu.serverConnecting" : "Łączenie...",
|
||||
"vcmi.mainMenu.serverAddressEnter" : "Wprowadź adres:",
|
||||
"vcmi.mainMenu.serverClosing" : "Zamykanie...",
|
||||
|
@ -37,7 +37,6 @@
|
||||
"vcmi.radialWheel.moveUnit" : "Перемістити істоту до іншої армії",
|
||||
"vcmi.radialWheel.splitUnit" : "Розділити істоту в інший слот",
|
||||
|
||||
"vcmi.mainMenu.highscoresNotImplemented" : "Вибачте, таблицю рекордів ще не реалізовано\n",
|
||||
"vcmi.mainMenu.serverConnecting" : "Підключення...",
|
||||
"vcmi.mainMenu.serverAddressEnter" : "Вкажіть адресу:",
|
||||
"vcmi.mainMenu.serverClosing" : "Завершення...",
|
||||
|
@ -59,6 +59,7 @@ set(client_SRCS
|
||||
mainmenu/CMainMenu.cpp
|
||||
mainmenu/CPrologEpilogVideo.cpp
|
||||
mainmenu/CreditsScreen.cpp
|
||||
mainmenu/CHighScoreScreen.cpp
|
||||
|
||||
mapView/MapRenderer.cpp
|
||||
mapView/MapRendererContext.cpp
|
||||
@ -213,6 +214,7 @@ set(client_HEADERS
|
||||
mainmenu/CMainMenu.h
|
||||
mainmenu/CPrologEpilogVideo.h
|
||||
mainmenu/CreditsScreen.h
|
||||
mainmenu/CHighScoreScreen.h
|
||||
|
||||
mapView/IMapRendererContext.h
|
||||
mapView/IMapRendererObserver.h
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "gui/WindowHandler.h"
|
||||
|
||||
#include "mainmenu/CMainMenu.h"
|
||||
#include "mainmenu/CHighScoreScreen.h"
|
||||
|
||||
#include "mapView/mapHandler.h"
|
||||
|
||||
@ -1697,16 +1698,43 @@ void CPlayerInterface::showShipyardDialogOrProblemPopup(const IShipyard *obj)
|
||||
|
||||
void CPlayerInterface::requestReturningToMainMenu(bool won)
|
||||
{
|
||||
HighScoreParameter param;
|
||||
param.difficulty = cb->getStartInfo()->difficulty;
|
||||
param.day = cb->getDate();
|
||||
param.townAmount = cb->howManyTowns();
|
||||
param.usedCheat = cb->getPlayerState(*cb->getPlayerID())->cheated;
|
||||
param.hasGrail = false;
|
||||
for(const CGHeroInstance * h : cb->getHeroesInfo())
|
||||
if(h->hasArt(ArtifactID::GRAIL))
|
||||
param.hasGrail = true;
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
if(t->builtBuildings.count(BuildingID::GRAIL))
|
||||
param.hasGrail = true;
|
||||
param.allDefeated = true;
|
||||
for (PlayerColor player(0); player < PlayerColor::PLAYER_LIMIT; ++player)
|
||||
{
|
||||
auto ps = cb->getPlayerState(player, false);
|
||||
if(ps && player != *cb->getPlayerID())
|
||||
if(!ps->checkVanquished())
|
||||
param.allDefeated = false;
|
||||
}
|
||||
param.scenarioName = cb->getMapHeader()->name;
|
||||
param.playerName = cb->getStartInfo()->playerInfos.find(*cb->getPlayerID())->second.name;
|
||||
HighScoreCalculation highScoreCalc;
|
||||
highScoreCalc.parameters.push_back(param);
|
||||
highScoreCalc.isCampaign = false;
|
||||
|
||||
if(won && cb->getStartInfo()->campState)
|
||||
CSH->startCampaignScenario(cb->getStartInfo()->campState);
|
||||
CSH->startCampaignScenario(param, cb->getStartInfo()->campState);
|
||||
else
|
||||
{
|
||||
GH.dispatchMainThread(
|
||||
[]()
|
||||
[won, highScoreCalc]()
|
||||
{
|
||||
CSH->endGameplay();
|
||||
GH.defActionsDef = 63;
|
||||
CMM->menu->switchToTab("main");
|
||||
GH.windows().createAndPushWindow<CHighScoreInputScreen>(won, highScoreCalc);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "mainmenu/CMainMenu.h"
|
||||
#include "mainmenu/CPrologEpilogVideo.h"
|
||||
#include "mainmenu/CHighScoreScreen.h"
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
#include "../lib/CAndroidVMHelper.h"
|
||||
@ -616,6 +617,8 @@ void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameSta
|
||||
CMM->disable();
|
||||
client = new CClient();
|
||||
|
||||
highScoreCalc = nullptr;
|
||||
|
||||
switch(si->mode)
|
||||
{
|
||||
case StartInfo::NEW_GAME:
|
||||
@ -685,14 +688,23 @@ void CServerHandler::endGameplay(bool closeConnection, bool restart)
|
||||
saveSession->Bool() = false;
|
||||
}
|
||||
|
||||
void CServerHandler::startCampaignScenario(std::shared_ptr<CampaignState> cs)
|
||||
void CServerHandler::startCampaignScenario(HighScoreParameter param, std::shared_ptr<CampaignState> cs)
|
||||
{
|
||||
std::shared_ptr<CampaignState> ourCampaign = cs;
|
||||
|
||||
if (!cs)
|
||||
ourCampaign = si->campState;
|
||||
|
||||
GH.dispatchMainThread([ourCampaign]()
|
||||
if(highScoreCalc == nullptr)
|
||||
{
|
||||
highScoreCalc = std::make_shared<HighScoreCalculation>();
|
||||
highScoreCalc->isCampaign = true;
|
||||
highScoreCalc->parameters.clear();
|
||||
}
|
||||
param.campaignName = cs->getName();
|
||||
highScoreCalc->parameters.push_back(param);
|
||||
|
||||
GH.dispatchMainThread([ourCampaign, this]()
|
||||
{
|
||||
CSH->campaignServerRestartLock.set(true);
|
||||
CSH->endGameplay();
|
||||
@ -712,7 +724,10 @@ void CServerHandler::startCampaignScenario(std::shared_ptr<CampaignState> cs)
|
||||
if(!ourCampaign->isCampaignFinished())
|
||||
CMM->openCampaignLobby(ourCampaign);
|
||||
else
|
||||
{
|
||||
CMM->openCampaignScreen(ourCampaign->campaignSet);
|
||||
GH.windows().createAndPushWindow<CHighScoreInputScreen>(true, *highScoreCalc);
|
||||
}
|
||||
};
|
||||
if(epilogue.hasPrologEpilog)
|
||||
{
|
||||
@ -960,7 +975,7 @@ void CServerHandler::threadRunServer()
|
||||
}
|
||||
|
||||
comm += " > \"" + logName + '\"';
|
||||
logGlobal->info("Server command line: %s", comm);
|
||||
logGlobal->info("Server command line: %s", comm);
|
||||
|
||||
#ifdef VCMI_WINDOWS
|
||||
int result = -1;
|
||||
|
@ -35,6 +35,9 @@ VCMI_LIB_NAMESPACE_END
|
||||
class CClient;
|
||||
class CBaseForLobbyApply;
|
||||
|
||||
class HighScoreCalculation;
|
||||
class HighScoreParameter;
|
||||
|
||||
// TODO: Add mutex so we can't set CONNECTION_CANCELLED if client already connected, but thread not setup yet
|
||||
enum class EClientState : ui8
|
||||
{
|
||||
@ -87,6 +90,8 @@ class CServerHandler : public IServerAPI, public LobbyInfo
|
||||
|
||||
std::vector<std::string> myNames;
|
||||
|
||||
std::shared_ptr<HighScoreCalculation> highScoreCalc;
|
||||
|
||||
void threadHandleConnection();
|
||||
void threadRunServer();
|
||||
void onServerFinished();
|
||||
@ -161,7 +166,7 @@ public:
|
||||
|
||||
void startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState = nullptr);
|
||||
void endGameplay(bool closeConnection = true, bool restart = false);
|
||||
void startCampaignScenario(std::shared_ptr<CampaignState> cs = {});
|
||||
void startCampaignScenario(HighScoreParameter param, std::shared_ptr<CampaignState> cs = {});
|
||||
void showServerError(const std::string & txt) const;
|
||||
|
||||
// TODO: LobbyState must be updated within game so we should always know how many player interfaces our client handle
|
||||
|
@ -349,7 +349,7 @@ void CVideoPlayer::redraw( int x, int y, SDL_Surface *dst, bool update )
|
||||
show(x, y, dst, update);
|
||||
}
|
||||
|
||||
void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, bool update )
|
||||
void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, bool update, std::function<void()> onVideoRestart)
|
||||
{
|
||||
if (sws == nullptr)
|
||||
return;
|
||||
@ -368,6 +368,8 @@ void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, boo
|
||||
show(x,y,dst,update);
|
||||
else
|
||||
{
|
||||
if(onVideoRestart)
|
||||
onVideoRestart();
|
||||
VideoPath filenameToReopen = fname; // create copy to backup this->fname
|
||||
open(filenameToReopen);
|
||||
nextFrame();
|
||||
|
@ -31,7 +31,7 @@ public:
|
||||
class IMainVideoPlayer : public IVideoPlayer
|
||||
{
|
||||
public:
|
||||
virtual void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true){}
|
||||
virtual void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true, std::function<void()> restart = 0){}
|
||||
virtual bool openAndPlayVideo(const VideoPath & name, int x, int y, bool stopOnKey = false, bool scale = false)
|
||||
{
|
||||
return false;
|
||||
@ -101,7 +101,7 @@ public:
|
||||
|
||||
void show(int x, int y, SDL_Surface *dst, bool update = true) override; //blit current frame
|
||||
void redraw(int x, int y, SDL_Surface *dst, bool update = true) override; //reblits buffer
|
||||
void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true) override; //moves to next frame if appropriate, and blits it or blits only if redraw parameter is set true
|
||||
void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true, std::function<void()> onVideoRestart = nullptr) override; //moves to next frame if appropriate, and blits it or blits only if redraw parameter is set true
|
||||
|
||||
// Opens video, calls playVideo, closes video; returns playVideo result (if whole video has been played)
|
||||
bool openAndPlayVideo(const VideoPath & name, int x, int y, bool stopOnKey = false, bool scale = false) override;
|
||||
|
@ -198,6 +198,8 @@
|
||||
<Unit filename="mainmenu/CPrologEpilogVideo.h" />
|
||||
<Unit filename="mainmenu/CreditsScreen.cpp" />
|
||||
<Unit filename="mainmenu/CreditsScreen.h" />
|
||||
<Unit filename="mainmenu/CHighScoreScreen.cpp" />
|
||||
<Unit filename="mainmenu/CHighScoreScreen.h" />
|
||||
<Unit filename="mapHandler.cpp" />
|
||||
<Unit filename="mapHandler.h" />
|
||||
<Unit filename="resource.h" />
|
||||
|
@ -217,6 +217,7 @@
|
||||
<ClCompile Include="mainmenu\CMainMenu.cpp" />
|
||||
<ClCompile Include="mainmenu\CPrologEpilogVideo.cpp" />
|
||||
<ClCompile Include="mainmenu\CreditsScreen.cpp" />
|
||||
<ClCompile Include="mainmenu\CHighScoreScreen.cpp" />
|
||||
<ClCompile Include="mapHandler.cpp" />
|
||||
<ClCompile Include="NetPacksClient.cpp" />
|
||||
<ClCompile Include="NetPacksLobbyClient.cpp" />
|
||||
@ -286,6 +287,7 @@
|
||||
<ClInclude Include="mainmenu\CMainMenu.h" />
|
||||
<ClInclude Include="mainmenu\CPrologEpilogVideo.h" />
|
||||
<ClInclude Include="mainmenu\CreditsScreen.h" />
|
||||
<ClInclude Include="mainmenu\CHighScoreScreen.h" />
|
||||
<ClInclude Include="mapHandler.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="SDLRWwrapper.h" />
|
||||
|
@ -132,6 +132,7 @@
|
||||
<ClCompile Include="mainmenu\CMainMenu.cpp" />
|
||||
<ClCompile Include="mainmenu\CPrologEpilogVideo.cpp" />
|
||||
<ClCompile Include="mainmenu\CreditsScreen.cpp" />
|
||||
<ClCompile Include="mainmenu\CHighScoreScreen.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="VCMI_client.rc" />
|
||||
@ -290,5 +291,6 @@
|
||||
<ClInclude Include="mainmenu\CMainMenu.h" />
|
||||
<ClInclude Include="mainmenu\CPrologEpilogVideo.h" />
|
||||
<ClInclude Include="mainmenu\CreditsScreen.h" />
|
||||
<ClInclude Include="mainmenu\CHighScoreScreen.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
383
client/mainmenu/CHighScoreScreen.cpp
Normal file
383
client/mainmenu/CHighScoreScreen.cpp
Normal file
@ -0,0 +1,383 @@
|
||||
/*
|
||||
* CHighScoreScreen.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 "CHighScoreScreen.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../render/Canvas.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CVideoHandler.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/constants/EntityIdentifiers.h"
|
||||
#include "../../lib/TextOperations.h"
|
||||
|
||||
#include "vstd/DateUtils.h"
|
||||
|
||||
auto HighScoreCalculation::calculate()
|
||||
{
|
||||
struct Result
|
||||
{
|
||||
int basic = 0;
|
||||
int total = 0;
|
||||
int sumDays = 0;
|
||||
bool cheater = false;
|
||||
};
|
||||
|
||||
Result firstResult, summary;
|
||||
const std::array<double, 5> difficultyMultipliers{0.8, 1.0, 1.3, 1.6, 2.0};
|
||||
for(auto & param : parameters)
|
||||
{
|
||||
double tmp = 200 - (param.day + 10) / (param.townAmount + 5) + (param.allDefeated ? 25 : 0) + (param.hasGrail ? 25 : 0);
|
||||
firstResult = Result{static_cast<int>(tmp), static_cast<int>(tmp * difficultyMultipliers.at(param.difficulty)), param.day, param.usedCheat};
|
||||
summary.basic += firstResult.basic * 5.0 / parameters.size();
|
||||
summary.total += firstResult.total * 5.0 / parameters.size();
|
||||
summary.sumDays += firstResult.sumDays;
|
||||
summary.cheater |= firstResult.cheater;
|
||||
}
|
||||
|
||||
if(parameters.size() == 1)
|
||||
return firstResult;
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
CreatureID HighScoreCalculation::getCreatureForPoints(int points, bool campaign)
|
||||
{
|
||||
static const JsonNode configCreatures(JsonPath::builtin("CONFIG/highscoreCreatures.json"));
|
||||
auto creatures = configCreatures["creatures"].Vector();
|
||||
int divide = campaign ? 5 : 1;
|
||||
|
||||
for(auto & creature : creatures)
|
||||
if(points / divide <= creature["max"].Integer() && points / divide >= creature["min"].Integer())
|
||||
return CreatureID::decode(creature["creature"].String());
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
CHighScoreScreen::CHighScoreScreen(HighScorePage highscorepage, int highlighted)
|
||||
: CWindowObject(BORDERED), highscorepage(highscorepage), highlighted(highlighted)
|
||||
{
|
||||
addUsedEvents(SHOW_POPUP);
|
||||
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
pos = center(Rect(0, 0, 800, 600));
|
||||
updateShadow();
|
||||
|
||||
addHighScores();
|
||||
addButtons();
|
||||
}
|
||||
|
||||
void CHighScoreScreen::showPopupWindow(const Point & cursorPosition)
|
||||
{
|
||||
for (int i = 0; i < screenRows; i++)
|
||||
{
|
||||
bool currentGameNotInListEntry = i == (screenRows - 1) && highlighted > (screenRows - 1);
|
||||
|
||||
Rect r = Rect(80, 40 + i * 50, 635, 50);
|
||||
if(r.isInside(cursorPosition - pos))
|
||||
{
|
||||
std::string tmp = persistentStorage["highscore"][highscorepage == HighScorePage::SCENARIO ? "scenario" : "campaign"][currentGameNotInListEntry ? highlighted : i]["datetime"].String();
|
||||
if(!tmp.empty())
|
||||
CRClickPopup::createAndPush(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CHighScoreScreen::addButtons()
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
buttons.clear();
|
||||
|
||||
buttons.push_back(std::make_shared<CButton>(Point(31, 113), AnimationPath::builtin("HISCCAM.DEF"), CButton::tooltip(), [&](){ buttonCampaignClick(); }));
|
||||
buttons.push_back(std::make_shared<CButton>(Point(31, 345), AnimationPath::builtin("HISCSTA.DEF"), CButton::tooltip(), [&](){ buttonScenarioClick(); }));
|
||||
buttons.push_back(std::make_shared<CButton>(Point(726, 113), AnimationPath::builtin("HISCRES.DEF"), CButton::tooltip(), [&](){ buttonResetClick(); }));
|
||||
buttons.push_back(std::make_shared<CButton>(Point(726, 345), AnimationPath::builtin("HISCEXT.DEF"), CButton::tooltip(), [&](){ buttonExitClick(); }));
|
||||
}
|
||||
|
||||
void CHighScoreScreen::addHighScores()
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
background = std::make_shared<CPicture>(ImagePath::builtin(highscorepage == HighScorePage::SCENARIO ? "HISCORE" : "HISCORE2"));
|
||||
|
||||
texts.clear();
|
||||
images.clear();
|
||||
|
||||
// Header
|
||||
texts.push_back(std::make_shared<CLabel>(115, 20, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.433"))); // rank
|
||||
texts.push_back(std::make_shared<CLabel>(225, 20, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.434"))); // player
|
||||
|
||||
if(highscorepage == HighScorePage::SCENARIO)
|
||||
{
|
||||
texts.push_back(std::make_shared<CLabel>(405, 20, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.435"))); // land
|
||||
texts.push_back(std::make_shared<CLabel>(557, 20, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.436"))); // days
|
||||
texts.push_back(std::make_shared<CLabel>(627, 20, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.75"))); // score
|
||||
}
|
||||
else
|
||||
{
|
||||
texts.push_back(std::make_shared<CLabel>(405, 20, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.672"))); // campaign
|
||||
texts.push_back(std::make_shared<CLabel>(592, 20, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.75"))); // score
|
||||
}
|
||||
|
||||
// Content
|
||||
int y = 66;
|
||||
auto & data = persistentStorage["highscore"][highscorepage == HighScorePage::SCENARIO ? "scenario" : "campaign"];
|
||||
for (int i = 0; i < screenRows; i++)
|
||||
{
|
||||
bool currentGameNotInListEntry = (i == (screenRows - 1) && highlighted > (screenRows - 1));
|
||||
auto & curData = data[currentGameNotInListEntry ? highlighted : i];
|
||||
|
||||
ColorRGBA color = (i == highlighted || currentGameNotInListEntry) ? Colors::YELLOW : Colors::WHITE;
|
||||
|
||||
texts.push_back(std::make_shared<CLabel>(115, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, std::to_string((currentGameNotInListEntry ? highlighted : i) + 1)));
|
||||
std::string tmp = curData["player"].String();
|
||||
TextOperations::trimRightUnicode(tmp, std::max(0, (int)TextOperations::getUnicodeCharactersCount(tmp) - 13));
|
||||
texts.push_back(std::make_shared<CLabel>(225, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, tmp));
|
||||
|
||||
if(highscorepage == HighScorePage::SCENARIO)
|
||||
{
|
||||
std::string tmp = curData["scenarioName"].String();
|
||||
TextOperations::trimRightUnicode(tmp, std::max(0, (int)TextOperations::getUnicodeCharactersCount(tmp) - 25));
|
||||
texts.push_back(std::make_shared<CLabel>(405, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, tmp));
|
||||
texts.push_back(std::make_shared<CLabel>(557, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, std::to_string(curData["days"].Integer())));
|
||||
texts.push_back(std::make_shared<CLabel>(627, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, std::to_string(curData["points"].Integer())));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string tmp = curData["campaignName"].String();
|
||||
TextOperations::trimRightUnicode(tmp, std::max(0, (int)TextOperations::getUnicodeCharactersCount(tmp) - 25));
|
||||
texts.push_back(std::make_shared<CLabel>(405, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, tmp));
|
||||
texts.push_back(std::make_shared<CLabel>(592, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, std::to_string(curData["points"].Integer())));
|
||||
}
|
||||
|
||||
if(curData["points"].Integer() > 0 && curData["points"].Integer() <= ((highscorepage == HighScorePage::CAMPAIGN) ? 2500 : 500))
|
||||
images.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("CPRSMALL"), (*CGI->creh)[HighScoreCalculation::getCreatureForPoints(curData["points"].Integer(), highscorepage == HighScorePage::CAMPAIGN)]->getIconIndex(), 0, 670, y - 15 + i * 50));
|
||||
}
|
||||
}
|
||||
|
||||
void CHighScoreScreen::buttonCampaignClick()
|
||||
{
|
||||
highscorepage = HighScorePage::CAMPAIGN;
|
||||
addHighScores();
|
||||
addButtons();
|
||||
redraw();
|
||||
}
|
||||
|
||||
void CHighScoreScreen::buttonScenarioClick()
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
highscorepage = HighScorePage::SCENARIO;
|
||||
addHighScores();
|
||||
addButtons();
|
||||
redraw();
|
||||
}
|
||||
|
||||
void CHighScoreScreen::buttonResetClick()
|
||||
{
|
||||
CInfoWindow::showYesNoDialog(
|
||||
CGI->generaltexth->allTexts[666],
|
||||
{},
|
||||
[this]()
|
||||
{
|
||||
Settings entry = persistentStorage.write["highscore"];
|
||||
entry->clear();
|
||||
addHighScores();
|
||||
addButtons();
|
||||
redraw();
|
||||
},
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
void CHighScoreScreen::buttonExitClick()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
CHighScoreInputScreen::CHighScoreInputScreen(bool won, HighScoreCalculation calc)
|
||||
: CWindowObject(BORDERED), won(won), calc(calc)
|
||||
{
|
||||
addUsedEvents(LCLICK | KEYBOARD);
|
||||
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
pos = center(Rect(0, 0, 800, 600));
|
||||
updateShadow();
|
||||
|
||||
background = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), Colors::BLACK);
|
||||
|
||||
if(won)
|
||||
{
|
||||
int border = 100;
|
||||
int textareaW = ((pos.w - 2 * border) / 4);
|
||||
std::vector<std::string> t = { "438", "439", "440", "441", "676" }; // time, score, difficulty, final score, rank
|
||||
for (int i = 0; i < 5; i++)
|
||||
texts.push_back(std::make_shared<CMultiLineLabel>(Rect(textareaW * i + border - (textareaW / 2), 450, textareaW, 100), FONT_HIGH_SCORE, ETextAlignment::TOPCENTER, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt." + t[i])));
|
||||
|
||||
std::string creatureName = (calc.calculate().cheater) ? CGI->generaltexth->translate("core.genrltxt.260") : (*CGI->creh)[HighScoreCalculation::getCreatureForPoints(calc.calculate().total, calc.isCampaign)]->getNameSingularTranslated();
|
||||
t = { std::to_string(calc.calculate().sumDays), std::to_string(calc.calculate().basic), CGI->generaltexth->translate("core.arraytxt." + std::to_string((142 + calc.parameters[0].difficulty))), std::to_string(calc.calculate().total), creatureName };
|
||||
for (int i = 0; i < 5; i++)
|
||||
texts.push_back(std::make_shared<CMultiLineLabel>(Rect(textareaW * i + border - (textareaW / 2), 530, textareaW, 100), FONT_HIGH_SCORE, ETextAlignment::TOPCENTER, Colors::WHITE, t[i]));
|
||||
|
||||
CCS->musich->playMusic(AudioPath::builtin("music/Win Scenario"), true, true);
|
||||
}
|
||||
else
|
||||
CCS->musich->playMusic(AudioPath::builtin("music/UltimateLose"), false, true);
|
||||
|
||||
video = won ? "HSANIM.SMK" : "LOSEGAME.SMK";
|
||||
}
|
||||
|
||||
int CHighScoreInputScreen::addEntry(std::string text) {
|
||||
std::vector<JsonNode> baseNode = persistentStorage["highscore"][calc.isCampaign ? "campaign" : "scenario"].Vector();
|
||||
|
||||
auto sortFunctor = [](const JsonNode & left, const JsonNode & right)
|
||||
{
|
||||
if(left["points"].Integer() == right["points"].Integer())
|
||||
return left["posFlag"].Integer() > right["posFlag"].Integer();
|
||||
return left["points"].Integer() > right["points"].Integer();
|
||||
};
|
||||
|
||||
JsonNode newNode = JsonNode();
|
||||
newNode["player"].String() = text;
|
||||
if(calc.isCampaign)
|
||||
newNode["campaignName"].String() = calc.calculate().cheater ? CGI->generaltexth->translate("core.genrltxt.260") : calc.parameters[0].campaignName;
|
||||
else
|
||||
newNode["scenarioName"].String() = calc.calculate().cheater ? CGI->generaltexth->translate("core.genrltxt.260") : calc.parameters[0].scenarioName;
|
||||
newNode["days"].Integer() = calc.calculate().sumDays;
|
||||
newNode["points"].Integer() = calc.calculate().cheater ? 0 : calc.calculate().total;
|
||||
newNode["datetime"].String() = vstd::getFormattedDateTime(std::time(0));
|
||||
newNode["posFlag"].Bool() = true;
|
||||
|
||||
baseNode.push_back(newNode);
|
||||
boost::range::sort(baseNode, sortFunctor);
|
||||
|
||||
int pos = -1;
|
||||
for (int i = 0; i < baseNode.size(); i++)
|
||||
{
|
||||
if(!baseNode[i]["posFlag"].isNull())
|
||||
{
|
||||
baseNode[i]["posFlag"].clear();
|
||||
pos = i;
|
||||
}
|
||||
}
|
||||
|
||||
Settings s = persistentStorage.write["highscore"][calc.isCampaign ? "campaign" : "scenario"];
|
||||
s->Vector() = baseNode;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
void CHighScoreInputScreen::show(Canvas & to)
|
||||
{
|
||||
CCS->videoh->update(pos.x, pos.y, to.getInternalSurface(), true, false,
|
||||
[&]()
|
||||
{
|
||||
if(won)
|
||||
{
|
||||
CCS->videoh->close();
|
||||
video = "HSLOOP.SMK";
|
||||
CCS->videoh->open(VideoPath::builtin(video));
|
||||
}
|
||||
else
|
||||
close();
|
||||
});
|
||||
redraw();
|
||||
|
||||
CIntObject::show(to);
|
||||
}
|
||||
|
||||
void CHighScoreInputScreen::activate()
|
||||
{
|
||||
if(!CCS->videoh->open(VideoPath::builtin(video)))
|
||||
{
|
||||
if(!won)
|
||||
close();
|
||||
}
|
||||
else
|
||||
background = nullptr;
|
||||
CIntObject::activate();
|
||||
}
|
||||
|
||||
void CHighScoreInputScreen::deactivate()
|
||||
{
|
||||
CCS->videoh->close();
|
||||
CIntObject::deactivate();
|
||||
}
|
||||
|
||||
void CHighScoreInputScreen::clickPressed(const Point & cursorPosition)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
if(!won)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!input)
|
||||
{
|
||||
input = std::make_shared<CHighScoreInput>(calc.parameters[0].playerName,
|
||||
[&] (std::string text)
|
||||
{
|
||||
if(!text.empty())
|
||||
{
|
||||
int pos = addEntry(text);
|
||||
close();
|
||||
GH.windows().createAndPushWindow<CHighScoreScreen>(calc.isCampaign ? CHighScoreScreen::HighScorePage::CAMPAIGN : CHighScoreScreen::HighScorePage::SCENARIO, pos);
|
||||
}
|
||||
else
|
||||
close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void CHighScoreInputScreen::keyPressed(EShortcut key)
|
||||
{
|
||||
clickPressed(Point());
|
||||
}
|
||||
|
||||
CHighScoreInput::CHighScoreInput(std::string playerName, std::function<void(std::string text)> readyCB)
|
||||
: CWindowObject(0, ImagePath::builtin("HIGHNAME")), ready(readyCB)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
pos = center(Rect(0, 0, 232, 212));
|
||||
updateShadow();
|
||||
|
||||
text = std::make_shared<CMultiLineLabel>(Rect(15, 15, 202, 202), FONT_SMALL, ETextAlignment::TOPCENTER, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.96"));
|
||||
|
||||
buttonOk = std::make_shared<CButton>(Point(26, 142), AnimationPath::builtin("MUBCHCK.DEF"), CGI->generaltexth->zelp[560], std::bind(&CHighScoreInput::okay, this), EShortcut::GLOBAL_ACCEPT);
|
||||
buttonCancel = std::make_shared<CButton>(Point(142, 142), AnimationPath::builtin("MUBCANC.DEF"), CGI->generaltexth->zelp[561], std::bind(&CHighScoreInput::abort, this), EShortcut::GLOBAL_CANCEL);
|
||||
statusBar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(7, 186, 218, 18), 7, 186));
|
||||
textInput = std::make_shared<CTextInput>(Rect(18, 104, 200, 25), FONT_SMALL, 0);
|
||||
textInput->setText(playerName);
|
||||
}
|
||||
|
||||
void CHighScoreInput::okay()
|
||||
{
|
||||
ready(textInput->getText());
|
||||
}
|
||||
|
||||
void CHighScoreInput::abort()
|
||||
{
|
||||
ready("");
|
||||
}
|
110
client/mainmenu/CHighScoreScreen.h
Normal file
110
client/mainmenu/CHighScoreScreen.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* CHighScoreScreen.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 CButton;
|
||||
class CLabel;
|
||||
class CMultiLineLabel;
|
||||
class CAnimImage;
|
||||
class CTextInput;
|
||||
|
||||
class TransparentFilledRectangle;
|
||||
|
||||
class HighScoreParameter
|
||||
{
|
||||
public:
|
||||
int difficulty;
|
||||
int day;
|
||||
int townAmount;
|
||||
bool usedCheat;
|
||||
bool hasGrail;
|
||||
bool allDefeated;
|
||||
std::string campaignName;
|
||||
std::string scenarioName;
|
||||
std::string playerName;
|
||||
};
|
||||
|
||||
class HighScoreCalculation
|
||||
{
|
||||
public:
|
||||
std::vector<HighScoreParameter> parameters = std::vector<HighScoreParameter>();
|
||||
bool isCampaign = false;
|
||||
|
||||
auto calculate();
|
||||
static CreatureID getCreatureForPoints(int points, bool campaign);
|
||||
};
|
||||
|
||||
class CHighScoreScreen : public CWindowObject
|
||||
{
|
||||
public:
|
||||
enum HighScorePage { SCENARIO, CAMPAIGN };
|
||||
|
||||
private:
|
||||
void addButtons();
|
||||
void addHighScores();
|
||||
|
||||
void buttonCampaignClick();
|
||||
void buttonScenarioClick();
|
||||
void buttonResetClick();
|
||||
void buttonExitClick();
|
||||
|
||||
void showPopupWindow(const Point & cursorPosition) override;
|
||||
|
||||
HighScorePage highscorepage;
|
||||
|
||||
std::shared_ptr<CPicture> background;
|
||||
std::vector<std::shared_ptr<CButton>> buttons;
|
||||
std::vector<std::shared_ptr<CLabel>> texts;
|
||||
std::vector<std::shared_ptr<CAnimImage>> images;
|
||||
|
||||
const int screenRows = 11;
|
||||
|
||||
int highlighted;
|
||||
public:
|
||||
CHighScoreScreen(HighScorePage highscorepage, int highlighted = -1);
|
||||
};
|
||||
|
||||
class CHighScoreInput : public CWindowObject
|
||||
{
|
||||
std::shared_ptr<CMultiLineLabel> text;
|
||||
std::shared_ptr<CButton> buttonOk;
|
||||
std::shared_ptr<CButton> buttonCancel;
|
||||
std::shared_ptr<CGStatusBar> statusBar;
|
||||
std::shared_ptr<CTextInput> textInput;
|
||||
|
||||
std::function<void(std::string text)> ready;
|
||||
|
||||
void okay();
|
||||
void abort();
|
||||
public:
|
||||
CHighScoreInput(std::string playerName, std::function<void(std::string text)> readyCB);
|
||||
};
|
||||
|
||||
class CHighScoreInputScreen : public CWindowObject
|
||||
{
|
||||
std::vector<std::shared_ptr<CMultiLineLabel>> texts;
|
||||
std::shared_ptr<CHighScoreInput> input;
|
||||
std::shared_ptr<TransparentFilledRectangle> background;
|
||||
|
||||
std::string video;
|
||||
bool won;
|
||||
HighScoreCalculation calc;
|
||||
public:
|
||||
CHighScoreInputScreen(bool won, HighScoreCalculation calc);
|
||||
|
||||
int addEntry(std::string text);
|
||||
|
||||
void show(Canvas & to) override;
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
void clickPressed(const Point & cursorPosition) override;
|
||||
void keyPressed(EShortcut key) override;
|
||||
};
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "CCampaignScreen.h"
|
||||
#include "CreditsScreen.h"
|
||||
#include "CHighScoreScreen.h"
|
||||
|
||||
#include "../lobby/CBonusSelection.h"
|
||||
#include "../lobby/CSelectionBase.h"
|
||||
@ -216,7 +217,7 @@ static std::function<void()> genCommand(CMenuScreen * menu, std::vector<std::str
|
||||
break;
|
||||
case 5: //highscores
|
||||
{
|
||||
return std::bind(CInfoWindow::showInfoDialog, CGI->generaltexth->translate("vcmi.mainMenu.highscoresNotImplemented"), std::vector<std::shared_ptr<CComponent>>(), PlayerColor(1));
|
||||
return std::bind(CMainMenu::openHighScoreScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -395,6 +396,12 @@ void CMainMenu::startTutorial()
|
||||
CSH->startMapAfterConnection(mapInfo);
|
||||
}
|
||||
|
||||
void CMainMenu::openHighScoreScreen()
|
||||
{
|
||||
GH.windows().createAndPushWindow<CHighScoreScreen>(CHighScoreScreen::HighScorePage::SCENARIO);
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<CMainMenu> CMainMenu::create()
|
||||
{
|
||||
if(!CMM)
|
||||
|
@ -152,6 +152,7 @@ public:
|
||||
static void openCampaignLobby(const std::string & campaignFileName, std::string campaignSet = "");
|
||||
static void openCampaignLobby(std::shared_ptr<CampaignState> campaign);
|
||||
static void startTutorial();
|
||||
static void openHighScoreScreen();
|
||||
void openCampaignScreen(std::string name);
|
||||
|
||||
static std::shared_ptr<CMainMenu> create();
|
||||
|
122
config/highscoreCreatures.json
Normal file
122
config/highscoreCreatures.json
Normal file
@ -0,0 +1,122 @@
|
||||
{
|
||||
"creatures": [
|
||||
{ "min" : 1, "max" : 4, "creature": "imp" },
|
||||
{ "min" : 5, "max" : 8, "creature": "gremlin" },
|
||||
{ "min" : 9, "max" : 12, "creature": "gnoll" },
|
||||
{ "min" : 13, "max" : 16, "creature": "troglodyte" },
|
||||
{ "min" : 17, "max" : 20, "creature": "familiar" },
|
||||
{ "min" : 21, "max" : 24, "creature": "skeleton" },
|
||||
{ "min" : 25, "max" : 28, "creature": "goblin" },
|
||||
{ "min" : 29, "max" : 32, "creature": "masterGremlin" },
|
||||
{ "min" : 33, "max" : 36, "creature": "hobgoblin" },
|
||||
{ "min" : 37, "max" : 40, "creature": "pikeman" },
|
||||
{ "min" : 41, "max" : 44, "creature": "infernalTroglodyte" },
|
||||
{ "min" : 45, "max" : 48, "creature": "skeletonWarrior" },
|
||||
{ "min" : 49, "max" : 52, "creature": "gnollMarauder" },
|
||||
{ "min" : 53, "max" : 56, "creature": "walkingDead" },
|
||||
{ "min" : 57, "max" : 60, "creature": "centaur" },
|
||||
{ "min" : 61, "max" : 64, "creature": "halberdier" },
|
||||
{ "min" : 65, "max" : 68, "creature": "archer" },
|
||||
{ "min" : 69, "max" : 72, "creature": "lizardman" },
|
||||
{ "min" : 73, "max" : 76, "creature": "zombie" },
|
||||
{ "min" : 77, "max" : 80, "creature": "goblinWolfRider" },
|
||||
{ "min" : 81, "max" : 84, "creature": "centaurCaptain" },
|
||||
{ "min" : 85, "max" : 88, "creature": "dwarf" },
|
||||
{ "min" : 89, "max" : 92, "creature": "harpy" },
|
||||
{ "min" : 93, "max" : 96, "creature": "lizardWarrior" },
|
||||
{ "min" : 97, "max" : 100, "creature": "gog" },
|
||||
{ "min" : 101, "max" : 104, "creature": "stoneGargoyle" },
|
||||
{ "min" : 105, "max" : 108, "creature": "sharpshooter" },
|
||||
{ "min" : 109, "max" : 112, "creature": "orc" },
|
||||
{ "min" : 113, "max" : 116, "creature": "obsidianGargoyle" },
|
||||
{ "min" : 117, "max" : 120, "creature": "hobgoblinWolfRider" },
|
||||
{ "min" : 121, "max" : 124, "creature": "battleDwarf" },
|
||||
{ "min" : 125, "max" : 128, "creature": "woodElf" },
|
||||
{ "min" : 129, "max" : 132, "creature": "harpyHag" },
|
||||
{ "min" : 133, "max" : 136, "creature": "magog" },
|
||||
{ "min" : 137, "max" : 140, "creature": "orcChieftain" },
|
||||
{ "min" : 141, "max" : 144, "creature": "stoneGolem" },
|
||||
{ "min" : 145, "max" : 148, "creature": "wight" },
|
||||
{ "min" : 149, "max" : 152, "creature": "serpentFly" },
|
||||
{ "min" : 153, "max" : 156, "creature": "dragonFly" },
|
||||
{ "min" : 157, "max" : 160, "creature": "wraith" },
|
||||
{ "min" : 161, "max" : 164, "creature": "waterElemental" },
|
||||
{ "min" : 165, "max" : 168, "creature": "earthElemental" },
|
||||
{ "min" : 169, "max" : 172, "creature": "grandElf" },
|
||||
{ "min" : 173, "max" : 176, "creature": "beholder" },
|
||||
{ "min" : 177, "max" : 180, "creature": "fireElemental" },
|
||||
{ "min" : 181, "max" : 184, "creature": "griffin" },
|
||||
{ "min" : 185, "max" : 187, "creature": "airElemental" },
|
||||
{ "min" : 188, "max" : 190, "creature": "hellHound" },
|
||||
{ "min" : 191, "max" : 193, "creature": "evilEye" },
|
||||
{ "min" : 194, "max" : 196, "creature": "cerberus" },
|
||||
{ "min" : 197, "max" : 199, "creature": "ironGolem" },
|
||||
{ "min" : 200, "max" : 202, "creature": "ogre" },
|
||||
{ "min" : 203, "max" : 205, "creature": "swordsman" },
|
||||
{ "min" : 206, "max" : 208, "creature": "demon" },
|
||||
{ "min" : 209, "max" : 211, "creature": "royalGriffin" },
|
||||
{ "min" : 212, "max" : 214, "creature": "hornedDemon" },
|
||||
{ "min" : 215, "max" : 217, "creature": "monk" },
|
||||
{ "min" : 218, "max" : 220, "creature": "dendroidGuard" },
|
||||
{ "min" : 221, "max" : 223, "creature": "medusa" },
|
||||
{ "min" : 224, "max" : 226, "creature": "pegasus" },
|
||||
{ "min" : 227, "max" : 229, "creature": "silverPegasus" },
|
||||
{ "min" : 230, "max" : 232, "creature": "basilisk" },
|
||||
{ "min" : 233, "max" : 235, "creature": "vampire" },
|
||||
{ "min" : 236, "max" : 238, "creature": "mage" },
|
||||
{ "min" : 239, "max" : 241, "creature": "medusaQueen" },
|
||||
{ "min" : 242, "max" : 244, "creature": "crusader" },
|
||||
{ "min" : 245, "max" : 247, "creature": "goldGolem" },
|
||||
{ "min" : 248, "max" : 250, "creature": "ogreMage" },
|
||||
{ "min" : 251, "max" : 253, "creature": "archMage" },
|
||||
{ "min" : 254, "max" : 256, "creature": "greaterBasilisk" },
|
||||
{ "min" : 257, "max" : 259, "creature": "zealot" },
|
||||
{ "min" : 260, "max" : 262, "creature": "pitFiend" },
|
||||
{ "min" : 263, "max" : 265, "creature": "diamondGolem" },
|
||||
{ "min" : 266, "max" : 268, "creature": "vampireLord" },
|
||||
{ "min" : 269, "max" : 271, "creature": "dendroidSoldier" },
|
||||
{ "min" : 272, "max" : 274, "creature": "minotaur" },
|
||||
{ "min" : 275, "max" : 277, "creature": "lich" },
|
||||
{ "min" : 278, "max" : 280, "creature": "genie" },
|
||||
{ "min" : 281, "max" : 283, "creature": "gorgon" },
|
||||
{ "min" : 284, "max" : 286, "creature": "masterGenie" },
|
||||
{ "min" : 287, "max" : 289, "creature": "roc" },
|
||||
{ "min" : 290, "max" : 292, "creature": "mightyGorgon" },
|
||||
{ "min" : 293, "max" : 295, "creature": "minotaurKing" },
|
||||
{ "min" : 296, "max" : 298, "creature": "powerLich" },
|
||||
{ "min" : 299, "max" : 301, "creature": "thunderbird" },
|
||||
{ "min" : 302, "max" : 304, "creature": "pitLord" },
|
||||
{ "min" : 305, "max" : 307, "creature": "cyclop" },
|
||||
{ "min" : 308, "max" : 310, "creature": "wyvern" },
|
||||
{ "min" : 311, "max" : 313, "creature": "cyclopKing" },
|
||||
{ "min" : 314, "max" : 316, "creature": "wyvernMonarch" },
|
||||
{ "min" : 317, "max" : 319, "creature": "manticore" },
|
||||
{ "min" : 320, "max" : 322, "creature": "scorpicore" },
|
||||
{ "min" : 323, "max" : 325, "creature": "efreet" },
|
||||
{ "min" : 326, "max" : 328, "creature": "unicorn" },
|
||||
{ "min" : 329, "max" : 331, "creature": "efreetSultan" },
|
||||
{ "min" : 332, "max" : 334, "creature": "cavalier" },
|
||||
{ "min" : 335, "max" : 337, "creature": "naga" },
|
||||
{ "min" : 338, "max" : 340, "creature": "warUnicorn" },
|
||||
{ "min" : 341, "max" : 343, "creature": "blackKnight" },
|
||||
{ "min" : 344, "max" : 346, "creature": "champion" },
|
||||
{ "min" : 347, "max" : 349, "creature": "dreadKnight" },
|
||||
{ "min" : 350, "max" : 352, "creature": "nagaQueen" },
|
||||
{ "min" : 353, "max" : 355, "creature": "behemoth" },
|
||||
{ "min" : 356, "max" : 358, "creature": "boneDragon" },
|
||||
{ "min" : 359, "max" : 361, "creature": "giant" },
|
||||
{ "min" : 362, "max" : 364, "creature": "hydra" },
|
||||
{ "min" : 365, "max" : 367, "creature": "ghostDragon" },
|
||||
{ "min" : 368, "max" : 370, "creature": "redDragon" },
|
||||
{ "min" : 371, "max" : 373, "creature": "greenDragon" },
|
||||
{ "min" : 374, "max" : 376, "creature": "angel" },
|
||||
{ "min" : 377, "max" : 379, "creature": "devil" },
|
||||
{ "min" : 380, "max" : 382, "creature": "chaosHydra" },
|
||||
{ "min" : 383, "max" : 385, "creature": "ancientBehemoth" },
|
||||
{ "min" : 386, "max" : 388, "creature": "archDevil" },
|
||||
{ "min" : 389, "max" : 391, "creature": "titan" },
|
||||
{ "min" : 392, "max" : 394, "creature": "goldDragon" },
|
||||
{ "min" : 395, "max" : 397, "creature": "blackDragon" },
|
||||
{ "min" : 398, "max" : 500, "creature": "archangel" }
|
||||
]
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
PlayerState::PlayerState()
|
||||
: color(-1), human(false), enteredWinningCheatCode(false),
|
||||
: color(-1), human(false), cheated(false), enteredWinningCheatCode(false),
|
||||
enteredLosingCheatCode(false), status(EPlayerStatus::INGAME)
|
||||
{
|
||||
setNodeType(PLAYER);
|
||||
@ -29,6 +29,7 @@ PlayerState::PlayerState(PlayerState && other) noexcept:
|
||||
human(other.human),
|
||||
team(other.team),
|
||||
resources(other.resources),
|
||||
cheated(other.cheated),
|
||||
enteredWinningCheatCode(other.enteredWinningCheatCode),
|
||||
enteredLosingCheatCode(other.enteredLosingCheatCode),
|
||||
status(other.status),
|
||||
|
@ -39,6 +39,7 @@ public:
|
||||
std::vector<QuestInfo> quests; //store info about all received quests
|
||||
std::vector<Bonus> battleBonuses; //additional bonuses to be added during battle with neutrals
|
||||
|
||||
bool cheated;
|
||||
bool enteredWinningCheatCode, enteredLosingCheatCode; //if true, this player has entered cheat codes for loss / victory
|
||||
EPlayerStatus status;
|
||||
std::optional<ui8> daysWithoutCastle;
|
||||
@ -83,6 +84,7 @@ public:
|
||||
h & visitedObjects;
|
||||
h & status;
|
||||
h & daysWithoutCastle;
|
||||
h & cheated;
|
||||
h & battleBonuses;
|
||||
h & enteredLosingCheatCode;
|
||||
h & enteredWinningCheatCode;
|
||||
|
@ -2514,6 +2514,7 @@ void PlayerCheated::applyGs(CGameState * gs) const
|
||||
|
||||
gs->getPlayerState(player)->enteredLosingCheatCode = losingCheatCode;
|
||||
gs->getPlayerState(player)->enteredWinningCheatCode = winningCheatCode;
|
||||
gs->getPlayerState(player)->cheated = true;
|
||||
}
|
||||
|
||||
void PlayerStartsTurn::applyGs(CGameState * gs) const
|
||||
|
@ -204,8 +204,8 @@ class HeroTypeID : public Identifier<HeroTypeID>
|
||||
public:
|
||||
using Identifier<HeroTypeID>::Identifier;
|
||||
///json serialization helpers
|
||||
static si32 decode(const std::string & identifier);
|
||||
static std::string encode(const si32 index);
|
||||
DLL_LINKAGE static si32 decode(const std::string & identifier);
|
||||
DLL_LINKAGE static std::string encode(const si32 index);
|
||||
static std::string entityType();
|
||||
|
||||
DLL_LINKAGE static const HeroTypeID NONE;
|
||||
@ -603,8 +603,8 @@ public:
|
||||
|
||||
static_assert (AFTER_LAST == BACKPACK_START, "incorrect number of artifact slots");
|
||||
|
||||
static si32 decode(const std::string & identifier);
|
||||
static std::string encode(const si32 index);
|
||||
DLL_LINKAGE static si32 decode(const std::string & identifier);
|
||||
DLL_LINKAGE static std::string encode(const si32 index);
|
||||
};
|
||||
|
||||
class ArtifactPosition : public IdentifierWithEnum<ArtifactPosition, ArtifactPositionBase>
|
||||
@ -643,8 +643,8 @@ public:
|
||||
using IdentifierWithEnum<ArtifactID, ArtifactIDBase>::IdentifierWithEnum;
|
||||
|
||||
///json serialization helpers
|
||||
static si32 decode(const std::string & identifier);
|
||||
static std::string encode(const si32 index);
|
||||
DLL_LINKAGE static si32 decode(const std::string & identifier);
|
||||
DLL_LINKAGE static std::string encode(const si32 index);
|
||||
static std::string entityType();
|
||||
};
|
||||
|
||||
@ -683,8 +683,8 @@ public:
|
||||
using IdentifierWithEnum<CreatureID, CreatureIDBase>::IdentifierWithEnum;
|
||||
|
||||
///json serialization helpers
|
||||
static si32 decode(const std::string & identifier);
|
||||
static std::string encode(const si32 index);
|
||||
DLL_LINKAGE static si32 decode(const std::string & identifier);
|
||||
DLL_LINKAGE static std::string encode(const si32 index);
|
||||
static std::string entityType();
|
||||
};
|
||||
|
||||
@ -801,8 +801,8 @@ public:
|
||||
using IdentifierWithEnum<SpellID, SpellIDBase>::IdentifierWithEnum;
|
||||
|
||||
///json serialization helpers
|
||||
static si32 decode(const std::string & identifier);
|
||||
static std::string encode(const si32 index);
|
||||
DLL_LINKAGE static si32 decode(const std::string & identifier);
|
||||
DLL_LINKAGE static std::string encode(const si32 index);
|
||||
static std::string entityType();
|
||||
};
|
||||
|
||||
@ -855,8 +855,8 @@ class TerrainId : public IdentifierWithEnum<TerrainId, TerrainIdBase>
|
||||
public:
|
||||
using IdentifierWithEnum<TerrainId, TerrainIdBase>::IdentifierWithEnum;
|
||||
|
||||
static si32 decode(const std::string & identifier);
|
||||
static std::string encode(const si32 index);
|
||||
DLL_LINKAGE static si32 decode(const std::string & identifier);
|
||||
DLL_LINKAGE static std::string encode(const si32 index);
|
||||
static std::string entityType();
|
||||
};
|
||||
|
||||
|
@ -109,11 +109,18 @@ bool PlayerMessageProcessor::handleHostCommand(PlayerColor player, const std::st
|
||||
}
|
||||
if(words.size() == 2 && words[1] == "cheaters")
|
||||
{
|
||||
if (cheaters.empty())
|
||||
broadcastSystemMessage("No cheaters registered!");
|
||||
int playersCheated = 0;
|
||||
for (const auto & player : gameHandler->gameState()->players)
|
||||
{
|
||||
if(player.second.cheated)
|
||||
{
|
||||
broadcastSystemMessage("Player " + player.first.toString() + " is cheater!");
|
||||
playersCheated++;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const & entry : cheaters)
|
||||
broadcastSystemMessage("Player " + entry.toString() + " is cheater!");
|
||||
if (!playersCheated)
|
||||
broadcastSystemMessage("No cheaters registered!");
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -411,7 +418,10 @@ bool PlayerMessageProcessor::handleCheatCode(const std::string & cheat, PlayerCo
|
||||
|
||||
std::vector<std::string> parameters = words;
|
||||
|
||||
cheaters.insert(i.first);
|
||||
PlayerCheated pc;
|
||||
pc.player = i.first;
|
||||
gameHandler->sendAndApply(&pc);
|
||||
|
||||
playerTargetedCheat = true;
|
||||
parameters.erase(parameters.begin());
|
||||
|
||||
@ -427,10 +437,13 @@ bool PlayerMessageProcessor::handleCheatCode(const std::string & cheat, PlayerCo
|
||||
executeCheatCode(cheatName, i.first, h->id, parameters);
|
||||
}
|
||||
|
||||
PlayerCheated pc;
|
||||
pc.player = player;
|
||||
gameHandler->sendAndApply(&pc);
|
||||
|
||||
if (!playerTargetedCheat)
|
||||
executeCheatCode(cheatName, player, currObj, words);
|
||||
|
||||
cheaters.insert(player);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,6 @@ class CGameHandler;
|
||||
|
||||
class PlayerMessageProcessor
|
||||
{
|
||||
std::set<PlayerColor> cheaters;
|
||||
|
||||
void executeCheatCode(const std::string & cheatName, PlayerColor player, ObjectInstanceID currObj, const std::vector<std::string> & arguments );
|
||||
bool handleCheatCode(const std::string & cheatFullCommand, PlayerColor player, ObjectInstanceID currObj);
|
||||
bool handleHostCommand(PlayerColor player, const std::string & message);
|
||||
@ -60,6 +58,5 @@ public:
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & cheaters;
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user