mirror of
https://github.com/vcmi/vcmi.git
synced 2025-02-21 19:19:26 +02:00
highscore refactoring
This commit is contained in:
parent
86477c7b04
commit
9ceb1c567d
@ -35,6 +35,7 @@
|
||||
#include "../lib/TurnTimerInfo.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/campaign/CampaignState.h"
|
||||
#include "../lib/gameState/HighScore.h"
|
||||
#include "../lib/CPlayerState.h"
|
||||
#include "../lib/mapping/CMapInfo.h"
|
||||
#include "../lib/mapObjects/CGTownInstance.h"
|
||||
@ -672,39 +673,9 @@ void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameSta
|
||||
setState(EClientState::GAMEPLAY);
|
||||
}
|
||||
|
||||
HighScoreParameter CServerHandler::prepareHighScores(PlayerColor player, bool victory)
|
||||
{
|
||||
const auto * gs = client->gameState();
|
||||
const auto * playerState = gs->getPlayerState(player);
|
||||
|
||||
HighScoreParameter param;
|
||||
param.difficulty = gs->getStartInfo()->difficulty;
|
||||
param.day = gs->getDate();
|
||||
param.townAmount = gs->howManyTowns(player);
|
||||
param.usedCheat = gs->getPlayerState(player)->cheated;
|
||||
param.hasGrail = false;
|
||||
for(const CGHeroInstance * h : playerState->heroes)
|
||||
if(h->hasArt(ArtifactID::GRAIL))
|
||||
param.hasGrail = true;
|
||||
for(const CGTownInstance * t : playerState->towns)
|
||||
if(t->builtBuildings.count(BuildingID::GRAIL))
|
||||
param.hasGrail = true;
|
||||
param.allEnemiesDefeated = true;
|
||||
for (PlayerColor otherPlayer(0); otherPlayer < PlayerColor::PLAYER_LIMIT; ++otherPlayer)
|
||||
{
|
||||
auto ps = gs->getPlayerState(otherPlayer, false);
|
||||
if(ps && otherPlayer != player && !ps->checkVanquished())
|
||||
param.allEnemiesDefeated = false;
|
||||
}
|
||||
param.scenarioName = gs->getMapHeader()->name.toString();
|
||||
param.playerName = gs->getStartInfo()->playerInfos.find(player)->second.name;
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
void CServerHandler::showHighScoresAndEndGameplay(PlayerColor player, bool victory)
|
||||
{
|
||||
HighScoreParameter param = prepareHighScores(player, victory);
|
||||
HighScoreParameter param = HighScore::prepareHighScores(client->gameState(), player, victory);
|
||||
|
||||
if(victory && client->gameState()->getStartInfo()->campState)
|
||||
{
|
||||
|
@ -128,8 +128,6 @@ class CServerHandler final : public IServerAPI, public LobbyInfo, public INetwor
|
||||
|
||||
bool isServerLocal() const;
|
||||
|
||||
HighScoreParameter prepareHighScores(PlayerColor player, bool victory);
|
||||
|
||||
public:
|
||||
/// High-level connection overlay that is capable of (de)serializing network data
|
||||
std::shared_ptr<CConnection> logicConnection;
|
||||
|
@ -34,74 +34,6 @@
|
||||
#include "../../lib/constants/EntityIdentifiers.h"
|
||||
#include "../../lib/gameState/HighScore.h"
|
||||
|
||||
auto HighScoreCalculation::calculate()
|
||||
{
|
||||
struct Result
|
||||
{
|
||||
int basic = 0;
|
||||
int total = 0;
|
||||
int sumDays = 0;
|
||||
bool cheater = false;
|
||||
};
|
||||
|
||||
Result firstResult;
|
||||
Result 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.allEnemiesDefeated ? 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;
|
||||
}
|
||||
|
||||
struct HighScoreCreature
|
||||
{
|
||||
CreatureID creature;
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
static std::vector<HighScoreCreature> getHighscoreCreaturesList()
|
||||
{
|
||||
JsonNode configCreatures(JsonPath::builtin("CONFIG/highscoreCreatures.json"));
|
||||
|
||||
std::vector<HighScoreCreature> ret;
|
||||
|
||||
for(auto & json : configCreatures["creatures"].Vector())
|
||||
{
|
||||
HighScoreCreature entry;
|
||||
entry.creature = CreatureID::decode(json["creature"].String());
|
||||
entry.max = json["max"].isNull() ? std::numeric_limits<int>::max() : json["max"].Integer();
|
||||
entry.min = json["min"].isNull() ? std::numeric_limits<int>::min() : json["min"].Integer();
|
||||
|
||||
ret.push_back(entry);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CreatureID HighScoreCalculation::getCreatureForPoints(int points, bool campaign)
|
||||
{
|
||||
static const std::vector<HighScoreCreature> creatures = getHighscoreCreaturesList();
|
||||
|
||||
int divide = campaign ? 5 : 1;
|
||||
|
||||
for(auto & creature : creatures)
|
||||
if(points / divide <= creature.max && points / divide >= creature.min)
|
||||
return creature.creature;
|
||||
|
||||
throw std::runtime_error("Unable to find creature for score " + std::to_string(points));
|
||||
}
|
||||
|
||||
CHighScoreScreen::CHighScoreScreen(HighScorePage highscorepage, int highlighted)
|
||||
: CWindowObject(BORDERED), highscorepage(highscorepage), highlighted(highlighted)
|
||||
{
|
||||
|
@ -21,16 +21,6 @@ class CFilledTexture;
|
||||
|
||||
class TransparentFilledRectangle;
|
||||
|
||||
class HighScoreCalculation
|
||||
{
|
||||
public:
|
||||
std::vector<HighScoreParameter> parameters;
|
||||
bool isCampaign = false;
|
||||
|
||||
auto calculate();
|
||||
static CreatureID getCreatureForPoints(int points, bool campaign);
|
||||
};
|
||||
|
||||
class CHighScoreScreen : public CWindowObject
|
||||
{
|
||||
public:
|
||||
|
@ -99,6 +99,7 @@ set(lib_MAIN_SRCS
|
||||
|
||||
gameState/CGameState.cpp
|
||||
gameState/CGameStateCampaign.cpp
|
||||
gameState/HighScore.cpp
|
||||
gameState/InfoAboutArmy.cpp
|
||||
gameState/RumorState.cpp
|
||||
gameState/TavernHeroesPool.cpp
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "TerrainHandler.h"
|
||||
#include "CHeroHandler.h"
|
||||
#include "StartInfo.h"
|
||||
#include "HighScore.h"
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
#include "../mapObjects/CGTownInstance.h"
|
||||
#include "../mapObjects/CGObjectInstance.h"
|
||||
@ -32,6 +33,11 @@ StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, cons
|
||||
{
|
||||
StatisticDataSetEntry data;
|
||||
|
||||
HighScoreParameter param = HighScore::prepareHighScores(gs, ps->color, false);
|
||||
HighScoreCalculation scenarioHighScores;
|
||||
scenarioHighScores.parameters.push_back(param);
|
||||
scenarioHighScores.isCampaign = false;
|
||||
|
||||
data.day = gs->getDate(Date::DAY);
|
||||
data.player = ps->color;
|
||||
data.team = ps->team;
|
||||
@ -39,7 +45,7 @@ StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, cons
|
||||
data.status = ps->status;
|
||||
data.resources = ps->resources;
|
||||
data.numberHeroes = ps->heroes.size();
|
||||
data.numberTowns = ps->towns.size();
|
||||
data.numberTowns = gs->howManyTowns(ps->color);
|
||||
data.numberArtifacts = Statistic::getNumberOfArts(ps);
|
||||
data.armyStrength = Statistic::getArmyStrength(ps, true);
|
||||
data.income = Statistic::getIncome(gs, ps);
|
||||
@ -47,6 +53,7 @@ StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, cons
|
||||
data.obeliskVisited = Statistic::getObeliskVisited(gs, ps->team);
|
||||
data.mightMagicRatio = Statistic::getMightMagicRatio(ps);
|
||||
data.numMines = Statistic::getNumMines(gs, ps);
|
||||
data.score = scenarioHighScores.calculate().total;
|
||||
|
||||
return data;
|
||||
}
|
||||
@ -69,7 +76,8 @@ std::string StatisticDataSet::toCsv()
|
||||
ss << "Income" << ";";
|
||||
ss << "MapVisitedRatio" << ";";
|
||||
ss << "ObeliskVisited" << ";";
|
||||
ss << "MightMagicRatio";
|
||||
ss << "MightMagicRatio" << ";";
|
||||
ss << "Score";
|
||||
for(auto & resource : resources)
|
||||
ss << ";" << GameConstants::RESOURCE_NAMES[resource];
|
||||
for(auto & resource : resources)
|
||||
@ -90,7 +98,8 @@ std::string StatisticDataSet::toCsv()
|
||||
ss << entry.income << ";";
|
||||
ss << entry.mapVisitedRatio << ";";
|
||||
ss << entry.obeliskVisited << ";";
|
||||
ss << entry.mightMagicRatio;
|
||||
ss << entry.mightMagicRatio << ";";
|
||||
ss << entry.score;
|
||||
for(auto & resource : resources)
|
||||
ss << ";" << entry.resources[resource];
|
||||
for(auto & resource : resources)
|
||||
|
@ -36,6 +36,7 @@ struct DLL_LINKAGE StatisticDataSetEntry
|
||||
int obeliskVisited;
|
||||
double mightMagicRatio;
|
||||
std::map<EGameResID, int> numMines;
|
||||
int score;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h)
|
||||
{
|
||||
@ -54,6 +55,7 @@ struct DLL_LINKAGE StatisticDataSetEntry
|
||||
h & obeliskVisited;
|
||||
h & mightMagicRatio;
|
||||
h & numMines;
|
||||
h & score;
|
||||
}
|
||||
};
|
||||
|
||||
|
111
lib/gameState/HighScore.cpp
Normal file
111
lib/gameState/HighScore.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* HighScore.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 "HighScore.h"
|
||||
#include "../CPlayerState.h"
|
||||
#include "../constants/StringConstants.h"
|
||||
#include "CGameState.h"
|
||||
#include "StartInfo.h"
|
||||
#include "../mapping/CMapHeader.h"
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
#include "../mapObjects/CGTownInstance.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
HighScoreParameter HighScore::prepareHighScores(const CGameState * gs, PlayerColor player, bool victory)
|
||||
{
|
||||
const auto * playerState = gs->getPlayerState(player);
|
||||
|
||||
HighScoreParameter param;
|
||||
param.difficulty = gs->getStartInfo()->difficulty;
|
||||
param.day = gs->getDate();
|
||||
param.townAmount = gs->howManyTowns(player);
|
||||
param.usedCheat = gs->getPlayerState(player)->cheated;
|
||||
param.hasGrail = false;
|
||||
for(const CGHeroInstance * h : playerState->heroes)
|
||||
if(h->hasArt(ArtifactID::GRAIL))
|
||||
param.hasGrail = true;
|
||||
for(const CGTownInstance * t : playerState->towns)
|
||||
if(t->builtBuildings.count(BuildingID::GRAIL))
|
||||
param.hasGrail = true;
|
||||
param.allEnemiesDefeated = true;
|
||||
for (PlayerColor otherPlayer(0); otherPlayer < PlayerColor::PLAYER_LIMIT; ++otherPlayer)
|
||||
{
|
||||
auto ps = gs->getPlayerState(otherPlayer, false);
|
||||
if(ps && otherPlayer != player && !ps->checkVanquished())
|
||||
param.allEnemiesDefeated = false;
|
||||
}
|
||||
param.scenarioName = gs->getMapHeader()->name.toString();
|
||||
param.playerName = gs->getStartInfo()->playerInfos.find(player)->second.name;
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
HighScoreCalculation::Result HighScoreCalculation::calculate()
|
||||
{
|
||||
Result firstResult;
|
||||
Result 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.allEnemiesDefeated ? 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;
|
||||
}
|
||||
|
||||
struct HighScoreCreature
|
||||
{
|
||||
CreatureID creature;
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
static std::vector<HighScoreCreature> getHighscoreCreaturesList()
|
||||
{
|
||||
JsonNode configCreatures(JsonPath::builtin("CONFIG/highscoreCreatures.json"));
|
||||
|
||||
std::vector<HighScoreCreature> ret;
|
||||
|
||||
for(auto & json : configCreatures["creatures"].Vector())
|
||||
{
|
||||
HighScoreCreature entry;
|
||||
entry.creature = CreatureID::decode(json["creature"].String());
|
||||
entry.max = json["max"].isNull() ? std::numeric_limits<int>::max() : json["max"].Integer();
|
||||
entry.min = json["min"].isNull() ? std::numeric_limits<int>::min() : json["min"].Integer();
|
||||
|
||||
ret.push_back(entry);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CreatureID HighScoreCalculation::getCreatureForPoints(int points, bool campaign)
|
||||
{
|
||||
static const std::vector<HighScoreCreature> creatures = getHighscoreCreaturesList();
|
||||
|
||||
int divide = campaign ? 5 : 1;
|
||||
|
||||
for(auto & creature : creatures)
|
||||
if(points / divide <= creature.max && points / divide >= creature.min)
|
||||
return creature.creature;
|
||||
|
||||
throw std::runtime_error("Unable to find creature for score " + std::to_string(points));
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
@ -9,8 +9,12 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../GameConstants.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CGameState;
|
||||
|
||||
class DLL_LINKAGE HighScoreParameter
|
||||
{
|
||||
public:
|
||||
@ -37,5 +41,28 @@ public:
|
||||
h & playerName;
|
||||
}
|
||||
};
|
||||
class DLL_LINKAGE HighScore
|
||||
{
|
||||
public:
|
||||
static HighScoreParameter prepareHighScores(const CGameState * gs, PlayerColor player, bool victory);
|
||||
};
|
||||
|
||||
class DLL_LINKAGE HighScoreCalculation
|
||||
{
|
||||
public:
|
||||
struct Result
|
||||
{
|
||||
int basic = 0;
|
||||
int total = 0;
|
||||
int sumDays = 0;
|
||||
bool cheater = false;
|
||||
};
|
||||
|
||||
std::vector<HighScoreParameter> parameters;
|
||||
bool isCampaign = false;
|
||||
|
||||
Result calculate();
|
||||
static CreatureID getCreatureForPoints(int points, bool campaign);
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
Loading…
x
Reference in New Issue
Block a user