forked from Mirrors/openclonk
332 lines
12 KiB
C++
332 lines
12 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.
|
|
*/
|
|
// game over dialog showing winners and losers
|
|
|
|
#include "C4Include.h"
|
|
#include "gui/C4GameOverDlg.h"
|
|
|
|
#include "game/C4Application.h"
|
|
#include "object/C4Def.h"
|
|
#include "object/C4DefList.h"
|
|
#include "game/C4Game.h"
|
|
#include "game/C4FullScreen.h"
|
|
#include "player/C4Player.h"
|
|
#include "gui/C4PlayerInfoListBox.h"
|
|
#include "player/C4PlayerList.h"
|
|
#include "object/C4GameObjects.h"
|
|
#include "control/C4GameControl.h"
|
|
#include "graphics/C4GraphicsResource.h"
|
|
|
|
|
|
// ---------------------------------------------------
|
|
// C4GoalDisplay
|
|
|
|
C4GoalDisplay::GoalPicture::GoalPicture(const C4Rect &rcBounds, C4ID idGoal, bool fFulfilled)
|
|
: C4GUI::Window(), idGoal(idGoal), fFulfilled(fFulfilled)
|
|
{
|
|
// bounds
|
|
SetBounds(rcBounds);
|
|
// can't get specialized desc from object at the moment because of potential script callbacks!
|
|
StdStrBuf strGoalName;
|
|
// just get desc from def
|
|
C4Def *pGoalDef = ::Definitions.ID2Def(idGoal);
|
|
if (pGoalDef)
|
|
{
|
|
strGoalName.Copy(pGoalDef->GetName());
|
|
// strGoalDesc.Copy(pGoalDef->GetDesc());
|
|
}
|
|
// get tooltip
|
|
StdStrBuf sToolTip;
|
|
if (fFulfilled)
|
|
sToolTip.Format(LoadResStr("IDS_DESC_GOALFULFILLED"), strGoalName.getData());
|
|
else
|
|
sToolTip.Format(LoadResStr("IDS_DESC_GOALNOTFULFILLED"), strGoalName.getData());
|
|
SetToolTip(sToolTip.getData());
|
|
// create buffered picture of goal definition
|
|
C4Def *pDrawDef = ::Definitions.ID2Def(idGoal);
|
|
if (pDrawDef)
|
|
{
|
|
Picture.Create(C4PictureSize, C4PictureSize);
|
|
// get an object instance to draw (optional; may be zero)
|
|
C4Object *pGoalObj = ::Objects.Find(pDrawDef);
|
|
// draw goal def!
|
|
pDrawDef->Draw(Picture, false, 0, pGoalObj);
|
|
}
|
|
// unfulfilled goal: grey out picture
|
|
if (!fFulfilled)
|
|
Picture.Grayscale(30);
|
|
}
|
|
|
|
void C4GoalDisplay::GoalPicture::DrawElement(C4TargetFacet &cgo)
|
|
{
|
|
// draw area
|
|
C4Facet cgoDraw;
|
|
cgoDraw.Set(cgo.Surface, cgo.X+rcBounds.x+cgo.TargetX, cgo.Y+rcBounds.y+cgo.TargetY, rcBounds.Wdt, rcBounds.Hgt);
|
|
// draw buffered picture
|
|
Picture.Draw(cgoDraw);
|
|
// draw star symbol if fulfilled
|
|
if (fFulfilled)
|
|
{
|
|
cgoDraw.Set(cgoDraw.Surface, cgoDraw.X+cgoDraw.Wdt*1/2, cgoDraw.Y+cgoDraw.Hgt*1/2, cgoDraw.Wdt/2, cgoDraw.Hgt/2);
|
|
C4GUI::Icon::GetIconFacet(C4GUI::Ico_Star).Draw(cgoDraw);
|
|
}
|
|
}
|
|
|
|
void C4GoalDisplay::SetGoals(const C4IDList &rAllGoals, const C4IDList &rFulfilledGoals, int32_t iGoalSymbolHeight)
|
|
{
|
|
// clear previous
|
|
ClearChildren();
|
|
// determine goal display area by goal count
|
|
int32_t iGoalSymbolMargin = C4GUI_DefDlgSmallIndent;
|
|
int32_t iGoalSymbolAreaHeight = 2*iGoalSymbolMargin + iGoalSymbolHeight;
|
|
int32_t iGoalAreaWdt = GetClientRect().Wdt;
|
|
int32_t iGoalsPerRow = std::max<int32_t>(1, iGoalAreaWdt / iGoalSymbolAreaHeight);
|
|
int32_t iGoalCount = rAllGoals.GetNumberOfIDs();
|
|
int32_t iRowCount = (iGoalCount-1) / iGoalsPerRow + 1;
|
|
C4Rect rcNewBounds = GetBounds();
|
|
rcNewBounds.Hgt = GetMarginTop()+GetMarginBottom()+iRowCount*iGoalSymbolAreaHeight;
|
|
SetBounds(rcNewBounds);
|
|
C4GUI::ComponentAligner caAll(GetClientRect(), 0,0, true);
|
|
// place goal symbols in this area
|
|
int32_t iGoal = 0;
|
|
for (int32_t iRow=0; iRow<iRowCount; ++iRow)
|
|
{
|
|
int32_t iColCount = std::min<int32_t>(iGoalCount - iGoal, iGoalsPerRow);
|
|
C4GUI::ComponentAligner caGoalArea(caAll.GetFromTop(iGoalSymbolAreaHeight, iColCount*iGoalSymbolAreaHeight), iGoalSymbolMargin,iGoalSymbolMargin, false);
|
|
for (int32_t iCol=0; iCol<iColCount; ++iCol,++iGoal)
|
|
{
|
|
C4ID idGoal = rAllGoals.GetID(iGoal);
|
|
bool fFulfilled = !!rFulfilledGoals.GetIDCount(idGoal, 1);
|
|
AddElement(new GoalPicture(caGoalArea.GetGridCell(iCol, iColCount, iRow, iRowCount), idGoal, fFulfilled));
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------
|
|
// C4GameOverDlg
|
|
|
|
bool C4GameOverDlg::is_shown = false;
|
|
|
|
C4GameOverDlg::C4GameOverDlg() : C4GUI::Dialog( (C4GUI::GetScreenWdt() < 800) ? (C4GUI::GetScreenWdt()-10) : std::min<int32_t>(C4GUI::GetScreenWdt()-150, 800),
|
|
(C4GUI::GetScreenHgt() < 600) ? (C4GUI::GetScreenHgt()-10) : std::min<int32_t>(C4GUI::GetScreenHgt()-150, 600),
|
|
LoadResStr("IDS_TEXT_EVALUATION"),
|
|
false), pNetResultLabel(nullptr), fIsNetDone(false), fHasNextMissionButton(false)
|
|
{
|
|
is_shown = true; // assume dlg will be shown, soon
|
|
UpdateOwnPos();
|
|
// indents / sizes
|
|
int32_t iDefBtnHeight = 32;
|
|
int32_t iIndentX1=10;
|
|
int32_t iIndentY1=6, iIndentY2=0;
|
|
// main screen components
|
|
C4GUI::ComponentAligner caMain(GetClientRect(), 0,iIndentY1,true);
|
|
int32_t iMainTextWidth = caMain.GetWidth() - 6*iIndentX1;
|
|
caMain.GetFromBottom(iIndentY2);
|
|
// lower button-area
|
|
C4GUI::ComponentAligner caBottom(caMain.GetFromBottom(iDefBtnHeight+iIndentY1*2), iIndentX1,0);
|
|
int32_t iBottomButtonSize = caBottom.GetInnerWidth();
|
|
iBottomButtonSize = std::min<int32_t>(iBottomButtonSize/2-2*iIndentX1, ::GraphicsResource.CaptionFont.GetTextWidth("Quit it, baby! And some.")*2);
|
|
// goal display
|
|
const C4IDList &rGoals = Game.RoundResults.GetGoals();
|
|
const C4IDList &rFulfilledGoals = Game.RoundResults.GetFulfilledGoals();
|
|
if (rGoals.GetNumberOfIDs())
|
|
{
|
|
C4GoalDisplay *pGoalDisplay = new C4GoalDisplay(caMain.GetFromTop(C4GUI_IconExHgt));
|
|
pGoalDisplay->SetGoals(rGoals, rFulfilledGoals, C4GUI_IconExHgt);
|
|
AddElement(pGoalDisplay);
|
|
// goal display may have resized itself; adjust component aligner
|
|
caMain.ExpandTop(C4GUI_IconExHgt - pGoalDisplay->GetBounds().Hgt);
|
|
}
|
|
// league/network result, present or pending
|
|
fIsNetDone = false;
|
|
bool fHasNetResult = Game.RoundResults.HasNetResult();
|
|
const char *szNetResult = nullptr;
|
|
if (Game.Parameters.isLeague() || fHasNetResult)
|
|
{
|
|
if (fHasNetResult)
|
|
szNetResult = Game.RoundResults.GetNetResultString();
|
|
else
|
|
szNetResult = LoadResStr("IDS_TEXT_LEAGUEWAITINGFOREVALUATIO");
|
|
pNetResultLabel = new C4GUI::Label(szNetResult, caMain.GetFromTop(::GraphicsResource.TextFont.GetLineHeight()*2, iMainTextWidth), ACenter, C4GUI_Caption2FontClr, nullptr, false, false, true);
|
|
AddElement(pNetResultLabel);
|
|
// only add label - contents and fIsNetDone will be set in next update
|
|
}
|
|
else
|
|
{
|
|
// otherwise, network is always done
|
|
fIsNetDone = true;
|
|
}
|
|
// extra evaluation string area
|
|
const char *szCustomEvaluationStrings = Game.RoundResults.GetCustomEvaluationStrings();
|
|
if (szCustomEvaluationStrings && *szCustomEvaluationStrings)
|
|
{
|
|
int32_t iMaxHgt = caMain.GetInnerHeight() / 3; // max 1/3rd of height for extra data
|
|
C4GUI::MultilineLabel *pCustomStrings = new C4GUI::MultilineLabel(caMain.GetFromTop(0 /* resized later*/, iMainTextWidth), 0,0, " ", true, true);
|
|
pCustomStrings->AddLine(szCustomEvaluationStrings, &::GraphicsResource.TextFont, C4GUI_MessageFontClr, true, false, nullptr);
|
|
C4Rect rcCustomStringBounds = pCustomStrings->GetBounds();
|
|
if (rcCustomStringBounds.Hgt > iMaxHgt)
|
|
{
|
|
// Buffer too large: Use a scrollbox instead
|
|
delete pCustomStrings;
|
|
rcCustomStringBounds.Hgt = iMaxHgt;
|
|
C4GUI::TextWindow *pCustomStringsWin = new C4GUI::TextWindow(rcCustomStringBounds, 0,0,0, 0,0," ",true, nullptr,0, true);
|
|
pCustomStringsWin->SetDecoration(false, false, nullptr, false);
|
|
pCustomStringsWin->AddTextLine(szCustomEvaluationStrings, &::GraphicsResource.TextFont, C4GUI_MessageFontClr, true, false, nullptr);
|
|
caMain.ExpandTop(-iMaxHgt);
|
|
AddElement(pCustomStringsWin);
|
|
}
|
|
else
|
|
{
|
|
// buffer size OK: Reserve required space
|
|
caMain.ExpandTop(-rcCustomStringBounds.Hgt);
|
|
AddElement(pCustomStrings);
|
|
}
|
|
}
|
|
// player list area
|
|
C4GUI::ComponentAligner caPlayerArea(caMain.GetAll(), iIndentX1,0);
|
|
iPlrListCount = 1; bool fSepTeamLists = false;
|
|
if (Game.Teams.GetTeamCount() == 2 && !Game.Teams.IsAutoGenerateTeams())
|
|
{
|
|
// exactly two predefined teams: Use two player list boxes; one for each team
|
|
iPlrListCount = 2;
|
|
fSepTeamLists = true;
|
|
}
|
|
ppPlayerLists = new C4PlayerInfoListBox *[iPlrListCount];
|
|
for (int32_t i=0; i<iPlrListCount; ++i)
|
|
{
|
|
ppPlayerLists[i] = new C4PlayerInfoListBox(caPlayerArea.GetGridCell(i,iPlrListCount,0,1), C4PlayerInfoListBox::PILBM_Evaluation, fSepTeamLists ? Game.Teams.GetTeamByIndex(i)->GetID() : 0);
|
|
ppPlayerLists[i]->SetSelectionDiabled(true);
|
|
ppPlayerLists[i]->SetDecoration(false, nullptr, true, false);
|
|
AddElement(ppPlayerLists[i]);
|
|
}
|
|
// add buttons
|
|
C4GUI::CallbackButton<C4GameOverDlg> *btnExit;
|
|
pBtnExit = btnExit = new C4GUI::CallbackButton<C4GameOverDlg>(LoadResStr("IDS_BTN_ENDROUND"), caBottom.GetGridCell(0,2, 0,1, iBottomButtonSize, -1, true), &C4GameOverDlg::OnExitBtn);
|
|
btnExit->SetToolTip(LoadResStr("IDS_DESC_ENDTHEROUND"));
|
|
AddElement(btnExit);
|
|
C4GUI::CallbackButton<C4GameOverDlg> *btnContinue;
|
|
pBtnContinue = btnContinue = new C4GUI::CallbackButton<C4GameOverDlg>(LoadResStr("IDS_BTN_CONTINUEGAME"), caBottom.GetGridCell(1,2, 0,1, iBottomButtonSize, -1, true), &C4GameOverDlg::OnContinueBtn);
|
|
btnContinue->SetToolTip(LoadResStr("IDS_DESC_CONTINUETHEROUNDWITHNOFUR"));
|
|
AddElement(btnContinue);
|
|
// convert continue button to "next mission" button if available
|
|
if (Game.NextMission)
|
|
{
|
|
// not available for regular replay and network clients, obviously
|
|
// it is available for films though, so you can create cinematics for adventures
|
|
if (::Control.isCtrlHost() || (Game.C4S.Head.Film == 2))
|
|
{
|
|
fHasNextMissionButton = true;
|
|
btnContinue->SetText(Game.NextMissionText.getData());
|
|
btnContinue->SetToolTip(Game.NextMissionDesc.getData());
|
|
}
|
|
}
|
|
fIsQuitBtnVisible = fIsNetDone || !::Network.isHost();
|
|
// updates
|
|
Application.Add(this);
|
|
Update();
|
|
// initial focus on quit button if visible, so space/enter/low gamepad buttons quit
|
|
if (fIsQuitBtnVisible) SetFocus(btnExit, false);
|
|
}
|
|
|
|
C4GameOverDlg::~C4GameOverDlg()
|
|
{
|
|
Application.Remove(this);
|
|
delete ppPlayerLists;
|
|
is_shown = false;
|
|
}
|
|
|
|
void C4GameOverDlg::Update()
|
|
{
|
|
for (int32_t i=0; i<iPlrListCount; ++i) ppPlayerLists[i]->Update();
|
|
if (pNetResultLabel)
|
|
{
|
|
SetNetResult(Game.RoundResults.GetNetResultString(), Game.RoundResults.GetNetResult(), ::Network.getPendingStreamData(), ::Network.isStreaming());
|
|
}
|
|
// exit/continue button only visible for host if league streaming finished
|
|
bool fBtnsVisible = fIsNetDone || !::Network.isHost();
|
|
if (fBtnsVisible != fIsQuitBtnVisible)
|
|
{
|
|
fIsQuitBtnVisible = fBtnsVisible;
|
|
pBtnExit->SetVisibility(fBtnsVisible);
|
|
pBtnContinue->SetVisibility(fBtnsVisible);
|
|
if (fIsQuitBtnVisible) SetFocus(pBtnExit, false);
|
|
}
|
|
}
|
|
|
|
void C4GameOverDlg::SetNetResult(const char *szResultString, C4RoundResults::NetResult eResultType, size_t iPendingStreamingData, bool fIsStreaming)
|
|
{
|
|
// add info about pending streaming data
|
|
StdStrBuf sResult(szResultString);
|
|
if (fIsStreaming)
|
|
{
|
|
sResult.AppendChar('|');
|
|
sResult.AppendFormat("[!]Transmitting record to league server... (%d kb remaining)", int(iPendingStreamingData/1024));
|
|
}
|
|
// message linebreak into box
|
|
StdStrBuf sBrokenResult;
|
|
::GraphicsResource.TextFont.BreakMessage(sResult.getData(), pNetResultLabel->GetBounds().Wdt, &sBrokenResult, true);
|
|
pNetResultLabel->SetText(sBrokenResult.getData(), false);
|
|
// all done?
|
|
if (eResultType != C4RoundResults::NR_None && !fIsStreaming)
|
|
{
|
|
// a final result is determined and all streaming data has been transmitted
|
|
fIsNetDone = true;
|
|
}
|
|
// network error?
|
|
if (eResultType == C4RoundResults::NR_NetError)
|
|
{
|
|
// disconnected. Do not show winners/losers
|
|
for (int32_t i=0; i<iPlrListCount; ++i) ppPlayerLists[i]->SetMode(C4PlayerInfoListBox::PILBM_EvaluationNoWinners);
|
|
}
|
|
}
|
|
|
|
void C4GameOverDlg::OnExitBtn(C4GUI::Control *btn)
|
|
{
|
|
// callback: exit button pressed.
|
|
Application.QuitGame();
|
|
Close(false);
|
|
}
|
|
|
|
void C4GameOverDlg::OnContinueBtn(C4GUI::Control *btn)
|
|
{
|
|
// callback: continue button pressed
|
|
if (fHasNextMissionButton)
|
|
{
|
|
// switch to next mission if next mission button is pressed
|
|
Application.SetNextMission(Game.NextMission.getData());
|
|
Application.QuitGame();
|
|
}
|
|
Close(true); // unpauses and deletes this object
|
|
}
|
|
|
|
void C4GameOverDlg::OnShown()
|
|
{
|
|
// close some other dialogs
|
|
Game.Scoreboard.HideDlg();
|
|
FullScreen.CloseMenu();
|
|
for (C4Player *plr = ::Players.First; plr; plr = plr->Next)
|
|
plr->CloseMenu();
|
|
// pause game when round results dlg is shown
|
|
Game.Pause();
|
|
}
|
|
|
|
void C4GameOverDlg::OnClosed(bool fOK)
|
|
{
|
|
Game.Unpause();
|
|
typedef C4GUI::Dialog BaseClass;
|
|
BaseClass::OnClosed(fOK); // deletes this object!
|
|
}
|