1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-21 19:19:26 +02:00

highscore refactoring

This commit is contained in:
Laserlicht 2024-08-02 19:37:46 +02:00
parent 86477c7b04
commit 9ceb1c567d
9 changed files with 155 additions and 114 deletions

View File

@ -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)
{

View File

@ -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;

View File

@ -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)
{

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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
View 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

View File

@ -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