forked from Mirrors/openclonk
1717 lines
63 KiB
C++
1717 lines
63 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2005-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.
|
|
*/
|
|
// Startup screen for non-parameterized engine start: Player selection dialog
|
|
// Also contains player creation, editing and crew management
|
|
|
|
#include "C4Include.h"
|
|
#include "gui/C4StartupPlrSelDlg.h"
|
|
|
|
#include "graphics/C4Draw.h"
|
|
#include "graphics/C4GraphicsResource.h"
|
|
#include "gui/C4FileSelDlg.h"
|
|
#include "gui/C4MouseControl.h"
|
|
#include "gui/C4StartupMainDlg.h"
|
|
#include "lib/C4Random.h"
|
|
#include "lib/StdColors.h"
|
|
#include "player/C4RankSystem.h"
|
|
|
|
// font clrs
|
|
const uint32_t ClrPlayerItem = 0xffffffff;
|
|
|
|
// Arbitrary cut-off value for player color value. This avoids pitch black
|
|
// colors which look ugly. Note that this limit is only applied in the UI,
|
|
// it's still possible to edit the Player.txt by hand.
|
|
const uint32_t PlayerColorValueLowBound = 64;
|
|
|
|
// ----- C4Utilities
|
|
|
|
StdStrBuf TimeString(int iSeconds)
|
|
{
|
|
int iHours = iSeconds / 3600; iSeconds -= 3600*iHours;
|
|
int iMinutes = iSeconds / 60; iSeconds -= 60*iMinutes;
|
|
return FormatString("%02d:%02d:%02d",iHours,iMinutes,iSeconds);
|
|
}
|
|
|
|
StdStrBuf DateString(int iTime)
|
|
{
|
|
if (!iTime) return StdStrBuf("", true);
|
|
time_t tTime = iTime;
|
|
struct tm *pLocalTime;
|
|
pLocalTime=localtime(&tTime);
|
|
return FormatString( "%02d.%02d.%d %02d:%02d",
|
|
pLocalTime->tm_mday,
|
|
pLocalTime->tm_mon+1,
|
|
pLocalTime->tm_year+1900,
|
|
pLocalTime->tm_hour,
|
|
pLocalTime->tm_min);
|
|
}
|
|
|
|
// ------------------------------------------------
|
|
// --- C4StartupPlrSelDlg::ListItem
|
|
C4StartupPlrSelDlg::ListItem::ListItem(C4StartupPlrSelDlg *pForDlg, C4GUI::ListBox *pForListBox, C4GUI::Element *pInsertBeforeElement, bool fActivated)
|
|
: Control(C4Rect(0,0,0,0)), pCheck(nullptr), pNameLabel(nullptr), pPlrSelDlg(pForDlg), pIcon(nullptr)
|
|
{
|
|
CStdFont &rUseFont = GraphicsResource.FontRegular;
|
|
// calc height
|
|
int32_t iHeight = rUseFont.GetLineHeight() + 2 * IconLabelSpacing;
|
|
// create subcomponents
|
|
pCheck = new C4GUI::CheckBox(C4Rect(0, 0, iHeight, iHeight), nullptr, fActivated);
|
|
pCheck->SetOnChecked(new C4GUI::CallbackHandler<C4StartupPlrSelDlg>(pForDlg, &C4StartupPlrSelDlg::OnItemCheckChange));
|
|
pKeyCheck = new C4KeyBinding(C4KeyCodeEx(K_SPACE), "StartupPlrSelTogglePlayerActive", KEYSCOPE_Gui,
|
|
new C4GUI::ControlKeyCB<ListItem>(*this, &ListItem::KeyCheck), C4CustomKey::PRIO_Ctrl);
|
|
pIcon = new C4GUI::Icon(C4Rect(iHeight + IconLabelSpacing, 0, iHeight, iHeight), C4GUI::Ico_Player);
|
|
pNameLabel = new C4GUI::Label("Q", (iHeight + IconLabelSpacing)*2, IconLabelSpacing, ALeft, ClrPlayerItem, &rUseFont, false, false);
|
|
pNameLabel->SetAutosize(false);
|
|
// calc own bounds - use icon bounds only, because only the height is used when the item is added
|
|
SetBounds(pIcon->GetBounds());
|
|
// add components
|
|
AddElement(pCheck);
|
|
AddElement(pIcon); AddElement(pNameLabel);
|
|
// add to listbox (will get resized horizontally and moved) - zero indent; no tree structure in this dialog
|
|
pForListBox->InsertElement(this, pInsertBeforeElement, 0);
|
|
// update name label width to stretch max listbox width
|
|
C4Rect rcNameLabelBounds = pNameLabel->GetBounds();
|
|
rcNameLabelBounds.Wdt = GetClientRect().Wdt - rcNameLabelBounds.x - IconLabelSpacing;
|
|
pNameLabel->SetBounds(rcNameLabelBounds);
|
|
// context menu
|
|
SetContextHandler(new C4GUI::CBContextHandler<C4StartupPlrSelDlg::ListItem>(this, &C4StartupPlrSelDlg::ListItem::ContextMenu));
|
|
}
|
|
|
|
C4StartupPlrSelDlg::ListItem::~ListItem()
|
|
{
|
|
delete pKeyCheck;
|
|
}
|
|
|
|
const char *C4StartupPlrSelDlg::ListItem::GetName() const
|
|
{
|
|
// name is stored in label only
|
|
return pNameLabel->GetText();
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::ListItem::SetName(const char *szNewName)
|
|
{
|
|
// update name in label
|
|
pNameLabel->SetText(szNewName);
|
|
// tooltip by name, so long names can be read via tooltip
|
|
SetToolTip(szNewName);
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::ListItem::GrabIcon(C4FacetSurface &rFromFacet)
|
|
{
|
|
// take over icon gfx from facet - deletes them from source facet!
|
|
if (rFromFacet.Surface)
|
|
{
|
|
pIcon->GetMFacet().GrabFrom(rFromFacet);
|
|
}
|
|
else
|
|
{
|
|
// reset custom icon
|
|
// following update-call will reset to default icon
|
|
pIcon->GetMFacet().Clear();
|
|
}
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::ListItem::SetIcon(C4GUI::Icons icoNew)
|
|
{
|
|
pIcon->SetIcon(icoNew);
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::ListItem::UpdateOwnPos()
|
|
{
|
|
// parent for client rect
|
|
typedef C4GUI::Window ParentClass;
|
|
ParentClass::UpdateOwnPos();
|
|
// reposition items
|
|
C4GUI::ComponentAligner caBounds(GetContainedClientRect(), IconLabelSpacing, IconLabelSpacing);
|
|
// nothing to reposition for now...
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::ListItem::SetFilename(const StdStrBuf &sNewFN)
|
|
{
|
|
// just set fn - UpdateCore-call will follow later
|
|
Filename.Copy(sNewFN);
|
|
}
|
|
|
|
bool C4StartupPlrSelDlg::ListItem::CheckNameHotkey(const char * c)
|
|
{
|
|
// return whether this item can be selected by entering given char:
|
|
// first char of name must match
|
|
// FIXME: Unicode
|
|
if (!pNameLabel) return false;
|
|
const char *szName = pNameLabel->GetText();
|
|
return szName && (toupper(*szName) == toupper(c[0]));
|
|
}
|
|
|
|
|
|
// ------------------------------------------------
|
|
// --- C4StartupPlrSelDlg::PlayerListItem
|
|
C4StartupPlrSelDlg::PlayerListItem::PlayerListItem(C4StartupPlrSelDlg *pForDlg, C4GUI::ListBox *pForListBox, C4GUI::Element *pInsertBeforeElement, bool fActivated)
|
|
: ListItem(pForDlg, pForListBox, pInsertBeforeElement, fActivated), fHasCustomIcon(false)
|
|
{
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::PlayerListItem::Load(const StdStrBuf &rsFilename)
|
|
{
|
|
int32_t iHeight = GetBounds().Hgt;
|
|
// backup filename
|
|
SetFilename(rsFilename);
|
|
// load player info
|
|
C4Group PlrGroup;
|
|
if (!PlrGroup.Open(Config.AtUserDataPath(rsFilename.getData())))
|
|
throw LoadError(FormatString("Error loading player file from %s: Error opening group: %s", rsFilename.getData(), PlrGroup.GetError()));
|
|
if (!Core.Load(PlrGroup))
|
|
throw LoadError(FormatString("Error loading player file from %s: Core data invalid or missing (Group: %s)!", rsFilename.getData(), PlrGroup.GetError()));
|
|
// load icon
|
|
C4FacetSurface fctIcon;
|
|
if (PlrGroup.FindEntry(C4CFN_BigIcon) && fctIcon.Load(PlrGroup, C4CFN_BigIcon, C4FCT_Full, C4FCT_Full, false, 0))
|
|
fHasCustomIcon = true;
|
|
else
|
|
{
|
|
// no custom icon: create default by player color
|
|
fctIcon.Create(iHeight,iHeight);
|
|
::GraphicsResource.fctPlayerClr.DrawClr(fctIcon, true, Core.PrefColorDw);
|
|
}
|
|
GrabIcon(fctIcon);
|
|
// done loading
|
|
if (!PlrGroup.Close())
|
|
throw LoadError(FormatString("Error loading player file from %s: Error closing group: %s", rsFilename.getData(), PlrGroup.GetError()));
|
|
// default name
|
|
if (!*Core.PrefName) SCopy(GetFilenameOnly(rsFilename.getData()), Core.PrefName, sizeof(Core.PrefName)-1);
|
|
SetName(Core.PrefName);
|
|
}
|
|
|
|
C4GUI::ContextMenu *C4StartupPlrSelDlg::PlayerListItem::ContextMenu()
|
|
{
|
|
// menu operations work on selected item only
|
|
pPlrSelDlg->SetSelection(this);
|
|
// context menu operations
|
|
C4GUI::ContextMenu *pCtx = new C4GUI::ContextMenu();
|
|
pCtx->AddItem(LoadResStr("IDS_BTN_PROPERTIES"), LoadResStr("IDS_DLGTIP_PLAYERPROPERTIES"), C4GUI::Ico_None, new C4GUI::CBMenuHandler<C4StartupPlrSelDlg>(pPlrSelDlg, &C4StartupPlrSelDlg::OnPropertyCtx));
|
|
pCtx->AddItem(LoadResStr("IDS_BTN_DELETE"), LoadResStr("IDS_DLGTIP_PLAYERDELETE"), C4GUI::Ico_None, new C4GUI::CBMenuHandler<C4StartupPlrSelDlg>(pPlrSelDlg, &C4StartupPlrSelDlg::OnDelCtx));
|
|
return pCtx;
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::PlayerListItem::GrabCustomIcon(C4FacetSurface &fctGrabFrom)
|
|
{
|
|
// set flag
|
|
fHasCustomIcon = !!fctGrabFrom.Surface;
|
|
// base class grab
|
|
GrabIcon(fctGrabFrom);
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::PlayerListItem::UpdateCore(C4PlayerInfoCore & NewCore)
|
|
{
|
|
C4Group PlrGroup;
|
|
if (!PlrGroup.Open(Config.AtUserDataPath(GetFilename().getData()))
|
|
|| !NewCore.Save(PlrGroup)
|
|
|| !PlrGroup.Close())
|
|
{
|
|
GetScreen()->ShowMessage(LoadResStr("IDS_FAIL_MODIFY"), "", C4GUI::Ico_Error);
|
|
return;
|
|
}
|
|
Core = NewCore;
|
|
SetName(Core.PrefName);
|
|
// re-set non-custom icons
|
|
if (!fHasCustomIcon)
|
|
{
|
|
fHasCustomIcon = false;
|
|
int32_t iHeight = GetBounds().Hgt;
|
|
C4FacetSurface fctIcon; fctIcon.Create(iHeight,iHeight);
|
|
::GraphicsResource.fctPlayerClr.DrawClr(fctIcon, true, Core.PrefColorDw);
|
|
GrabIcon(fctIcon);
|
|
}
|
|
// update in selection
|
|
C4StartupPlrSelDlg *pDlg = static_cast<C4StartupPlrSelDlg *>(GetDlg());
|
|
if (pDlg && pDlg->GetSelection() == this) pDlg->UpdateSelection();
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::PlayerListItem::SetSelectionInfo(C4GUI::TextWindow *pSelectionInfo)
|
|
{
|
|
// write info text for player
|
|
pSelectionInfo->ClearText(false);
|
|
pSelectionInfo->AddTextLine(FormatString("%s", Core.PrefName).getData(), &C4Startup::Get()->Graphics.BookFontCapt, ClrPlayerItem, false, false);
|
|
pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_DESC_PLAYER"), (int)Core.TotalScore, (int)Core.Rounds, (int)Core.RoundsWon, (int)Core.RoundsLost, TimeString(Core.TotalPlayingTime).getData(), Core.Comment).getData(), &C4Startup::Get()->Graphics.BookFont, ClrPlayerItem, false, false);
|
|
if (Core.LastRound.Title[0])
|
|
pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_DESC_LASTGAME"),Core.LastRound.Title.getData(),DateString(Core.LastRound.Date).getData(),TimeString(Core.LastRound.Duration).getData(),(int)Core.LastRound.FinalScore).getData(), &C4Startup::Get()->Graphics.BookFont, ClrPlayerItem, false, false);
|
|
pSelectionInfo->UpdateHeight();
|
|
}
|
|
|
|
StdStrBuf C4StartupPlrSelDlg::PlayerListItem::GetDelWarning()
|
|
{
|
|
StdStrBuf sWarning;
|
|
sWarning.Format(LoadResStr("IDS_MSG_DELETEPLR"), Core.PrefName);
|
|
int32_t iPlrTime = Core.TotalPlayingTime;
|
|
if (iPlrTime > 60*60*10)
|
|
sWarning.Append(FormatString(LoadResStr("IDS_MSG_DELETEPLR_PLAYTIME"),
|
|
TimeString(iPlrTime).getData()).getData());
|
|
return sWarning;
|
|
}
|
|
|
|
bool C4StartupPlrSelDlg::PlayerListItem::MoveFilename(const char *szToFilename)
|
|
{
|
|
// anything to do?
|
|
if (ItemIdentical(GetFilename().getData(), szToFilename)) return true;
|
|
// do it
|
|
StdStrBuf PathFrom(Config.General.UserDataPath);
|
|
PathFrom.Append(GetFilename());
|
|
StdStrBuf PathTo(Config.General.UserDataPath);
|
|
PathTo.Append(szToFilename);
|
|
if (!MoveItem(PathFrom.getData(), PathTo.getData())) return false;
|
|
// reflect change in class
|
|
SetFilename(StdStrBuf(szToFilename));
|
|
return true;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------
|
|
// --- C4StartupPlrSelDlg::CrewListItem
|
|
|
|
C4StartupPlrSelDlg::CrewListItem::CrewListItem(C4StartupPlrSelDlg *pForDlg, C4GUI::ListBox *pForListBox, uint32_t dwPlrClr)
|
|
: ListItem(pForDlg, pForListBox, nullptr, false), fLoaded(false), dwPlrClr(dwPlrClr), pParentGrp(nullptr)
|
|
{
|
|
SetIcon(C4GUI::Ico_Wait);
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::CrewListItem::UpdateClonkEnabled()
|
|
{
|
|
if (!fLoaded) return;
|
|
Core.Participation = pCheck->GetChecked();
|
|
// immediate save of changes
|
|
RewriteCore();
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::CrewListItem::Load(C4Group &rGrp, const StdStrBuf &rsFilename)
|
|
{
|
|
// backup filename (doesn't include path)
|
|
SetFilename(rsFilename);
|
|
// load core
|
|
C4Group CrewGroup;
|
|
if (!CrewGroup.OpenAsChild(&rGrp, rsFilename.getData()))
|
|
throw LoadError(FormatString("Error loading crew file from %s in %s: Error opening group: %s",
|
|
rsFilename.getData(), rGrp.GetFullName().getData(), CrewGroup.GetError()));
|
|
if (!Core.Load(CrewGroup))
|
|
throw LoadError(FormatString("Error loading crew file from %s: Core data invalid or missing (Group: %s)!",
|
|
CrewGroup.GetFullName().getData(), CrewGroup.GetError()));
|
|
ListItem::SetName(Core.Name);
|
|
pCheck->SetChecked(!!Core.Participation);
|
|
// load rank as icon
|
|
C4FacetSurface fctIcon;
|
|
if (fctIcon.Load(CrewGroup, C4CFN_ClonkRank, C4FCT_Full, C4FCT_Full, false, true))
|
|
{
|
|
GrabIcon(fctIcon);
|
|
}
|
|
else
|
|
{
|
|
// no custom icon: create default by rank system
|
|
if (C4RankSystem::DrawRankSymbol(&fctIcon, Core.Rank, &::GraphicsResource.fctRank, ::GraphicsResource.iNumRanks, true))
|
|
GrabIcon(fctIcon);
|
|
}
|
|
// backup group loaded from - assumes it stays valid!
|
|
pParentGrp = &rGrp;
|
|
// load success!
|
|
fLoaded=true;
|
|
}
|
|
|
|
C4GUI::ContextMenu *C4StartupPlrSelDlg::CrewListItem::ContextMenu()
|
|
{
|
|
// menu operations work on selected item only
|
|
pPlrSelDlg->SetSelection(this);
|
|
// context menu operations
|
|
C4GUI::ContextMenu *pCtx = new C4GUI::ContextMenu();
|
|
pCtx->AddItem(LoadResStr("IDS_BTN_RENAME"), LoadResStr("IDS_DESC_CREWRENAME"), C4GUI::Ico_None, new C4GUI::CBMenuHandler<C4StartupPlrSelDlg>(pPlrSelDlg, &C4StartupPlrSelDlg::OnPropertyCtx));
|
|
pCtx->AddItem(LoadResStr("IDS_BTN_DELETE"), LoadResStr("IDS_MSG_DELETECLONK_DESC"), C4GUI::Ico_None, new C4GUI::CBMenuHandler<C4StartupPlrSelDlg>(pPlrSelDlg, &C4StartupPlrSelDlg::OnDelCtx));
|
|
pCtx->AddItem(LoadResStr("IDS_MSG_SETDEATHMESSAGE"), LoadResStr("IDS_MSG_SETTHEMESSAGETHATAPPEARWH"), C4GUI::Ico_None, new C4GUI::CBMenuHandler<C4StartupPlrSelDlg::CrewListItem>(this, &C4StartupPlrSelDlg::CrewListItem::OnDeathMessageCtx));
|
|
return pCtx;
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::CrewListItem::OnDeathMessageCtx(C4GUI::Element *el)
|
|
{
|
|
// Death message dialog
|
|
C4GUI::InputDialog *pDlg;
|
|
GetScreen()->ShowRemoveDlg(pDlg=new C4GUI::InputDialog(LoadResStr("IDS_MSG_ENTERNEWDEATHMESSAGE"), LoadResStr("IDS_MSG_SETDEATHMESSAGE"), C4GUI::Ico_Ex_Comment, new C4GUI::InputCallback<C4StartupPlrSelDlg::CrewListItem>(this, &C4StartupPlrSelDlg::CrewListItem::OnDeathMessageSet), false));
|
|
pDlg->SetMaxText(C4MaxDeathMsg);
|
|
pDlg->SetInputText(Core.DeathMessage);
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::CrewListItem::OnDeathMessageSet(const StdStrBuf &rsNewMessage)
|
|
{
|
|
// copy msg
|
|
if (!rsNewMessage) *Core.DeathMessage='\0'; else SCopy(rsNewMessage.getData(), Core.DeathMessage, C4MaxDeathMsg);
|
|
// save
|
|
RewriteCore();
|
|
// acoustic feedback
|
|
C4GUI::GUISound("UI::Confirmed");
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::CrewListItem::RewriteCore()
|
|
{
|
|
if (!fLoaded) return;
|
|
C4Group CrewGroup;
|
|
if (!CrewGroup.OpenAsChild(pParentGrp, GetFilename().getData())
|
|
|| !Core.Save(CrewGroup, nullptr)
|
|
|| !CrewGroup.Close() || !pParentGrp->Save(true))
|
|
{
|
|
GetScreen()->ShowMessage(LoadResStr("IDS_FAIL_MODIFY"), "", C4GUI::Ico_Error);
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool C4StartupPlrSelDlg::CrewListItem::SetName(const char *szNewName)
|
|
{
|
|
if (!fLoaded) return false;
|
|
// validate name
|
|
if (!szNewName || !*szNewName) return false;
|
|
if (SEqual(szNewName, Core.Name)) return true;
|
|
// generate filename from new name
|
|
char fn[_MAX_PATH+1];
|
|
SCopy(szNewName, fn, _MAX_PATH);
|
|
MakeFilenameFromTitle(fn);
|
|
if (!*fn) return false;
|
|
SAppend(".oci", fn, _MAX_PATH);
|
|
// check if a rename is due
|
|
if (!ItemIdentical(fn, GetFilename().getData()))
|
|
{
|
|
// check for duplicate filename
|
|
if (pParentGrp->FindEntry(fn))
|
|
{
|
|
StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_CLONKCOLLISION"), fn);
|
|
::pGUI->ShowMessageModal(sMsg.getData(), LoadResStr("IDS_FAIL_RENAME"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Error);
|
|
return false;
|
|
}
|
|
// OK; then rename
|
|
if (!pParentGrp->Rename(GetFilename().getData(), fn) || !pParentGrp->Save(true))
|
|
{
|
|
StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_RENAMEFILE"), GetFilename().getData(), fn);
|
|
::pGUI->ShowMessageModal(sMsg.getData(), LoadResStr("IDS_FAIL_RENAME"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Error);
|
|
return false;
|
|
}
|
|
const char *szConstFn = fn;
|
|
SetFilename(StdStrBuf(szConstFn));
|
|
}
|
|
// update clonk name and core
|
|
ListItem::SetName(szNewName);
|
|
SCopy(szNewName, Core.Name, C4MaxName);
|
|
RewriteCore();
|
|
return true;
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::CrewListItem::SetSelectionInfo(C4GUI::TextWindow *pSelectionInfo)
|
|
{
|
|
// write info text for player
|
|
pSelectionInfo->ClearText(false);
|
|
pSelectionInfo->AddTextLine(FormatString("%s %s", Core.sRankName.getData(), Core.Name).getData(), &C4Startup::Get()->Graphics.BookFontCapt, ClrPlayerItem, false, false);
|
|
StdStrBuf sPromo;
|
|
int32_t iNextRankExp; StdStrBuf sNextRankName;
|
|
if (Core.GetNextRankInfo(::DefaultRanks, &iNextRankExp, &sNextRankName))
|
|
sPromo.Format(LoadResStr("IDS_DESC_PROMO"),sNextRankName.getData(),(int) iNextRankExp);
|
|
else
|
|
sPromo.Copy(LoadResStr("IDS_DESC_NOPROMO"));
|
|
pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_DESC_OBJECT"),
|
|
Core.TypeName, Core.Experience, Core.Rounds, Core.DeathCount,
|
|
sPromo.getData(), TimeString(Core.TotalPlayingTime).getData(), DateString(Core.Birthday).getData()).getData(),
|
|
&C4Startup::Get()->Graphics.BookFont, ClrPlayerItem, false, false);
|
|
pSelectionInfo->UpdateHeight();
|
|
}
|
|
|
|
StdStrBuf C4StartupPlrSelDlg::CrewListItem::GetDelWarning()
|
|
{
|
|
StdStrBuf sWarning;
|
|
sWarning.Format(LoadResStr("IDS_MSG_DELETECLONK"),
|
|
Core.sRankName.getData(), Core.Name, GetFilename().getData());
|
|
int32_t iPlrTime = Core.TotalPlayingTime;
|
|
if (iPlrTime > 60*60*10)
|
|
sWarning.Append(static_cast<const StdStrBuf &>(FormatString(LoadResStr("IDS_MSG_DELETECLONK_PLAYTIME"), TimeString(iPlrTime).getData())));
|
|
return sWarning;
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::CrewListItem::CrewRename()
|
|
{
|
|
if (pPlrSelDlg->pRenameEdit) return;
|
|
// rename this entry
|
|
pPlrSelDlg->pRenameEdit = new C4GUI::CallbackRenameEdit<C4StartupPlrSelDlg::CrewListItem, RenameParams>(pNameLabel, this, RenameParams(), &C4StartupPlrSelDlg::CrewListItem::DoRenaming, &C4StartupPlrSelDlg::CrewListItem::AbortRenaming);
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::CrewListItem::AbortRenaming(RenameParams par)
|
|
{
|
|
// no renaming
|
|
pPlrSelDlg->pRenameEdit = nullptr;
|
|
}
|
|
|
|
C4GUI::RenameEdit::RenameResult C4StartupPlrSelDlg::CrewListItem::DoRenaming(RenameParams par, const char *szNewName)
|
|
{
|
|
// accept if name can be set; will fail if name is invalid or already given to another Crew member
|
|
if (!SetName(szNewName)) return C4GUI::RenameEdit::RR_Invalid;
|
|
pPlrSelDlg->pRenameEdit = nullptr;
|
|
// update in selection
|
|
C4StartupPlrSelDlg *pDlg = static_cast<C4StartupPlrSelDlg *>(GetDlg());
|
|
if (pDlg && pDlg->GetSelection() == this) pDlg->UpdateSelection();
|
|
return C4GUI::RenameEdit::RR_Accepted;
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------
|
|
// --- C4StartupPlrSelDlg
|
|
|
|
C4StartupPlrSelDlg::C4StartupPlrSelDlg() : C4StartupDlg("W")
|
|
{
|
|
// ctor
|
|
UpdateSize(); // for clientrect
|
|
|
|
// screen calculations
|
|
int iButtonHeight = C4GUI_ButtonHgt;
|
|
int iButtonXSpacing = (GetClientRect().Wdt > 700) ? GetClientRect().Wdt/58 : 2;
|
|
int iButtonCount = 6;
|
|
C4GUI::ComponentAligner caMain(GetClientRect(), 0,0, true);
|
|
C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(std::max(caMain.GetHeight()/15, iButtonHeight)),0,0);
|
|
rcBottomButtons = caButtonArea.GetCentered(caMain.GetWidth(), iButtonHeight);
|
|
iBottomButtonWidth = (caButtonArea.GetWidth() - iButtonXSpacing * (iButtonCount-1)) / iButtonCount;
|
|
C4Rect rcMain = caMain.GetAll();
|
|
C4Rect rcPlrList = C4Rect(rcMain.Wdt/8, rcMain.Hgt/8, rcMain.Wdt*5/16, rcMain.Hgt*6/8);
|
|
C4Rect rcInfoWindow = C4Rect(rcMain.Wdt*9/16, rcMain.Hgt/8, rcMain.Wdt*5/16, rcMain.Hgt*6/8);
|
|
|
|
AddElement(pPlrListBox = new C4GUI::ListBox(rcPlrList));
|
|
pPlrListBox->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERFILES"));
|
|
pPlrListBox->SetDecoration(true, &C4Startup::Get()->Graphics.sfctBookScroll, true);
|
|
pPlrListBox->UpdateElementPositions();
|
|
pPlrListBox->SetSelectionChangeCallbackFn(new C4GUI::CallbackHandler<C4StartupPlrSelDlg>(this, &C4StartupPlrSelDlg::OnSelChange));
|
|
pPlrListBox->SetSelectionDblClickFn(new C4GUI::CallbackHandler<C4StartupPlrSelDlg>(this, &C4StartupPlrSelDlg::OnSelDblClick));
|
|
AddElement(pSelectionInfo = new C4GUI::TextWindow(rcInfoWindow,0,0,0,100,4096," ",false,nullptr,0,true));
|
|
pSelectionInfo->SetDecoration(true, true, &C4Startup::Get()->Graphics.sfctBookScroll, true);
|
|
pSelectionInfo->UpdateHeight();
|
|
|
|
// bottom line buttons - positioning done in UpdateBottomButtons by UpdatePlayerList
|
|
C4Rect rcDefault(0,0,10,10);
|
|
AddElement(btnBack = new C4GUI::CallbackButton<C4StartupPlrSelDlg>(LoadResStr("IDS_BTN_BACK"), rcDefault, &C4StartupPlrSelDlg::OnBackBtn));
|
|
AddElement(btnNew = new C4GUI::CallbackButton<C4StartupPlrSelDlg>(LoadResStr("IDS_BTN_NEW"), rcDefault, &C4StartupPlrSelDlg::OnNewBtn));
|
|
btnNew->SetToolTip(LoadResStr("IDS_DLGTIP_NEWPLAYER"));
|
|
AddElement(btnActivatePlr = new C4GUI::CallbackButton<C4StartupPlrSelDlg>(nullptr, rcDefault, &C4StartupPlrSelDlg::OnActivateBtn));
|
|
AddElement(btnDelete = new C4GUI::CallbackButton<C4StartupPlrSelDlg>(LoadResStr("IDS_BTN_DELETE"), rcDefault, &C4StartupPlrSelDlg::OnDelBtn));
|
|
AddElement(btnProperties = new C4GUI::CallbackButton<C4StartupPlrSelDlg>(nullptr, rcDefault, &C4StartupPlrSelDlg::OnPropertyBtn));
|
|
AddElement(btnCrew = new C4GUI::CallbackButton<C4StartupPlrSelDlg>(LoadResStr("IDS_SELECT_CREW"), rcDefault, &C4StartupPlrSelDlg::OnCrewBtn));
|
|
btnCrew->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERCREW"));
|
|
|
|
// refill listboxes
|
|
UpdatePlayerList();
|
|
// Just safety incase listbox was empty, in which case no selection change callback will have been done:
|
|
// Update current listbox selection to that of no selected item
|
|
if (!pPlrListBox->GetFirst()) UpdateSelection();
|
|
|
|
// initial focus on player list
|
|
SetFocus(pPlrListBox, false);
|
|
|
|
// key bindings
|
|
C4CustomKey::CodeList keys;
|
|
keys.emplace_back(K_BACK);
|
|
keys.emplace_back(K_LEFT);
|
|
keys.emplace_back(K_ESCAPE);
|
|
if (Config.Controls.GamepadGuiControl)
|
|
{
|
|
ControllerKeys::Cancel(keys);
|
|
}
|
|
pKeyBack = new C4KeyBinding(keys, "StartupPlrSelBack", KEYSCOPE_Gui,
|
|
new C4GUI::DlgKeyCB<C4StartupPlrSelDlg>(*this, &C4StartupPlrSelDlg::KeyBack), C4CustomKey::PRIO_CtrlOverride);
|
|
pKeyProperties = new C4KeyBinding(C4KeyCodeEx(K_F2), "StartupPlrSelProp", KEYSCOPE_Gui,
|
|
new C4GUI::DlgKeyCB<C4StartupPlrSelDlg>(*this, &C4StartupPlrSelDlg::KeyProperties), C4CustomKey::PRIO_CtrlOverride);
|
|
pKeyCrew = new C4KeyBinding(C4KeyCodeEx(K_RIGHT), "StartupPlrSelCrew", KEYSCOPE_Gui,
|
|
new C4GUI::ControlKeyDlgCB<C4StartupPlrSelDlg>(pPlrListBox, *this, &C4StartupPlrSelDlg::KeyCrew), C4CustomKey::PRIO_CtrlOverride);
|
|
pKeyDelete = new C4KeyBinding(C4KeyCodeEx(K_DELETE), "StartupPlrSelDelete", KEYSCOPE_Gui,
|
|
new C4GUI::DlgKeyCB<C4StartupPlrSelDlg>(*this, &C4StartupPlrSelDlg::KeyDelete), C4CustomKey::PRIO_CtrlOverride);
|
|
pKeyNew = new C4KeyBinding(C4KeyCodeEx(K_INSERT), "StartupPlrSelNew", KEYSCOPE_Gui,
|
|
new C4GUI::DlgKeyCB<C4StartupPlrSelDlg>(*this, &C4StartupPlrSelDlg::KeyNew), C4CustomKey::PRIO_CtrlOverride);
|
|
}
|
|
|
|
C4StartupPlrSelDlg::~C4StartupPlrSelDlg()
|
|
{
|
|
delete pKeyDelete;
|
|
delete pKeyCrew;
|
|
delete pKeyProperties;
|
|
delete pKeyBack;
|
|
delete pKeyNew;
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::AbortRenaming()
|
|
{
|
|
if (pRenameEdit) pRenameEdit->Abort();
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::DrawElement(C4TargetFacet &cgo)
|
|
{
|
|
// draw background
|
|
typedef C4GUI::FullscreenDialog Base;
|
|
Base::DrawElement(cgo);
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::UpdateBottomButtons()
|
|
{
|
|
// bottom line buttons depend on list mode
|
|
C4GUI::ComponentAligner caBottomButtons(rcBottomButtons,0,0);
|
|
switch (eMode)
|
|
{
|
|
case PSDM_Player:
|
|
{
|
|
// update some buttons for player mode
|
|
btnProperties->SetText(LoadResStr("IDS_BTN_PROPERTIES"));
|
|
btnProperties->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERPROPERTIES"));
|
|
btnNew->SetVisibility(true);
|
|
btnCrew->SetVisibility(true);
|
|
btnDelete->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERDELETE"));
|
|
btnBack->SetToolTip(LoadResStr("IDS_DLGTIP_BACKMAIN"));
|
|
btnBack ->SetBounds(caBottomButtons.GetGridCell(0,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
|
|
btnNew ->SetBounds(caBottomButtons.GetGridCell(1,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
|
|
btnActivatePlr->SetBounds(caBottomButtons.GetGridCell(2,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
|
|
btnDelete ->SetBounds(caBottomButtons.GetGridCell(3,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
|
|
btnProperties ->SetBounds(caBottomButtons.GetGridCell(4,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
|
|
btnCrew ->SetBounds(caBottomButtons.GetGridCell(5,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
|
|
}
|
|
break;
|
|
|
|
case PSDM_Crew:
|
|
{
|
|
// update some buttons for player mode
|
|
btnProperties->SetText(LoadResStr("IDS_BTN_RENAME"));
|
|
btnProperties->SetToolTip(LoadResStr("IDS_DESC_CREWRENAME"));
|
|
btnNew->SetVisibility(false);
|
|
btnCrew->SetVisibility(false);
|
|
btnDelete->SetToolTip(LoadResStr("IDS_MSG_DELETECLONK_DESC"));
|
|
btnBack->SetToolTip(LoadResStr("IDS_MSG_BACKTOPLAYERDLG"));
|
|
btnBack ->SetBounds(caBottomButtons.GetGridCell(0,4,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
|
|
btnActivatePlr->SetBounds(caBottomButtons.GetGridCell(1,4,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
|
|
btnDelete ->SetBounds(caBottomButtons.GetGridCell(2,4,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
|
|
btnProperties ->SetBounds(caBottomButtons.GetGridCell(3,4,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
|
|
}
|
|
break;
|
|
};
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::UpdatePlayerList()
|
|
{
|
|
// something has changed!
|
|
AbortRenaming();
|
|
// refill pPlrListBox with players in player folder or crew
|
|
// clear old items
|
|
C4GUI::Element *pEl;
|
|
while ((pEl = pPlrListBox->GetFirst())) delete pEl;
|
|
// update command buttons
|
|
UpdateBottomButtons();
|
|
// create new
|
|
switch (eMode)
|
|
{
|
|
case PSDM_Player:
|
|
{
|
|
SetTitle(LoadResStrNoAmp("IDS_DLG_PLAYERSELECTION"));
|
|
// player mode: insert all players
|
|
const char *szFn;
|
|
StdStrBuf sSearchPath(Config.General.UserDataPath);
|
|
PlayerListItem *pFirstActivatedPlrItem=nullptr, *pFirstDeactivatedPlrItem=nullptr, *pPlrItem=nullptr;
|
|
for (DirectoryIterator i(sSearchPath.getData()); (szFn=*i); i++)
|
|
{
|
|
szFn = Config.AtRelativePath(szFn);
|
|
if (*GetFilename(szFn) == '.') continue; // ignore ".", ".." and private files (".*")
|
|
if (!WildcardMatch(C4CFN_PlayerFiles, GetFilename(szFn))) continue;
|
|
bool fIsParticipating = !!SIsModule(Config.General.Participants, szFn, nullptr, false);
|
|
pPlrItem = new PlayerListItem(this, pPlrListBox, nullptr, fIsParticipating);
|
|
try
|
|
{
|
|
pPlrItem->Load(StdStrBuf(szFn));
|
|
}
|
|
catch (ListItem::LoadError & e)
|
|
{
|
|
// invalid player: ignore but log error message
|
|
DebugLog(e.getData());
|
|
delete pPlrItem;
|
|
continue;
|
|
}
|
|
if (fIsParticipating)
|
|
{
|
|
if (!pFirstActivatedPlrItem) pFirstActivatedPlrItem = pPlrItem;
|
|
}
|
|
else if (!pFirstDeactivatedPlrItem) pFirstDeactivatedPlrItem = pPlrItem;
|
|
}
|
|
// select first element; prefer activated player
|
|
if (!(pPlrItem = pFirstActivatedPlrItem))
|
|
pPlrItem = pFirstDeactivatedPlrItem;
|
|
if (pPlrItem)
|
|
pPlrListBox->SelectEntry(pPlrItem, false);
|
|
// re-validate Game.PlayerFilename
|
|
UpdateActivatedPlayers();
|
|
break;
|
|
}
|
|
|
|
case PSDM_Crew:
|
|
{
|
|
SetTitle(FormatString("%s %s", LoadResStrNoAmp("IDS_CTL_CREW"), CurrPlayer.Core.PrefName).getData());
|
|
// crew mode: Insert complete crew of player (2do: sort)
|
|
bool fSucc; char szFn[_MAX_PATH+1];
|
|
for (fSucc=CurrPlayer.Grp.FindEntry(C4CFN_ObjectInfoFiles, szFn); fSucc; fSucc=CurrPlayer.Grp.FindNextEntry(C4CFN_ObjectInfoFiles, szFn, nullptr, true))
|
|
{
|
|
CrewListItem *pCrewItem = new CrewListItem(this, pPlrListBox, CurrPlayer.Core.PrefColorDw);
|
|
try
|
|
{
|
|
pCrewItem->Load(CurrPlayer.Grp, StdStrBuf(szFn));
|
|
}
|
|
catch (ListItem::LoadError & e)
|
|
{
|
|
// invalid player: ignore but log error message
|
|
DebugLog(e.getData());
|
|
delete pCrewItem;
|
|
continue;
|
|
}
|
|
}
|
|
// resort crew by type and experience
|
|
ResortCrew();
|
|
pPlrListBox->SelectFirstEntry(false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
C4StartupPlrSelDlg::ListItem *C4StartupPlrSelDlg::GetSelection()
|
|
{
|
|
// get selected item: There's only instances of PlrListItem in this list
|
|
return static_cast<ListItem *>(pPlrListBox->GetSelectedItem());
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::SetSelection(ListItem *pNewItem)
|
|
{
|
|
// update selection in listbox
|
|
pPlrListBox->SelectEntry(pNewItem, false);
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::UpdateSelection()
|
|
{
|
|
// something has changed!
|
|
AbortRenaming();
|
|
// get currently selected player
|
|
ListItem *pSel = GetSelection();
|
|
// button text 'activate' if current player is deactivated; 'deactivate' otherwise
|
|
if (pSel && pSel->IsActivated())
|
|
{
|
|
btnActivatePlr->SetText(LoadResStr("IDS_BTN_DEACTIVATE"));
|
|
btnActivatePlr->SetToolTip(FormatString(LoadResStr("IDS_MSG_NOPARTICIPATE_DESC"), pSel->GetName()).getData());
|
|
}
|
|
else
|
|
{
|
|
btnActivatePlr->SetText(LoadResStr("IDS_BTN_ACTIVATE"));
|
|
btnActivatePlr->SetToolTip(FormatString(LoadResStr("IDS_MSG_PARTICIPATE_DESC"), pSel ? pSel->GetName() : "").getData());
|
|
}
|
|
// no item selected?
|
|
if (!pSel)
|
|
{
|
|
pSelectionInfo->ClearText(true);
|
|
// 2do: disable buttons
|
|
return;
|
|
}
|
|
// info text for selection
|
|
pSel->SetSelectionInfo(pSelectionInfo);
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::OnItemCheckChange(C4GUI::Element *pCheckBox)
|
|
{
|
|
switch (eMode)
|
|
{
|
|
case PSDM_Player:
|
|
// Deselect all other players
|
|
for (ListItem* pEl = static_cast<ListItem*>(pPlrListBox->GetFirst()); pEl != nullptr; pEl = pEl->GetNext())
|
|
if (pCheckBox && pEl != pCheckBox->GetParent())
|
|
pEl->SetActivated(false);
|
|
// update Config.General.Participants
|
|
UpdateActivatedPlayers();
|
|
break;
|
|
case PSDM_Crew:
|
|
// update affected crew item
|
|
if (pCheckBox) static_cast<CrewListItem *>(pCheckBox->GetParent())->UpdateClonkEnabled();
|
|
break;
|
|
}
|
|
// update player selection text
|
|
UpdateSelection();
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::UpdateActivatedPlayers()
|
|
{
|
|
assert(eMode == PSDM_Player);
|
|
// refill Config.General.Participants-list
|
|
*Config.General.Participants = '\0';
|
|
for (ListItem *pPlrItem = static_cast<ListItem *>(pPlrListBox->GetFirst()); pPlrItem; pPlrItem = pPlrItem->GetNext())
|
|
if (pPlrItem->IsActivated())
|
|
{
|
|
const char *szAddFilename = pPlrItem->GetFilename().getData();
|
|
if (std::strlen(Config.General.Participants) + 1 + std::strlen(szAddFilename) < sizeof(Config.General.Participants))
|
|
SAddModule(Config.General.Participants, szAddFilename);
|
|
else
|
|
{
|
|
pPlrItem->SetActivated(false);
|
|
GetScreen()->ShowMessage(FormatString(LoadResStr("IDS_ERR_PLAYERSTOOLONG"), pPlrItem->GetName()).getData(), LoadResStr("IDS_ERR_TITLE"), C4GUI::Ico_Error);
|
|
}
|
|
}
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::OnActivateBtn(C4GUI::Control *btn)
|
|
{
|
|
// toggle activation state of current item
|
|
// get currently selected player
|
|
ListItem *pSel = GetSelection();
|
|
if (!pSel) return;
|
|
pSel->SetActivated(!pSel->IsActivated());
|
|
// update stuff
|
|
OnItemCheckChange(pSel->GetCheckBox());
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::DoBack()
|
|
{
|
|
switch (eMode)
|
|
{
|
|
case PSDM_Player:
|
|
{
|
|
// back 2 main
|
|
C4Startup::Get()->SwitchDialog(C4Startup::SDID_Back);
|
|
break;
|
|
}
|
|
|
|
case PSDM_Crew:
|
|
// back 2 player list
|
|
SetPlayerMode();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::OnNewBtn(C4GUI::Control *btn)
|
|
{
|
|
if (eMode != PSDM_Player) return;
|
|
C4GUI::Dialog *pDlg;
|
|
GetScreen()->ShowRemoveDlg(pDlg=new C4StartupPlrPropertiesDlg(nullptr, this));
|
|
pDlg->SetPos(std::min<int32_t>(GetBounds().Wdt/10, GetBounds().Wdt - pDlg->GetBounds().Wdt), std::min<int32_t>(GetBounds().Hgt/4, GetBounds().Hgt - pDlg->GetBounds().Hgt));
|
|
}
|
|
|
|
bool C4StartupPlrSelDlg::CheckPlayerName(const StdStrBuf &Playername, StdStrBuf &Filename, const StdStrBuf *pPrevFilename, bool fWarnEmpty)
|
|
{
|
|
// must not be empty
|
|
if (!Playername.getLength())
|
|
{
|
|
if (fWarnEmpty) C4GUI::Screen::GetScreenS()->ShowMessage(LoadResStr("IDS_ERR_PLRNAME_EMPTY"), "", C4GUI::Ico_Error);
|
|
return false;
|
|
}
|
|
// generate valid filename
|
|
Filename.Copy(Playername);
|
|
// Slashes in Filenames are no good
|
|
SReplaceChar(Filename.getMData(), '\\', '_');
|
|
SReplaceChar(Filename.getMData(), '/', '_');
|
|
SReplaceChar(Filename.getMData(), ':', '_');
|
|
SReplaceChar(Filename.getMData(), '*', '_');
|
|
SReplaceChar(Filename.getMData(), '?', '_');
|
|
SReplaceChar(Filename.getMData(), '"', '_');
|
|
SReplaceChar(Filename.getMData(), '<', '_');
|
|
SReplaceChar(Filename.getMData(), '>', '_');
|
|
SReplaceChar(Filename.getMData(), '|', '_');
|
|
if (*Filename.getData() == '.') *Filename.getMData() = '_';
|
|
Filename.Append(".ocp");
|
|
StdStrBuf Path(Config.General.UserDataPath); // start at local path
|
|
|
|
Path.Append(Filename);
|
|
// validity check: Must not exist yet if renamed
|
|
if (!pPrevFilename || !ItemIdentical(Path.getData(), Config.AtUserDataPath(pPrevFilename->getData()))) if (ItemExists(Path.getData()))
|
|
{
|
|
C4GUI::Screen::GetScreenS()->ShowMessage(FormatString(LoadResStr("IDS_ERR_PLRNAME_TAKEN"),
|
|
Playername.getData()).getData(), "", C4GUI::Ico_Error);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::OnCrewBtn(C4GUI::Control *btn)
|
|
{
|
|
// display crew for activated player
|
|
if (eMode != PSDM_Player) return;
|
|
PlayerListItem *pSel = static_cast<PlayerListItem *>(GetSelection());
|
|
if (!pSel) return;
|
|
SetCrewMode(pSel);
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::SetPlayerMode()
|
|
{
|
|
// change view to listing players
|
|
C4GUI::GUISound("UI::Close");
|
|
StdStrBuf LastPlrFilename;
|
|
LastPlrFilename.Copy(static_cast<const StdStrBuf &>(CurrPlayer.Grp.GetFullName()));
|
|
CurrPlayer.Grp.Close();
|
|
eMode = PSDM_Player;
|
|
UpdatePlayerList();
|
|
SelectItem(LastPlrFilename, false);
|
|
UpdateSelection();
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::SetCrewMode(PlayerListItem *pSel)
|
|
{
|
|
// change view to listing crew of a player
|
|
CurrPlayer.Core = pSel->GetCore();
|
|
|
|
StdStrBuf Path(Config.General.UserDataPath); // start at local path
|
|
// Path.Append(Config.General.PlayerPath);
|
|
Path.Append(pSel->GetFilename());
|
|
|
|
if (!CurrPlayer.Grp.Open(Path.getData())) return;
|
|
if (!CurrPlayer.Grp.FindEntry(C4CFN_ObjectInfoFiles))
|
|
{
|
|
StdCopyStrBuf strCrew(FormatString("%s %s", LoadResStrNoAmp("IDS_CTL_CREW"), CurrPlayer.Core.PrefName));
|
|
// player has no crew!
|
|
GetScreen()->ShowMessage(FormatString(LoadResStr("IDS_ERR_PLRNOCREW"),
|
|
CurrPlayer.Core.PrefName).getData(),
|
|
strCrew.getData(), C4GUI::Ico_Player);
|
|
return;
|
|
}
|
|
C4GUI::GUISound("UI::Open");
|
|
eMode = PSDM_Crew;
|
|
UpdatePlayerList();
|
|
UpdateSelection();
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::OnDelBtn(C4GUI::Control *btn)
|
|
{
|
|
// something has changed!
|
|
AbortRenaming();
|
|
// delete selected player
|
|
ListItem *pSel = GetSelection();
|
|
if (!pSel) return;
|
|
StdStrBuf sWarning; sWarning.Take(pSel->GetDelWarning());
|
|
GetScreen()->ShowRemoveDlg(new C4GUI::ConfirmationDialog(sWarning.getData(), LoadResStr("IDS_BTN_DELETE"),
|
|
new C4GUI::CallbackHandlerExPar<C4StartupPlrSelDlg, ListItem *>(this, &C4StartupPlrSelDlg::OnDelBtnConfirm, pSel), C4GUI::MessageDialog::btnYesNo));
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::OnDelBtnConfirm(ListItem *pSel)
|
|
{
|
|
StdStrBuf Path;
|
|
|
|
switch (eMode)
|
|
{
|
|
case PSDM_Player:
|
|
Path.Append(Config.General.UserDataPath); // start at local path
|
|
Path.Append(pSel->GetFilename());
|
|
if (!C4Group_DeleteItem(Path.getData()))
|
|
{
|
|
StdStrBuf sMsg; sMsg.Copy(LoadResStr("IDS_FAIL_DELETE"));
|
|
GetScreen()->ShowMessage(sMsg.getData(), LoadResStr("IDS_DLG_CLEAR"), C4GUI::Ico_Error);
|
|
}
|
|
break;
|
|
|
|
case PSDM_Crew:
|
|
if (!CurrPlayer.Grp.DeleteEntry(pSel->GetFilename().getData()))
|
|
{
|
|
StdStrBuf sMsg; sMsg.Copy(LoadResStr("IDS_FAIL_DELETE"));
|
|
GetScreen()->ShowMessage(sMsg.getData(), LoadResStr("IDS_DLG_CLEAR"), C4GUI::Ico_Error);
|
|
}
|
|
break;
|
|
}
|
|
// update buttons 'n stuff
|
|
UpdatePlayerList();
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::SelectItem(const StdStrBuf &Filename, bool fActivate)
|
|
{
|
|
// find item
|
|
for (ListItem *pPlrItem = static_cast<ListItem *>(pPlrListBox->GetFirst()); pPlrItem; pPlrItem = pPlrItem->GetNext())
|
|
if (ItemIdentical(pPlrItem->GetFilename().getData(), Filename.getData()))
|
|
{
|
|
// select it
|
|
pPlrListBox->SelectEntry(pPlrItem, false);
|
|
// activate it
|
|
if (fActivate)
|
|
{
|
|
pPlrItem->SetActivated(true);
|
|
// player activation updates
|
|
OnItemCheckChange(pPlrItem->GetCheckBox());
|
|
}
|
|
// max one
|
|
return;
|
|
}
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::OnPropertyBtn(C4GUI::Control *btn)
|
|
{
|
|
// something has changed!
|
|
AbortRenaming();
|
|
switch (eMode)
|
|
{
|
|
case PSDM_Player:
|
|
{
|
|
// show property dialog for selected player
|
|
PlayerListItem *pSel = static_cast<PlayerListItem *>(GetSelection());
|
|
if (!pSel) return;
|
|
C4GUI::Dialog *pDlg;
|
|
GetScreen()->ShowRemoveDlg(pDlg=new C4StartupPlrPropertiesDlg(pSel, this));
|
|
pDlg->SetPos(std::min<int32_t>(GetBounds().Wdt/10, GetBounds().Wdt - pDlg->GetBounds().Wdt),
|
|
(GetBounds().Hgt - pDlg->GetBounds().Hgt) / 2);
|
|
}
|
|
break;
|
|
|
|
case PSDM_Crew:
|
|
// rename crew
|
|
CrewListItem *pSel = static_cast<CrewListItem *>(GetSelection());
|
|
if (!pSel) return;
|
|
pSel->CrewRename();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* -- Crew sorting -- */
|
|
|
|
struct C4StartupPlrSelDlg_CrewSortDataEntry
|
|
{
|
|
int32_t iMaxExp;
|
|
C4ID idType;
|
|
|
|
C4StartupPlrSelDlg_CrewSortDataEntry(int32_t iMaxExp, C4ID idType) : iMaxExp(iMaxExp), idType(idType) {}
|
|
};
|
|
|
|
class C4StartupPlrSelDlg_CrewSortDataMatchType
|
|
{
|
|
C4ID idType;
|
|
|
|
public:
|
|
C4StartupPlrSelDlg_CrewSortDataMatchType(C4ID idType) : idType(idType) {}
|
|
bool operator()(C4StartupPlrSelDlg_CrewSortDataEntry Check) { return Check.idType == idType; }
|
|
};
|
|
|
|
typedef std::vector<C4StartupPlrSelDlg_CrewSortDataEntry> C4StartupPlrSelDlg_CrewSortData;
|
|
|
|
int32_t C4StartupPlrSelDlg::CrewSortFunc(const C4GUI::Element *pEl1, const C4GUI::Element *pEl2, void *par)
|
|
{
|
|
const auto *pItem1 = static_cast<const CrewListItem *>(pEl1);
|
|
const auto *pItem2 = static_cast<const CrewListItem *>(pEl2);
|
|
C4StartupPlrSelDlg_CrewSortData &rSortData = *static_cast<C4StartupPlrSelDlg_CrewSortData *>(par);
|
|
C4StartupPlrSelDlg_CrewSortData::iterator i = std::find_if(rSortData.begin(), rSortData.end(), C4StartupPlrSelDlg_CrewSortDataMatchType(pItem1->GetCore().id)),
|
|
j = std::find_if(rSortData.begin(), rSortData.end(), C4StartupPlrSelDlg_CrewSortDataMatchType(pItem2->GetCore().id));
|
|
// primary sort: By Clonk type, where high exp Clonk types are sorted atop low exp Clonk types
|
|
if (i != j)
|
|
{
|
|
if (i == rSortData.end()) return -1; else if (j == rSortData.end()) return +1; // can't really happen
|
|
return (*i).iMaxExp - (*j).iMaxExp;
|
|
}
|
|
// secondary: By experience
|
|
return pItem1->GetCore().Experience - pItem2->GetCore().Experience;
|
|
}
|
|
|
|
void C4StartupPlrSelDlg::ResortCrew()
|
|
{
|
|
assert(eMode == PSDM_Crew);
|
|
// create a list of Clonk types and their respective maximum experience
|
|
C4StartupPlrSelDlg_CrewSortData SortData;
|
|
for (CrewListItem *pCrewItem = static_cast<CrewListItem *>(pPlrListBox->GetFirst()); pCrewItem; pCrewItem = pCrewItem->GetNext())
|
|
{
|
|
C4StartupPlrSelDlg_CrewSortData::iterator i = std::find_if(SortData.begin(), SortData.end(), C4StartupPlrSelDlg_CrewSortDataMatchType(pCrewItem->GetCore().id));
|
|
if (i == SortData.end())
|
|
SortData.emplace_back(pCrewItem->GetCore().Experience, pCrewItem->GetCore().id);
|
|
else
|
|
(*i).iMaxExp = std::max<int32_t>((*i).iMaxExp, pCrewItem->GetCore().Experience);
|
|
}
|
|
pPlrListBox->SortElements(&CrewSortFunc, &SortData);
|
|
}
|
|
|
|
// ------------------------------------------------
|
|
// --- Player color HSV chooser
|
|
class C4StartupPlrColorPickerDlg : public C4GUI::Dialog
|
|
{
|
|
public:
|
|
C4StartupPlrColorPickerDlg(C4PlayerInfoCore *plrcore);
|
|
|
|
protected:
|
|
// Event handler
|
|
void OnClosed(bool commit) override;
|
|
|
|
private:
|
|
class Picker : public C4GUI::Control
|
|
{
|
|
public:
|
|
Picker(const C4Rect &bounds);
|
|
|
|
// Set/retrieve current color value
|
|
void SetColor(uint32_t rgb);
|
|
uint32_t GetColor() const;
|
|
|
|
protected:
|
|
// Event handlers, overridden from C4GUI::Control
|
|
void DrawElement(C4TargetFacet &cgo) override;
|
|
void MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override;
|
|
void DoDragging(C4GUI::CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam) override;
|
|
|
|
private:
|
|
static const unsigned int HSPickerCursorSize = 5;
|
|
static const unsigned int VPickerCursorSize = 7;
|
|
C4FacetSurface hsFacet, vFacet; // chooser backgrounds
|
|
C4Rect hsPickerRect, vPickerRect;
|
|
C4GUI::Picture *flagPreview, *crewPreview;
|
|
uint32_t hsv; // current color
|
|
enum {
|
|
PS_Idle, // user isn't dragging anything
|
|
PS_IdleDragging, // user started the drag on empty space
|
|
PS_DragHS, // user started the drag over the HS picker
|
|
PS_DragV // user started the drag over the V picker
|
|
} state;
|
|
|
|
bool HandleMouseDown(int32_t x, int32_t y);
|
|
void UpdateVFacet(uint32_t h, uint32_t s);
|
|
void UpdatePreview();
|
|
};
|
|
|
|
C4PlayerInfoCore *plrcore;
|
|
Picker *picker;
|
|
|
|
static uint32_t HSV2RGB(uint32_t hsv)
|
|
{
|
|
float h = GetRedValue(hsv) / 255.f * 6.f;
|
|
float s = GetGreenValue(hsv) / 255.f;
|
|
float v = GetBlueValue(hsv) / 255.f;
|
|
|
|
float chroma = s * v;
|
|
float x = chroma * (1.f - std::abs(std::fmod(h, 2.f) - 1.f));
|
|
|
|
float r = 0;
|
|
float g = 0;
|
|
float b = 0;
|
|
|
|
switch (static_cast<int>(h))
|
|
{
|
|
case 0: case 6:
|
|
r = chroma; g = x; break;
|
|
case 1:
|
|
r = x; g = chroma; break;
|
|
case 2:
|
|
g = chroma; b = x; break;
|
|
case 3:
|
|
g = x; b = chroma; break;
|
|
case 4:
|
|
b = chroma; r = x; break;
|
|
case 5:
|
|
b = x; r = chroma; break;
|
|
}
|
|
r += v-chroma;
|
|
g += v-chroma;
|
|
b += v-chroma;
|
|
|
|
return RGBA(r * 255.f, g * 255.f, b * 255.f, hsv >> 24);
|
|
}
|
|
static uint32_t RGB2HSV(uint32_t rgb)
|
|
{
|
|
float r = GetRedValue(rgb) / 255.f;
|
|
float g = GetGreenValue(rgb) / 255.f;
|
|
float b = GetBlueValue(rgb) / 255.f;
|
|
|
|
float min = std::min(r, std::min(g, b));
|
|
float max = std::max(r, std::max(g, b));
|
|
|
|
float chroma = max - min;
|
|
|
|
float hue = 0;
|
|
if (r == max)
|
|
hue = std::fmod((g-b) / chroma, 6.f);
|
|
else if (g == max)
|
|
hue = (b-r) / chroma + 2.f;
|
|
else
|
|
hue = (r-g) / chroma + 4.f;
|
|
|
|
float h = hue / 6.f;
|
|
float s = max == 0 ? 0.f : chroma / max;
|
|
float v = max;
|
|
|
|
return RGBA(h * 255.f, s * 255.f, v * 255.f, rgb >> 24);
|
|
}
|
|
};
|
|
|
|
C4StartupPlrColorPickerDlg::C4StartupPlrColorPickerDlg(C4PlayerInfoCore *plrcore)
|
|
: Dialog(400, 296 + C4GUI_ButtonAreaHgt, LoadResStr("IDS_DLG_PLAYERCOLORSELECTION"), false), plrcore(plrcore)
|
|
{
|
|
C4GUI::ComponentAligner caMain(GetClientRect(), 0, 1, true);
|
|
|
|
picker = new Picker(caMain.GetFromTop(280));
|
|
picker->SetColor(plrcore->PrefColorDw);
|
|
AddElement(picker);
|
|
|
|
// buttons
|
|
C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(C4GUI_ButtonAreaHgt), 0, 0);
|
|
caButtonArea = C4GUI::ComponentAligner(caButtonArea.GetCentered(2*128 + 4*8, C4GUI_ButtonAreaHgt), 8, 8);
|
|
C4GUI::Button *cancelButton = new C4GUI::CancelButton(caButtonArea.GetFromRight(128));
|
|
C4GUI::Button *okButton = new C4GUI::OKButton(caButtonArea.GetFromRight(128));
|
|
AddElement(okButton);
|
|
AddElement(cancelButton);
|
|
}
|
|
|
|
void C4StartupPlrColorPickerDlg::OnClosed(bool commit)
|
|
{
|
|
// Write chosen color back to player core
|
|
if (commit)
|
|
plrcore->PrefColorDw = picker->GetColor();
|
|
}
|
|
|
|
C4StartupPlrColorPickerDlg::Picker::Picker(const C4Rect &bounds)
|
|
: Control(bounds), state(PS_Idle)
|
|
{
|
|
C4GUI::ComponentAligner caMain(bounds, 8, 8, true);
|
|
caMain.ExpandBottom(-(caMain.GetInnerHeight() - 256));
|
|
hsPickerRect = caMain.GetFromLeft(256);
|
|
vPickerRect = caMain.GetFromLeft(16 + VPickerCursorSize);
|
|
vPickerRect.Hgt = 256 - PlayerColorValueLowBound;
|
|
|
|
C4Facet &flagPreviewPic = ::GraphicsResource.fctFlagClr;
|
|
int preview_width = std::min<int>(flagPreviewPic.Wdt, caMain.GetInnerWidth());
|
|
flagPreview = new C4GUI::Picture(caMain.GetFromTop(flagPreviewPic.GetHeightByWidth(preview_width), preview_width), true);
|
|
flagPreview->SetFacet(flagPreviewPic);
|
|
AddElement(flagPreview);
|
|
|
|
C4Facet &crewPreviewPic = ::GraphicsResource.fctCrewClr;
|
|
preview_width = std::min<int>(crewPreviewPic.Wdt, caMain.GetInnerWidth());
|
|
crewPreview = new C4GUI::Picture(caMain.GetFromTop(crewPreviewPic.GetHeightByWidth(preview_width), preview_width), true);
|
|
crewPreview->SetFacet(crewPreviewPic);
|
|
AddElement(crewPreview);
|
|
|
|
// Pre-draw the H+S chooser background, it never changes anyway
|
|
hsFacet.Create(hsPickerRect.Wdt, hsPickerRect.Hgt);
|
|
hsFacet.Surface->Lock();
|
|
for (int y = 0; y < hsFacet.Hgt; ++y)
|
|
for (int x = 0; x < hsFacet.Wdt; ++x)
|
|
hsFacet.Surface->SetPixDw(x, y, HSV2RGB(C4RGB(x, 255-y, 255)));
|
|
hsFacet.Surface->Unlock();
|
|
|
|
vFacet.Create(vPickerRect.Wdt - VPickerCursorSize, vPickerRect.Hgt);
|
|
UpdateVFacet(255, 255);
|
|
}
|
|
|
|
void C4StartupPlrColorPickerDlg::Picker::UpdateVFacet(uint32_t h, uint32_t s)
|
|
{
|
|
// Draw the V chooser background according to current H+S values
|
|
vFacet.Surface->Lock();
|
|
for (int y = 0; y < vPickerRect.Hgt; ++y)
|
|
for (int x = 0; x < vFacet.Wdt; ++x)
|
|
vFacet.Surface->SetPixDw(x, y, HSV2RGB(C4RGB(h, s, 255-y)));
|
|
vFacet.Surface->Unlock();
|
|
}
|
|
|
|
void C4StartupPlrColorPickerDlg::Picker::UpdatePreview()
|
|
{
|
|
flagPreview->SetDrawColor(HSV2RGB(hsv));
|
|
crewPreview->SetDrawColor(HSV2RGB(hsv));
|
|
}
|
|
|
|
void C4StartupPlrColorPickerDlg::Picker::SetColor(uint32_t rgb)
|
|
{
|
|
hsv = RGB2HSV(rgb);
|
|
UpdateVFacet(GetRedValue(hsv), GetGreenValue(hsv));
|
|
UpdatePreview();
|
|
}
|
|
|
|
uint32_t C4StartupPlrColorPickerDlg::Picker::GetColor() const
|
|
{
|
|
return HSV2RGB(hsv);
|
|
}
|
|
|
|
void C4StartupPlrColorPickerDlg::Picker::DrawElement(C4TargetFacet &cgo)
|
|
{
|
|
// H+S chooser background
|
|
C4Facet cgoPicker(cgo.Surface, cgo.TargetX + hsPickerRect.x, cgo.TargetY + hsPickerRect.y, hsPickerRect.Wdt, hsPickerRect.Hgt);
|
|
hsFacet.Draw(cgoPicker.Surface, cgoPicker.X, cgoPicker.Y);
|
|
// H+S cursor
|
|
cgoPicker.Wdt = cgoPicker.Hgt = HSPickerCursorSize;
|
|
cgoPicker.X += GetRedValue(hsv) - cgoPicker.Wdt / 2;
|
|
cgoPicker.Y += 255 - GetGreenValue(hsv) - cgoPicker.Hgt / 2;
|
|
pDraw->DrawLineDw(cgoPicker.Surface, cgoPicker.X, cgoPicker.Y, cgoPicker.X + cgoPicker.Wdt, cgoPicker.Y + cgoPicker.Hgt, C4RGB(0, 0, 0));
|
|
pDraw->DrawLineDw(cgoPicker.Surface, cgoPicker.X + cgoPicker.Wdt, cgoPicker.Y, cgoPicker.X, cgoPicker.Y + cgoPicker.Hgt, C4RGB(0, 0, 0));
|
|
|
|
// V chooser background
|
|
cgoPicker.Set(cgo.Surface, cgo.TargetX + vPickerRect.x + VPickerCursorSize, cgo.TargetY + vPickerRect.y, vPickerRect.Wdt - VPickerCursorSize, vPickerRect.Hgt);
|
|
vFacet.Draw(cgoPicker.Surface, cgoPicker.X, cgoPicker.Y);
|
|
// V cursor
|
|
cgoPicker.Wdt = cgoPicker.Hgt = VPickerCursorSize;
|
|
cgoPicker.X -= cgoPicker.Wdt / 2 + 1;
|
|
cgoPicker.Y += 255 - GetBlueValue(hsv) - cgoPicker.Hgt / 2;
|
|
for (int i = 0; i < cgoPicker.Hgt / 2 + 1; ++i)
|
|
pDraw->DrawLineDw(cgoPicker.Surface, cgoPicker.X + i, cgoPicker.Y + i, cgoPicker.X + i, cgoPicker.Y + cgoPicker.Hgt - i, C4RGB(255, 255, 255));
|
|
}
|
|
|
|
bool C4StartupPlrColorPickerDlg::Picker::HandleMouseDown(int32_t x, int32_t y)
|
|
{
|
|
if (state == PS_IdleDragging)
|
|
{
|
|
// User is dragging something that is neither of the pickers. Ignore.
|
|
return false;
|
|
}
|
|
// Check if a drag starts or was originally started over a picker
|
|
else if (state == PS_DragHS || (state == PS_Idle && hsPickerRect.Contains(x, y)))
|
|
{
|
|
int h = Clamp(x - hsPickerRect.x, 0, hsPickerRect.Wdt - 1);
|
|
assert(Inside(h, 0, 255));
|
|
int s = 255 - Clamp(y - hsPickerRect.y, 0, hsPickerRect.Hgt - 1);
|
|
assert(Inside(s, 0, 255));
|
|
hsv = C4RGB(h, s, GetBlueValue(hsv));
|
|
UpdateVFacet(h, s);
|
|
UpdatePreview();
|
|
state = PS_DragHS;
|
|
return true;
|
|
}
|
|
else if (state == PS_DragV || (state == PS_Idle && vPickerRect.Contains(x, y)))
|
|
{
|
|
int v = 255 - Clamp(y - vPickerRect.y, 0, vPickerRect.Hgt - 1);
|
|
assert(Inside(v, 0, 255));
|
|
hsv = (hsv & 0xFFFFFF00) | v;
|
|
UpdatePreview();
|
|
state = PS_DragV;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Drag started outside of all picker areas; ignore movement until user releases mouse button.
|
|
state = PS_IdleDragging;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void C4StartupPlrColorPickerDlg::Picker::MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
|
|
{
|
|
Control::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
|
|
|
|
if (!rMouse.IsLDown()) state = PS_Idle;
|
|
if (rMouse.pDragElement) return;
|
|
if (rMouse.IsLDown())
|
|
{
|
|
if (HandleMouseDown(iX, iY))
|
|
{
|
|
rMouse.pDragElement = this;
|
|
C4GUI::GUISound("UI::Select");
|
|
}
|
|
else
|
|
{
|
|
rMouse.pDragElement = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void C4StartupPlrColorPickerDlg::Picker::DoDragging(C4GUI::CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam)
|
|
{
|
|
HandleMouseDown(iX, iY);
|
|
}
|
|
|
|
/* ---- Player property dlg ---- */
|
|
|
|
C4StartupPlrPropertiesDlg::C4StartupPlrPropertiesDlg(C4StartupPlrSelDlg::PlayerListItem * pForPlayer, C4StartupPlrSelDlg *pParentDlg)
|
|
: Dialog(C4Startup::Get()->Graphics.fctPlrPropBG.Wdt, C4Startup::Get()->Graphics.fctPlrPropBG.Hgt, "", false), pMainDlg(pParentDlg), pForPlayer(pForPlayer), fClearBigIcon(false)
|
|
{
|
|
if (pForPlayer)
|
|
{
|
|
// edit existing player
|
|
C4P = pForPlayer->GetCore();
|
|
}
|
|
else
|
|
{
|
|
// create new player: Use default C4P values, with a few exceptions
|
|
// FIXME: Use Player, not Clonkranks
|
|
C4P.Default(&::DefaultRanks);
|
|
// Set name, color, comment
|
|
SCopy(LoadResStr("IDS_PLR_NEWCOMMENT"), C4P.Comment, C4MaxComment);
|
|
C4P.PrefColor = UnsyncedRandom(8);
|
|
C4P.PrefColorDw = C4P.GetPrefColorValue(C4P.PrefColor);
|
|
C4P.OldPrefControlStyle = 1;
|
|
C4P.OldPrefAutoContextMenu = 1;
|
|
C4P.OldPrefControl = 0;
|
|
}
|
|
const int32_t BetweenElementDist = 2;
|
|
// use black fonts here
|
|
CStdFont *pUseFont = &C4Startup::Get()->Graphics.BookFont;
|
|
CStdFont *pSmallFont = &C4Startup::Get()->Graphics.BookSmallFont;
|
|
// get positions
|
|
UpdateSize();
|
|
C4GUI::ComponentAligner caMain(GetClientRect(), 0, 1, true);
|
|
C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(C4GUI_ButtonAreaHgt), 0,0);
|
|
// dlg title
|
|
const char *szTitle;
|
|
if (pForPlayer)
|
|
{
|
|
szTitle = LoadResStr("IDS_DLG_PLAYER2");
|
|
}
|
|
else
|
|
{
|
|
szTitle = LoadResStr("IDS_PLR_NEWPLAYER");
|
|
}
|
|
C4GUI::Label *pLbl = new C4GUI::Label(szTitle, caMain.GetFromTop(pUseFont->GetLineHeight()), ALeft, C4StartupFontClr, pUseFont, false);
|
|
AddElement(pLbl);
|
|
caMain.ExpandTop(-BetweenElementDist);
|
|
// place name label
|
|
AddElement(new C4GUI::Label(LoadResStr("IDS_CTL_NAME2"), caMain.GetFromTop(pSmallFont->GetLineHeight()), ALeft, C4StartupFontClr, pSmallFont, false));
|
|
// place name edit
|
|
pNameEdit = new C4GUI::Edit(caMain.GetFromTop(C4GUI::Edit::GetCustomEditHeight(pUseFont)));
|
|
pNameEdit->SetFont(pUseFont);
|
|
pNameEdit->SetColors(C4StartupEditBGColor, C4StartupFontClr, C4StartupEditBorderColor);
|
|
pNameEdit->InsertText(C4P.PrefName, false);
|
|
pNameEdit->SetMaxText(C4MaxName);
|
|
AddElement(pNameEdit);
|
|
SetFocus(pNameEdit, false);
|
|
caMain.ExpandTop(-BetweenElementDist);
|
|
|
|
int32_t iControlPicSize = C4GUI::ArrowButton::GetDefaultHeight(); // GetGridCell(0,3,0,1,-1,-1,false,2)
|
|
int32_t label_hgt = pSmallFont->GetLineHeight();
|
|
|
|
// place color label
|
|
C4GUI::ComponentAligner caColorArea(caMain.GetFromTop(iControlPicSize + BetweenElementDist + label_hgt), 2, 0);
|
|
C4GUI::ComponentAligner caPictureArea(caColorArea.GetFromRight(iControlPicSize, iControlPicSize + BetweenElementDist + label_hgt), 2,0);
|
|
caColorArea.ExpandLeft(2);
|
|
AddElement(new C4GUI::Label(FormatString("%s:", LoadResStr("IDS_CTL_COLOR")).getData(), caColorArea.GetFromTop(label_hgt), ALeft, C4StartupFontClr, pSmallFont, false));
|
|
caColorArea.ExpandTop(-BetweenElementDist);
|
|
// place picture label
|
|
AddElement(new C4GUI::Label(LoadResStr("IDS_CTL_PICTURE"), caPictureArea.GetFromTop(label_hgt), ALeft, C4StartupFontClr, pSmallFont, false));
|
|
caPictureArea.ExpandTop(-BetweenElementDist);
|
|
// place color controls
|
|
C4GUI::Button *pBtn; const char *szTip;
|
|
szTip = LoadResStr("IDS_DLGTIP_PLAYERCOLORS");
|
|
AddElement(pBtn = new C4GUI::CallbackButton<C4StartupPlrPropertiesDlg, C4GUI::ArrowButton>(C4GUI::ArrowButton::Left, caColorArea.GetFromLeft(C4GUI::ArrowButton::GetDefaultWidth()), &C4StartupPlrPropertiesDlg::OnClrChangeLeft));
|
|
pBtn->SetToolTip(szTip);
|
|
C4Facet &rfctClrPreviewPic = ::GraphicsResource.fctFlagClr;
|
|
pClrPreview = new C4GUI::CallbackButton<C4StartupPlrPropertiesDlg, C4GUI::IconButton>(C4GUI::Ico_None, caColorArea.GetFromLeft(rfctClrPreviewPic.GetWidthByHeight(caColorArea.GetHeight())), 'C', &C4StartupPlrPropertiesDlg::OnClrChangeCustom);
|
|
pClrPreview->SetFacet(rfctClrPreviewPic);
|
|
AddElement(pClrPreview);
|
|
AddElement(pBtn = new C4GUI::CallbackButton<C4StartupPlrPropertiesDlg, C4GUI::ArrowButton>(C4GUI::ArrowButton::Right, caColorArea.GetFromLeft(C4GUI::ArrowButton::GetDefaultWidth()), &C4StartupPlrPropertiesDlg::OnClrChangeRight));
|
|
pBtn->SetToolTip(szTip);
|
|
if (!C4P.PrefColorDw) C4P.PrefColorDw=0xff;
|
|
// Place picture controls
|
|
AddElement(pPictureBtn = new C4GUI::CallbackButton<C4StartupPlrPropertiesDlg, C4GUI::IconButton>(C4GUI::Ico_Player, caPictureArea.GetAll(), 'P' /* 2do */, &C4StartupPlrPropertiesDlg::OnPictureBtn));
|
|
pPictureBtn->SetToolTip(LoadResStr("IDS_DESC_SELECTAPICTUREANDORLOBBYI"));
|
|
UpdateBigIcon();
|
|
UpdatePlayerColor(true);
|
|
caMain.ExpandTop(-BetweenElementDist);
|
|
// place control label
|
|
C4GUI::ComponentAligner caControlArea(caMain.GetFromTop(iControlPicSize + label_hgt + BetweenElementDist), 0,0, false);
|
|
C4GUI::ComponentAligner caSkinArea(caControlArea.GetFromRight(iControlPicSize + label_hgt + BetweenElementDist), 0,0, false);
|
|
AddElement(new C4GUI::Label(FormatString("%s:", LoadResStr("IDS_CTL_CONTROL")).getData(), caControlArea.GetFromTop(label_hgt), ALeft, C4StartupFontClr, pSmallFont, false));
|
|
caControlArea.ExpandTop(-BetweenElementDist);
|
|
// place clonk style label
|
|
AddElement(new C4GUI::Label(LoadResStr("IDS_CTL_CLONKSKIN"), caSkinArea.GetFromTop(label_hgt), ALeft, C4StartupFontClr, pSmallFont, false));
|
|
caSkinArea.ExpandTop(-BetweenElementDist);
|
|
// place control controls
|
|
C4GUI::ComponentAligner caControl(caControlArea.GetFromTop(iControlPicSize), 2,0);
|
|
szTip = LoadResStr("IDS_DLGTIP_PLAYERCONTROL");
|
|
AddElement(pBtn = new C4GUI::CallbackButton<C4StartupPlrPropertiesDlg, C4GUI::ArrowButton>(C4GUI::ArrowButton::Left, caControl.GetFromLeft(C4GUI::ArrowButton::GetDefaultWidth()), &C4StartupPlrPropertiesDlg::OnCtrlChangeLeft));
|
|
pBtn->SetToolTip(szTip);
|
|
caControl.ExpandBottom(label_hgt); C4Rect ctrl_name_rect = caControl.GetFromBottom(label_hgt);
|
|
C4Facet &rfctCtrlPic = ::GraphicsResource.fctKeyboard; // UpdatePlayerControl() will alternatively set fctGamepad
|
|
AddElement(pCtrlImg = new C4GUI::Picture(caControl.GetFromLeft(rfctCtrlPic.GetWidthByHeight(caControl.GetHeight())), true));
|
|
pCtrlImg->SetToolTip(szTip);
|
|
AddElement(pBtn = new C4GUI::CallbackButton<C4StartupPlrPropertiesDlg, C4GUI::ArrowButton>(C4GUI::ArrowButton::Right, caControl.GetFromLeft(C4GUI::ArrowButton::GetDefaultWidth()), &C4StartupPlrPropertiesDlg::OnCtrlChangeRight));
|
|
pBtn->SetToolTip(szTip);
|
|
caControl.ExpandLeft(-10);
|
|
C4P.OldPrefControl = Clamp<int32_t>(C4P.OldPrefControl, 0, C4MaxControlSet-1);
|
|
ctrl_name_lbl = new C4GUI::Label("CtrlName", ctrl_name_rect, ALeft, C4StartupFontClr, pSmallFont, false, false, true);
|
|
AddElement(ctrl_name_lbl);
|
|
UpdatePlayerControl();
|
|
|
|
C4GUI::ComponentAligner caSkin(caSkinArea.GetFromTop(iControlPicSize), 2,0);
|
|
szTip = LoadResStr("IDS_DLGTIP_PLAYERCREWSKIN");
|
|
AddElement(pBtn = new C4GUI::CallbackButton<C4StartupPlrPropertiesDlg, C4GUI::ArrowButton>(C4GUI::ArrowButton::Left, caSkin.GetFromLeft(C4GUI::ArrowButton::GetDefaultWidth()), &C4StartupPlrPropertiesDlg::OnSkinChangeLeft));
|
|
pBtn->SetToolTip(szTip);
|
|
C4Facet rfctSkinPic = ::GraphicsResource.fctClonkSkin.GetPhase(0);
|
|
AddElement(pSkinImg = new C4GUI::Picture(caSkin.GetFromLeft(rfctSkinPic.GetWidthByHeight(caSkin.GetHeight())), true));
|
|
pSkinImg->SetToolTip(szTip);
|
|
pSkinImg->SetFacet(::GraphicsResource.fctClonkSkin.GetPhase(0));
|
|
AddElement(pBtn = new C4GUI::CallbackButton<C4StartupPlrPropertiesDlg, C4GUI::ArrowButton>(C4GUI::ArrowButton::Right, caSkin.GetFromLeft(C4GUI::ArrowButton::GetDefaultWidth()), &C4StartupPlrPropertiesDlg::OnSkinChangeRight));
|
|
pBtn->SetToolTip(szTip);
|
|
caSkin.ExpandLeft(-10);
|
|
UpdatePlayerSkin();
|
|
|
|
caMain.ExpandTop(-BetweenElementDist);
|
|
// place buttons
|
|
// OK
|
|
C4GUI::Button *pBtnOK = new C4GUI::OKIconButton(C4Rect(147-GetMarginLeft(), 295+35-GetMarginTop(), 54, 33), C4GUI::Ico_None);
|
|
AddElement(pBtnOK);
|
|
// Cancel
|
|
C4GUI::Button *pBtnAbort = new C4GUI::CancelIconButton(C4Rect(317-GetMarginLeft(), 16-GetMarginTop(), 21, 21), C4GUI::Ico_None);
|
|
AddElement(pBtnAbort);
|
|
// when called from player selection screen: input dlg always closed in the end
|
|
// otherwise, modal proc will delete
|
|
if (pMainDlg) SetDelOnClose();
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::DrawElement(C4TargetFacet &cgo)
|
|
{
|
|
C4Startup::Get()->Graphics.fctPlrPropBG.Draw(cgo.Surface, rcBounds.x+cgo.TargetX, rcBounds.y+cgo.TargetY);
|
|
}
|
|
|
|
bool IsColorConflict(DWORD dwClr1, DWORD dwClr2);
|
|
|
|
void C4StartupPlrPropertiesDlg::UpdatePlayerColor(bool fUpdateSliders)
|
|
{
|
|
C4P.PrefColorDw = C4P.PrefColorDw | 0xFF000000; // Ensure full opacity
|
|
pClrPreview->SetColor(C4P.PrefColorDw);
|
|
pPictureBtn->SetColor(C4P.PrefColorDw);
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::OnClrChangeLeft(C4GUI::Control *pBtn)
|
|
{
|
|
// previous standard color in list
|
|
C4P.PrefColor = C4P.PrefColor ? C4P.PrefColor - 1 : 11;
|
|
C4P.PrefColorDw = C4PlayerInfoCore::GetPrefColorValue(C4P.PrefColor);
|
|
UpdatePlayerColor(true);
|
|
}
|
|
void C4StartupPlrPropertiesDlg::OnClrChangeCustom(C4GUI::Control *pBtn)
|
|
{
|
|
GetScreen()->ShowModalDlg(new C4StartupPlrColorPickerDlg(&C4P));
|
|
UpdatePlayerColor(true);
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::OnClrChangeRight(C4GUI::Control *pBtn)
|
|
{
|
|
// next standard color in list
|
|
C4P.PrefColor = (C4P.PrefColor + 1) % 12;
|
|
C4P.PrefColorDw = C4PlayerInfoCore::GetPrefColorValue(C4P.PrefColor);
|
|
UpdatePlayerColor(true);
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::UpdatePlayerControl()
|
|
{
|
|
C4PlayerControlAssignmentSet *control_set = Game.PlayerControlUserAssignmentSets.GetSetByName(C4P.PrefControl.getData());
|
|
if (!control_set) control_set = Game.PlayerControlUserAssignmentSets.GetDefaultSet();
|
|
// update keyboard image of selected control
|
|
C4Facet fctCtrlPic;
|
|
if (control_set) fctCtrlPic = control_set->GetPicture();
|
|
pCtrlImg->SetFacet(fctCtrlPic);
|
|
if (control_set)
|
|
ctrl_name_lbl->SetText(control_set->GetGUIName());
|
|
else
|
|
ctrl_name_lbl->SetText("???");
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::OnCtrlChangeLeft(C4GUI::Control *pBtn)
|
|
{
|
|
// previous control set in list
|
|
C4PlayerControlAssignmentSet *control_set = Game.PlayerControlUserAssignmentSets.GetSetByName(C4P.PrefControl.getData());
|
|
int32_t index = Game.PlayerControlUserAssignmentSets.GetSetIndex(control_set);
|
|
if (index < 0) index = 0; // defined control set not found - probably an old CR player file
|
|
if (!index--) index = Game.PlayerControlUserAssignmentSets.GetSetCount() - 1;
|
|
control_set = Game.PlayerControlUserAssignmentSets.GetSetByIndex(index);
|
|
if (control_set) C4P.PrefControl = control_set->GetName();
|
|
UpdatePlayerControl();
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::OnCtrlChangeRight(C4GUI::Control *pBtn)
|
|
{
|
|
// next control set in list
|
|
C4PlayerControlAssignmentSet *control_set = Game.PlayerControlUserAssignmentSets.GetSetByName(C4P.PrefControl.getData());
|
|
int32_t index = Game.PlayerControlUserAssignmentSets.GetSetIndex(control_set);
|
|
if (index < 0) index = 0; // defined control set not found - probably an old CR player file
|
|
if (++index >= int32_t(Game.PlayerControlUserAssignmentSets.GetSetCount())) index = 0;
|
|
control_set = Game.PlayerControlUserAssignmentSets.GetSetByIndex(index);
|
|
if (control_set) C4P.PrefControl = control_set->GetName();
|
|
UpdatePlayerControl();
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::UpdatePlayerSkin()
|
|
{
|
|
pSkinImg->SetFacet(::GraphicsResource.fctClonkSkin.GetPhase(C4P.PrefClonkSkin));
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::OnSkinChangeLeft(C4GUI::Control *pBtn)
|
|
{
|
|
// previous skin in list
|
|
C4P.PrefClonkSkin = C4P.PrefClonkSkin ? C4P.PrefClonkSkin - 1 : 3;
|
|
UpdatePlayerSkin();
|
|
}
|
|
void C4StartupPlrPropertiesDlg::OnSkinChangeRight(C4GUI::Control *pBtn)
|
|
{
|
|
// next skin in list
|
|
C4P.PrefClonkSkin = (C4P.PrefClonkSkin + 1) % 4;
|
|
UpdatePlayerSkin();
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::UserClose(bool fOK)
|
|
{
|
|
// check name validity
|
|
if (fOK)
|
|
{
|
|
StdStrBuf PlrName(pNameEdit->GetText()), Filename;
|
|
if (!C4StartupPlrSelDlg::CheckPlayerName(PlrName, Filename, pForPlayer ? &pForPlayer->GetFilename() : nullptr, true)) return;
|
|
|
|
// Warn that gamepad controls are still unfinished.
|
|
C4PlayerControlAssignmentSet *control_set = Game.PlayerControlUserAssignmentSets.GetSetByName(C4P.PrefControl.getData());
|
|
if (control_set && control_set->HasGamepad())
|
|
{
|
|
GetScreen()->ShowMessageModal(
|
|
LoadResStr("IDS_DLG_GAMEPADEXPERIMENTAL"),
|
|
LoadResStr("IDS_DLG_GAMEPADEXPTITLE"),
|
|
C4GUI::MessageDialog::btnOK,
|
|
C4GUI::Ico_Gamepad
|
|
);
|
|
}
|
|
}
|
|
Close(fOK);
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::OnClosed(bool fOK)
|
|
{
|
|
if (fOK)
|
|
{
|
|
// store selected data if desired
|
|
StdStrBuf PlrName(pNameEdit->GetText()), Filename;
|
|
if (C4StartupPlrSelDlg::CheckPlayerName(PlrName, Filename, pForPlayer ? &pForPlayer->GetFilename() : nullptr, true))
|
|
{
|
|
SCopy(PlrName.getData(), C4P.PrefName, C4MaxName);
|
|
C4Group PlrGroup;
|
|
bool fSucc=false;
|
|
// existant player: update file
|
|
if (pForPlayer)
|
|
{
|
|
if (!pForPlayer->MoveFilename(Filename.getData()))
|
|
GetScreen()->ShowMessage(LoadResStr("IDS_FAIL_RENAME"), "", C4GUI::Ico_Error);
|
|
// update bigicon
|
|
if (fClearBigIcon || fctNewBigIcon.Surface)
|
|
{
|
|
C4Group PlrGroup;
|
|
if (PlrGroup.Open(Config.AtUserDataPath(Filename.getData())))
|
|
{
|
|
if (fClearBigIcon || fctNewBigIcon.Surface) PlrGroup.Delete(C4CFN_BigIcon);
|
|
if (fctNewBigIcon.Surface) fctNewBigIcon.GetFace().SavePNG(PlrGroup, C4CFN_BigIcon);
|
|
if (PlrGroup.Close()) fSucc = true;
|
|
if (fClearBigIcon || fctNewBigIcon.Surface) pForPlayer->GrabCustomIcon(fctNewBigIcon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fSucc = true;
|
|
}
|
|
pForPlayer->UpdateCore(C4P);
|
|
// player may have been activated: Make sure any new filename is reflected in participants list
|
|
if (pMainDlg) pMainDlg->UpdateActivatedPlayers();
|
|
}
|
|
else
|
|
{
|
|
// NewPlayer: Open new player group
|
|
if (PlrGroup.Open(Config.AtUserDataPath(Filename.getData()), true))
|
|
{
|
|
// Do not overwrite (should have been caught earlier anyway)
|
|
if (PlrGroup.FindEntry(C4CFN_PlayerInfoCore)) return;
|
|
// Save info core
|
|
C4P.Save(PlrGroup);
|
|
// Add BigIcon
|
|
if (fctNewBigIcon.Surface)
|
|
{
|
|
fctNewBigIcon.GetFace().SavePNG(PlrGroup, C4CFN_BigIcon);
|
|
}
|
|
// Close group
|
|
if (PlrGroup.Close()) fSucc=true;
|
|
// update activate button text
|
|
if (pMainDlg)
|
|
{
|
|
pMainDlg->UpdatePlayerList();
|
|
pMainDlg->SelectItem(Filename, true);
|
|
}
|
|
else
|
|
{
|
|
// no main player selection dialog: This means that this dlg was shown as a creation dialog from the main startup dlg
|
|
// Just set the newly created player as current selection
|
|
SCopy(Config.AtRelativePath(Filename.getData()), Config.General.Participants, sizeof Config.General.Participants);
|
|
}
|
|
}
|
|
}
|
|
if (!fSucc) GetScreen()->ShowErrorMessage(PlrGroup.GetError());
|
|
}
|
|
}
|
|
// Make the dialog go away
|
|
Dialog::OnClosed(fOK);
|
|
}
|
|
|
|
bool C4StartupPlrPropertiesDlg::SetNewPicture(C4Surface &srcSfc, C4FacetSurface *trgFct, int32_t iMaxSize, bool fColorize)
|
|
{
|
|
if (fColorize)
|
|
{
|
|
C4Surface srcSfcClr;
|
|
if (!srcSfcClr.CreateColorByOwner(&srcSfc)) return false;
|
|
return trgFct->CopyFromSfcMaxSize(srcSfcClr, iMaxSize, C4P.PrefColorDw);
|
|
}
|
|
else
|
|
{
|
|
return trgFct->CopyFromSfcMaxSize(srcSfc, iMaxSize);
|
|
}
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::SetNewPicture(const char *szFromFilename)
|
|
{
|
|
if (!szFromFilename)
|
|
{
|
|
// If szFromFilename==nullptr, clear bigicon
|
|
fClearBigIcon = true;
|
|
fctNewBigIcon.Clear();
|
|
}
|
|
else
|
|
{
|
|
// else set new bigicon by loading and scaling if necessary.
|
|
C4Surface sfcNewPic;
|
|
C4Group SrcGrp;
|
|
StdStrBuf sParentPath;
|
|
GetParentPath(szFromFilename, &sParentPath);
|
|
bool fSucc = false;
|
|
if (SrcGrp.Open(sParentPath.getData()))
|
|
{
|
|
if (sfcNewPic.Load(SrcGrp, GetFilename(szFromFilename), false, false, 0))
|
|
{
|
|
fSucc = true;
|
|
if (!SetNewPicture(sfcNewPic, &fctNewBigIcon, C4MaxBigIconSize, true)) fSucc = false;
|
|
}
|
|
}
|
|
if (!fSucc)
|
|
{
|
|
// error!
|
|
GetScreen()->ShowErrorMessage(FormatString(LoadResStr("IDS_PRC_NOGFXFILE"), szFromFilename, SrcGrp.GetError()).getData());
|
|
}
|
|
}
|
|
// update icon
|
|
UpdateBigIcon();
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::OnPictureBtn(C4GUI::Control *pBtn)
|
|
{
|
|
StdStrBuf sNewPic;
|
|
if (C4PortraitSelDlg::SelectPortrait(GetScreen(), &sNewPic))
|
|
{
|
|
SetNewPicture(sNewPic.getData());
|
|
}
|
|
}
|
|
|
|
void C4StartupPlrPropertiesDlg::UpdateBigIcon()
|
|
{
|
|
// new icon?
|
|
bool fHasIcon = false;
|
|
if (fctNewBigIcon.Surface)
|
|
{
|
|
pPictureBtn->SetFacet(fctNewBigIcon);
|
|
fHasIcon = true;
|
|
}
|
|
// old icon in existing player?
|
|
else if (!fClearBigIcon && pForPlayer)
|
|
{
|
|
C4Group PlrGroup;
|
|
if (PlrGroup.Open(Config.AtUserDataPath(pForPlayer->GetFilename().getData())))
|
|
{
|
|
if (PlrGroup.FindEntry(C4CFN_BigIcon))
|
|
{
|
|
if (fctOldBigIcon.Load(PlrGroup, C4CFN_BigIcon, C4FCT_Full, C4FCT_Full, false, 0))
|
|
{
|
|
pPictureBtn->SetFacet(fctOldBigIcon);
|
|
fHasIcon = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// no icon: Set default
|
|
if (!fHasIcon)
|
|
{
|
|
pPictureBtn->SetFacet(::GraphicsResource.fctPlayerClr);
|
|
}
|
|
}
|