Browse Source

highscore refactoring

Laserlicht 1 year ago
parent
commit
9ceb1c567d

+ 2 - 31
client/CServerHandler.cpp

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

+ 0 - 2
client/CServerHandler.h

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

+ 0 - 68
client/mainmenu/CHighScoreScreen.cpp

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

+ 0 - 10
client/mainmenu/CHighScoreScreen.h

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

+ 1 - 0
lib/CMakeLists.txt

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

+ 12 - 3
lib/gameState/GameStatistics.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)

+ 2 - 0
lib/gameState/GameStatistics.h

@@ -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 - 0
lib/gameState/HighScore.cpp

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

+ 27 - 0
lib/gameState/HighScore.h

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