openclonk/src/control/C4RoundResults.cpp

436 lines
13 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2008-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
// Round result information to be displayed in game over dialog
#include "C4Include.h"
#include "control/C4RoundResults.h"
#include "player/C4Player.h"
#include "game/C4Game.h"
#include "object/C4Def.h"
#include "object/C4Object.h"
#include "player/C4PlayerList.h"
#include "object/C4GameObjects.h"
#include "object/C4DefList.h"
// *** C4RoundResultsPlayer
void C4RoundResultsPlayer::CompileFunc(StdCompiler *pComp)
{
// remember to adjust operator = and == when adding values here!
pComp->Value(mkNamingAdapt(id, "ID", 0));
// pComp->Value(mkNamingAdapt(fctBigIcon, "Icon", C4TargetFacet())); - not possible
pComp->Value(mkNamingAdapt(iTotalPlayingTime, "TotalPlayingTime", 0u));
pComp->Value(mkNamingAdapt(iScoreOld, "SettlementScoreOld", -1));
pComp->Value(mkNamingAdapt(iScoreNew, "SettlementScoreNew", -1));
pComp->Value(mkNamingAdapt(iLeagueScoreNew, "Score", -1)); // name used in league reply!
pComp->Value(mkNamingAdapt(iLeagueScoreGain, "GameScore", -1)); // name used in league reply!
pComp->Value(mkNamingAdapt(iLeagueRankNew, "Rank", 0)); // name used in league reply!
pComp->Value(mkNamingAdapt(iLeagueRankSymbolNew, "RankSymbol", 0)); // name used in league reply!
pComp->Value(mkNamingAdapt(sLeagueProgressData, "LeagueProgressData", StdCopyStrBuf()));
StdEnumEntry<LeagueStatus> LeagueStatusEntries[] =
{
{ "", RRPLS_Unknown },
{ "Lost", RRPLS_Lost },
{ "Won", RRPLS_Won },
};
pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(eLeagueStatus, LeagueStatusEntries), "Status", RRPLS_Unknown)); // name used in league reply!
}
void C4RoundResultsPlayer::EvaluatePlayer(C4Player *pPlr)
{
assert(pPlr);
// set fields by player
iTotalPlayingTime = pPlr->TotalPlayingTime;
if (pPlr->Evaluated)
{
iScoreNew = pPlr->TotalScore;
iScoreOld = iScoreNew - pPlr->LastRound.FinalScore;
}
else
{
// player not evaluated (e.g., removed by disconnect): Old score known only
iScoreOld = pPlr->TotalScore;
}
// load icon from player
fctBigIcon.Clear();
if (pPlr->BigIcon.Surface)
{
fctBigIcon.Create(pPlr->BigIcon.Wdt, pPlr->BigIcon.Hgt);
pPlr->BigIcon.Draw(fctBigIcon);
}
// progress data by player
C4PlayerInfo *pInfo = pPlr->GetInfo();
if (pInfo)
{
sLeagueProgressData.Copy(pInfo->GetLeagueProgressData());
}
}
void C4RoundResultsPlayer::EvaluateLeague(C4RoundResultsPlayer *pLeaguePlayerInfo)
{
assert(pLeaguePlayerInfo);
//
// copy league info
iLeagueScoreNew = pLeaguePlayerInfo->iLeagueScoreNew;
iLeagueScoreGain = pLeaguePlayerInfo->iLeagueScoreGain;
iLeagueRankNew = pLeaguePlayerInfo->iLeagueRankNew;
iLeagueRankSymbolNew = pLeaguePlayerInfo->iLeagueRankSymbolNew;
sLeagueProgressData =pLeaguePlayerInfo->sLeagueProgressData;
}
void C4RoundResultsPlayer::AddCustomEvaluationString(const char *szCustomString)
{
if (sCustomEvaluationStrings.getLength()) sCustomEvaluationStrings.Append(" ");
sCustomEvaluationStrings.Append(szCustomString);
}
bool C4RoundResultsPlayer::operator ==(const C4RoundResultsPlayer &cmp)
{
// cmp all xcept icon
if (id != cmp.id) return false;
if (iTotalPlayingTime != cmp.iTotalPlayingTime) return false;
if (iScoreOld != cmp.iScoreOld) return false;
if (iScoreNew != cmp.iScoreNew) return false;
if (sCustomEvaluationStrings != cmp.sCustomEvaluationStrings) return false;
if (iLeagueScoreNew != cmp.iLeagueScoreNew) return false;
if (iLeagueScoreGain != cmp.iLeagueScoreGain) return false;
if (iLeagueRankNew != cmp.iLeagueRankNew) return false;
if (iLeagueRankSymbolNew != cmp.iLeagueRankSymbolNew) return false;
if (eLeagueStatus != cmp.eLeagueStatus) return false;
if (sLeagueProgressData != cmp.sLeagueProgressData) return false;
return true;
}
C4RoundResultsPlayer &C4RoundResultsPlayer::operator =(const C4RoundResultsPlayer &cpy)
{
if (this == &cpy) return *this;
// assign all xcept icon
id = cpy.id;
iTotalPlayingTime = cpy.iTotalPlayingTime;
iScoreOld = cpy.iScoreOld;
iScoreNew = cpy.iScoreNew;
sCustomEvaluationStrings = cpy.sCustomEvaluationStrings;
iLeagueScoreNew = cpy.iLeagueScoreNew;
iLeagueScoreGain = cpy.iLeagueScoreGain;
iLeagueRankNew = cpy.iLeagueRankNew;
iLeagueRankSymbolNew = cpy.iLeagueRankSymbolNew;
eLeagueStatus = cpy.eLeagueStatus;
sLeagueProgressData = cpy.sLeagueProgressData;
return *this;
}
// *** C4RoundResultsPlayers
void C4RoundResultsPlayers::Clear()
{
while (iPlayerCount) delete ppPlayers[--iPlayerCount];
delete [] ppPlayers;
ppPlayers = NULL;
iPlayerCapacity = 0;
}
void C4RoundResultsPlayers::CompileFunc(StdCompiler *pComp)
{
bool fCompiler = pComp->isCompiler();
if (fCompiler) Clear();
int32_t iTemp = iPlayerCount;
pComp->Value(mkNamingCountAdapt<int32_t>(iTemp, "Player"));
if (iTemp < 0 || iTemp > C4MaxPlayer)
{ pComp->excCorrupt("player count out of range"); return; }
// Grow list, if necessary
if (fCompiler && iTemp > iPlayerCapacity)
{
GrowList(iTemp - iPlayerCapacity);
iPlayerCount = iTemp;
ZeroMem(ppPlayers, sizeof(*ppPlayers) * iPlayerCount);
}
// Compile
pComp->Value(mkNamingAdapt(mkArrayAdaptMap(ppPlayers, iPlayerCount, mkPtrAdaptNoNull<C4RoundResultsPlayer>), "Player"));
// Force specialization
mkPtrAdaptNoNull<C4RoundResultsPlayer>(*ppPlayers);
}
C4RoundResultsPlayer *C4RoundResultsPlayers::GetByIndex(int32_t idx) const
{
if (idx>=0 && idx<iPlayerCount)
return ppPlayers[idx];
else
return NULL;
}
C4RoundResultsPlayer *C4RoundResultsPlayers::GetByID(int32_t id) const
{
for (int32_t idx=0; idx<iPlayerCount; ++idx)
if (ppPlayers[idx]->GetID() == id)
return ppPlayers[idx];
return NULL;
}
void C4RoundResultsPlayers::GrowList(size_t iByVal)
{
// create new list (out of mem: simply returns here; info list remains in a valid state)
C4RoundResultsPlayer **ppNew = new C4RoundResultsPlayer *[iPlayerCapacity += iByVal];
// move existing
if (ppPlayers)
{
memcpy(ppNew, ppPlayers, iPlayerCount * sizeof(C4RoundResultsPlayer *));
delete [] ppPlayers;
}
// assign new
ppPlayers = ppNew;
}
void C4RoundResultsPlayers::Add(C4RoundResultsPlayer *pNewPlayer)
{
assert(pNewPlayer);
if (iPlayerCount == iPlayerCapacity) GrowList(4);
ppPlayers[iPlayerCount++] = pNewPlayer;
}
C4RoundResultsPlayer *C4RoundResultsPlayers::GetCreateByID(int32_t id)
{
assert(id);
// find existing
C4RoundResultsPlayer *pPlr = GetByID(id);
// not found: Add new
if (!pPlr)
{
pPlr = new C4RoundResultsPlayer();
pPlr->SetID(id);
Add(pPlr);
}
return pPlr;
}
bool C4RoundResultsPlayers::operator ==(const C4RoundResultsPlayers &cmp)
{
if (iPlayerCount != cmp.iPlayerCount) return false;
for (int32_t i = 0; i < iPlayerCount; ++i)
if (!(*ppPlayers[i] == *cmp.ppPlayers[i]))
return false;
// equal
return true;
}
C4RoundResultsPlayers &C4RoundResultsPlayers::operator =(const C4RoundResultsPlayers &cpy)
{
Clear();
C4RoundResultsPlayer *pPlr; int32_t i=0;
while ((pPlr = cpy.GetByIndex(i++)))
Add(new C4RoundResultsPlayer(*pPlr));
return *this;
}
// *** C4RoundResults
void C4RoundResults::Clear()
{
Players.Clear();
Goals.Clear();
iPlayingTime = 0;
sCustomEvaluationStrings.Clear();
iLeaguePerformance = 0;
sNetResult.Clear();
eNetResult = NR_None;
fHideSettlementScore=false;
}
void C4RoundResults::Init()
{
if (Game.C4S.Game.IsMelee())
fHideSettlementScore=true;
else fHideSettlementScore=false;
}
void C4RoundResults::CompileFunc(StdCompiler *pComp)
{
bool fCompiler = pComp->isCompiler();
if (fCompiler) Clear();
pComp->Value(mkNamingAdapt(Goals, "Goals", C4IDList()));
pComp->Value(mkNamingAdapt(iPlayingTime, "PlayingTime", 0u));
pComp->Value(mkNamingAdapt(fHideSettlementScore, "HideSettlementScore", Game.C4S.Game.IsMelee()));
pComp->Value(mkNamingAdapt(sCustomEvaluationStrings, "CustomEvaluationStrings", StdCopyStrBuf()));
pComp->Value(mkNamingAdapt(iLeaguePerformance, "LeaguePerformance", 0));
pComp->Value(mkNamingAdapt(Players, "PlayerInfos", C4RoundResultsPlayers()));
pComp->Value(mkNamingAdapt(sNetResult, "NetResult", StdCopyStrBuf()));
StdEnumEntry<NetResult> NetResultEntries[] =
{
{ "", NR_None },
{ "LeagueOK", NR_LeagueOK },
{ "LeagueError", NR_LeagueError},
{ "NetError", NR_NetError },
};
pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(eNetResult, NetResultEntries), "NetResult", NR_None));
}
void C4RoundResults::EvaluateGoals(C4IDList &GoalList, C4IDList &FulfilledGoalList, int32_t iPlayerNumber)
{
// clear prev
GoalList.Clear(); FulfilledGoalList.Clear();
// Items
int32_t cnt; C4ID idGoal;
for (cnt=0; (idGoal=::Objects.GetListID(C4D_Goal,cnt)); cnt++)
{
// determine if the goal is fulfilled - do the calls even if the menu is not to be opened to ensure synchronization
bool fFulfilled = false;;
C4Object *pObj = C4Id2Def(idGoal) ? ::Objects.Find(::Definitions.ID2Def(idGoal)) : NULL;
if (pObj)
{
// Check fulfilled per player, this enables the possibility of rivalry.
C4AulParSet pars(iPlayerNumber);
fFulfilled = !!pObj->Call(PSF_IsFulfilled, &pars);
}
GoalList.SetIDCount(idGoal, cnt, true);
if (fFulfilled) FulfilledGoalList.SetIDCount(idGoal, 1, true);
}
}
void C4RoundResults::EvaluateGame()
{
// set game data
C4Player *pFirstLocalPlayer = ::Players.GetLocalByIndex(0);
int32_t iFirstLocalPlayer = pFirstLocalPlayer ? pFirstLocalPlayer->Number : NO_OWNER;
EvaluateGoals(Goals, FulfilledGoals, iFirstLocalPlayer);
iPlayingTime = Game.Time;
}
void C4RoundResults::EvaluateNetwork(C4RoundResults::NetResult eNetResult, const char *szResultMsg)
{
// take result only if there was no previous result (the previous one is usually more specific)
if (!HasNetResult())
{
this->eNetResult = eNetResult;
if (szResultMsg) sNetResult.Copy(szResultMsg); else sNetResult.Clear();
}
}
void C4RoundResults::EvaluateLeague(const char *szResultMsg, bool fSuccess, const C4RoundResultsPlayers &rLeagueInfo)
{
// League evaluation imples network evaluation
Game.RoundResults.EvaluateNetwork(fSuccess ? C4RoundResults::NR_LeagueOK : C4RoundResults::NR_LeagueError, szResultMsg);
// Evaluation called by league: Sets new league scores and ranks
C4RoundResultsPlayer *pPlr, *pOwnPlr; int32_t i = 0;
while ((pPlr = rLeagueInfo.GetByIndex(i++)))
{
pOwnPlr = Players.GetCreateByID(pPlr->GetID());
pOwnPlr->EvaluateLeague(pPlr);
}
}
void C4RoundResults::EvaluatePlayer(C4Player *pPlr)
{
// Evaluation called by player when it's evaluated
assert(pPlr);
C4RoundResultsPlayer *pOwnPlr = Players.GetCreateByID(pPlr->ID);
pOwnPlr->EvaluatePlayer(pPlr);
}
void C4RoundResults::AddCustomEvaluationString(const char *szCustomString, int32_t idPlayer)
{
// Set custom string to be shown in game over dialog
// idPlayer==0 for global strings
if (!idPlayer)
{
if (sCustomEvaluationStrings.getLength()) sCustomEvaluationStrings.AppendChar('|');
sCustomEvaluationStrings.Append(szCustomString);
}
else
{
C4RoundResultsPlayer *pOwnPlr = Players.GetCreateByID(idPlayer);
pOwnPlr->AddCustomEvaluationString(szCustomString);
}
}
void C4RoundResults::HideSettlementScore(bool fHide)
{
fHideSettlementScore=fHide;
}
bool C4RoundResults::SettlementScoreIsHidden()
{
return fHideSettlementScore;
}
void C4RoundResults::SetLeaguePerformance(int32_t iNewPerf, int32_t idPlayer)
{
// Store to be sent later. idPlayer == 0 means global performance.
if(!idPlayer)
{
iLeaguePerformance = iNewPerf;
}
else
{
C4RoundResultsPlayer *pOwnPlr = Players.GetCreateByID(idPlayer);
pOwnPlr->SetLeaguePerformance(iNewPerf);
}
}
int32_t C4RoundResults::GetLeaguePerformance(int32_t idPlayer) const
{
if(!idPlayer)
return iLeaguePerformance;
else
if(C4RoundResultsPlayer *pPlr = Players.GetByID(idPlayer))
return pPlr->GetLeaguePerformance();
return 0;
}
bool C4RoundResults::Load(C4Group &hGroup, const char *szFilename)
{
// clear previous
Clear();
// load file contents
StdStrBuf Buf;
if (!hGroup.LoadEntryString(szFilename, &Buf)) return false;
// compile
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(*this, "RoundResults"), Buf, szFilename)) return false;
// done, success
return true;
}
bool C4RoundResults::Save(C4Group &hGroup, const char *szFilename)
{
// remove previous entry from group
hGroup.DeleteEntry(szFilename);
// decompile
try
{
StdStrBuf Buf = DecompileToBuf<StdCompilerINIWrite>(mkNamingAdapt(*this, "RoundResults"));
// save it, if not empty
if (Buf.getLength())
if (!hGroup.Add(szFilename, Buf, false, true))
return false;
}
catch (StdCompiler::Exception *)
{ return false; }
// done, success
return true;
}
// *** C4PacketLeagueRoundResults
void C4PacketLeagueRoundResults::CompileFunc(StdCompiler *pComp)
{
pComp->Value(mkNamingAdapt(fSuccess, "Success", false));
pComp->Value(mkNamingAdapt(sResultsString, "ResultString", StdCopyStrBuf()));
pComp->Value(Players);
}