forked from Mirrors/openclonk
895 lines
31 KiB
C++
895 lines
31 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 1998-2000, 2003, 2007-2008 Matthes Bender
|
|
* Copyright (c) 2001 Michael Käser
|
|
* Copyright (c) 2003, 2007-2008 Sven Eberhardt
|
|
* Copyright (c) 2004, 2008-2009 Günther Brammer
|
|
* Copyright (c) 2010 Benjamin Herr
|
|
* Copyright (c) 2010 Nicolas Hake
|
|
* Copyright (c) 2008-2009, RedWolf Design GmbH, http://www.clonk.de
|
|
*
|
|
* Portions might be copyrighted by other authors who have contributed
|
|
* to OpenClonk.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
* See isc_license.txt for full license and disclaimer.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender.
|
|
* See clonk_trademark_license.txt for full license.
|
|
*/
|
|
// Engine internal C4Menus: Main menu, Options, Player join, Hostility, etc.
|
|
|
|
#include <C4Include.h>
|
|
#include <C4MainMenu.h>
|
|
|
|
#include <C4DefList.h>
|
|
#include <C4FullScreen.h>
|
|
#include <C4Viewport.h>
|
|
#include <C4Player.h>
|
|
#include <C4GameOverDlg.h>
|
|
#include <C4Object.h>
|
|
#include <C4GraphicsResource.h>
|
|
#include <C4GraphicsSystem.h>
|
|
#include <C4Game.h>
|
|
#include <C4PlayerList.h>
|
|
#include <C4GameObjects.h>
|
|
#include <C4Network2.h>
|
|
#include <C4GameControl.h>
|
|
|
|
// -----------------------------------------------------------
|
|
// C4MainMenu
|
|
|
|
C4MainMenu::C4MainMenu() : C4Menu() // will be re-adjusted later
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void C4MainMenu::Default()
|
|
{
|
|
C4Menu::Default();
|
|
Player=NO_OWNER;
|
|
}
|
|
|
|
bool C4MainMenu::Init(C4FacetSurface &fctSymbol, const char *szEmpty, int32_t iPlayer, int32_t iExtra, int32_t iExtraData, int32_t iId, int32_t iStyle)
|
|
{
|
|
if (!DoInit(fctSymbol, szEmpty, iExtra, iExtraData, iId, iStyle)) return false;
|
|
Player=iPlayer;
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::InitRefSym(const C4Facet &fctSymbol, const char *szEmpty, int32_t iPlayer, int32_t iExtra, int32_t iExtraData, int32_t iId, int32_t iStyle)
|
|
{
|
|
if (!DoInitRefSym(fctSymbol, szEmpty, iExtra, iExtraData, iId, iStyle)) return false;
|
|
Player=iPlayer;
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::ActivateNewPlayer(int32_t iPlayer)
|
|
{
|
|
// league or replay game
|
|
if (Game.Parameters.isLeague() || Game.C4S.Head.Replay) return false;
|
|
// Max player limit
|
|
if (::Players.GetCount() >= Game.Parameters.MaxPlayers) return false;
|
|
|
|
// Menu symbol/init
|
|
if (GfxR->fctPlayerClr.Surface)
|
|
GfxR->fctPlayerClr.Surface->SetClr(0xff);
|
|
InitRefSym(GfxR->fctPlayerClr, LoadResStr("IDS_MENU_NOPLRFILES"), iPlayer);
|
|
for (DirectoryIterator iter(Config.General.UserDataPath); *iter; ++iter)
|
|
if (WildcardMatch("*.ocp", *iter))
|
|
{
|
|
char szFilename[_MAX_PATH+1], szCommand[_MAX_PATH+30+1];
|
|
SCopy(*iter, szFilename, _MAX_PATH);
|
|
if (DirectoryExists(szFilename)) continue;
|
|
if (::Players.FileInUse(szFilename)) continue;
|
|
// Open group
|
|
C4Group hGroup;
|
|
if (!hGroup.Open(szFilename)) continue;
|
|
// Load player info
|
|
C4PlayerInfoCore C4P;
|
|
if (!C4P.Load(hGroup)) { hGroup.Close(); continue; }
|
|
// Close group
|
|
hGroup.Close();
|
|
// Add player item
|
|
sprintf(szCommand, "JoinPlayer:%s", szFilename);
|
|
StdStrBuf sItemText;
|
|
sItemText.Format(LoadResStr("IDS_MENU_NEWPLAYER"), C4P.PrefName);
|
|
C4FacetSurface fctSymbol;
|
|
// Add menu item
|
|
Add(sItemText.getData(), fctSymbol, szCommand);
|
|
// Reset symbol facet (menu holds on to the surface)
|
|
fctSymbol.Default();
|
|
}
|
|
|
|
// Alignment
|
|
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
|
|
// Go back to options menu on close
|
|
SetCloseCommand("ActivateMenu:Main");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::DoRefillInternal(bool &rfRefilled)
|
|
{
|
|
// Variables
|
|
C4FacetSurface fctSymbol;
|
|
C4Player *pPlayer;
|
|
C4IDList ListItems;
|
|
C4Facet fctTarget;
|
|
bool fWasEmpty = !GetItemCount();
|
|
|
|
// Refill
|
|
switch (Identification)
|
|
{
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
case C4MN_Hostility:
|
|
{
|
|
// Clear items
|
|
ClearItems();
|
|
// Refill player
|
|
if (!(pPlayer = ::Players.Get(Player))) return false;
|
|
// Refill items
|
|
C4Player *pPlr; int32_t iIndex;
|
|
for (iIndex=0; (pPlr = ::Players.GetByIndex(iIndex)); iIndex++)
|
|
// Ignore player self and invisible
|
|
if (pPlr != pPlayer) if (!pPlr->IsInvisible())
|
|
{
|
|
// Symbol
|
|
fctSymbol.Create(C4SymbolSize,C4SymbolSize);
|
|
pPlayer->DrawHostility(fctSymbol,iIndex);
|
|
// Message
|
|
StdStrBuf sMsg;
|
|
bool isFriendly = pPlayer->Hostility.find(pPlr) == pPlayer->Hostility.end();
|
|
if (isFriendly)
|
|
sMsg.Format(LoadResStr("IDS_MENU_ATTACK"),pPlr->GetName());
|
|
else
|
|
sMsg.Format(LoadResStr("IDS_MENU_NOATTACK"),pPlr->GetName());
|
|
// Command
|
|
char szCommand[1000];
|
|
sprintf(szCommand,"SetHostility:%i",pPlr->Number);
|
|
// Info caption
|
|
char szInfoCaption[C4MaxTitle+1],szFriendly[50],szNot[30]="";
|
|
SCopy(LoadResStr(isFriendly ? "IDS_MENU_ATTACKHOSTILE" : "IDS_MENU_ATTACKFRIENDLY"),szFriendly);
|
|
if (!isFriendly) SCopy(LoadResStr("IDS_MENU_ATTACKNOT"),szNot);
|
|
sprintf(szInfoCaption,LoadResStr("IDS_MENU_ATTACKINFO"),pPlr->GetName(),szFriendly,szNot);
|
|
if (iIndex==pPlayer->Number) SCopy(LoadResStr("IDS_MENU_ATTACKSELF"),szInfoCaption);
|
|
// Add item
|
|
Add(sMsg.getData(),fctSymbol,szCommand,C4MN_Item_NoCount,NULL,szInfoCaption);
|
|
fctSymbol.Default();
|
|
}
|
|
break;
|
|
}
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
case C4MN_TeamSelection:
|
|
case C4MN_TeamSwitch:
|
|
{
|
|
// Clear items
|
|
ClearItems();
|
|
// add all teams as menu items
|
|
// 2do: Icon
|
|
C4Team *pTeam; int32_t i=0; bool fAddNewTeam=Game.Teams.IsAutoGenerateTeams();
|
|
for (;;)
|
|
{
|
|
pTeam = Game.Teams.GetTeamByIndex(i);
|
|
if (pTeam)
|
|
{
|
|
// next regular team
|
|
++i;
|
|
// do not add a new team if an empty team exists
|
|
if (!pTeam->GetPlayerCount()) fAddNewTeam = false;
|
|
}
|
|
else if (fAddNewTeam)
|
|
{
|
|
// join new team
|
|
fAddNewTeam = false;
|
|
}
|
|
else
|
|
{
|
|
// all teams done
|
|
break;
|
|
}
|
|
// create team symbol: Icon spec if specified; otherwise flag for empty and crew for nonempty team
|
|
fctSymbol.Create(C4SymbolSize,C4SymbolSize);
|
|
const char *szIconSpec = pTeam ? pTeam->GetIconSpec() : NULL;
|
|
bool fHasIcon = false;
|
|
if (szIconSpec && *szIconSpec)
|
|
{
|
|
C4FacetSurface fctSrc;
|
|
fHasIcon = Game.DrawTextSpecImage(fctSrc, szIconSpec, pTeam->GetColor());
|
|
fctSrc.Draw(fctSymbol);
|
|
}
|
|
if (!fHasIcon)
|
|
{
|
|
if (pTeam && pTeam->GetPlayerCount())
|
|
::GraphicsResource.fctCrewClr.DrawClr(fctSymbol, true, pTeam->GetColor());
|
|
else
|
|
C4GUI::Icon::GetIconFacet(C4GUI::Ico_Team).Draw(fctSymbol, true);
|
|
}
|
|
StdStrBuf sTeamName;
|
|
if (pTeam)
|
|
{
|
|
sTeamName.Take(pTeam->GetNameWithParticipants());
|
|
}
|
|
else
|
|
sTeamName.Ref(LoadResStr("IDS_PRC_NEWTEAM"));
|
|
const char *szOperation = (Identification == C4MN_TeamSwitch) ? "TeamSwitch" : "TeamSel";
|
|
Add(sTeamName.getData(), fctSymbol,FormatString("%s:%d", szOperation, pTeam ? pTeam->GetID() : TEAMID_New).getData(),
|
|
C4MN_Item_NoCount,NULL,FormatString(LoadResStr("IDS_MSG_JOINTEAM"), sTeamName.getData()).getData(), C4ID(pTeam ? pTeam->GetID() : 0));
|
|
fctSymbol.Default();
|
|
}
|
|
break;
|
|
}
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
case C4MN_Observer: // observer menu
|
|
{
|
|
// Clear items
|
|
ClearItems();
|
|
// Check validity
|
|
C4Viewport *pVP = ::Viewports.GetViewport(NO_OWNER);
|
|
if (!pVP) return false;
|
|
int32_t iInitialSelection = 0;
|
|
// Add free view
|
|
AddRefSym(LoadResStr("IDS_MSG_FREEVIEW"), C4GUI::Icon::GetIconFacet(C4GUI::Ico_Star), "Observe:Free", C4MN_Item_NoCount, NULL, LoadResStr("IDS_MSG_FREELYSCROLLAROUNDTHEMAP"));
|
|
// Add players
|
|
C4Player *pPlr; int32_t iIndex;
|
|
for (iIndex=0; (pPlr = ::Players.GetByIndex(iIndex)); iIndex++)
|
|
{
|
|
// Ignore invisible
|
|
if (!pPlr->IsInvisible())
|
|
{
|
|
// Symbol
|
|
fctSymbol.Create(C4SymbolSize,C4SymbolSize);
|
|
::GraphicsResource.fctPlayerClr.DrawClr(fctSymbol, true, pPlr->ColorDw);
|
|
// Message
|
|
StdStrBuf sMsg;
|
|
DWORD dwClr = pPlr->ColorDw;
|
|
sMsg.Format("<c %x>%s</c>", (unsigned int)C4GUI::MakeColorReadableOnBlack(dwClr), pPlr->GetName());
|
|
// Command
|
|
StdStrBuf sCommand;
|
|
sCommand.Format("Observe:%d", (int)pPlr->Number);
|
|
// Info caption
|
|
StdStrBuf sInfo;
|
|
sInfo.Format(LoadResStr("IDS_TEXT_FOLLOWVIEWOFPLAYER"), pPlr->GetName());
|
|
// Add item
|
|
Add(sMsg.getData(),fctSymbol,sCommand.getData(),C4MN_Item_NoCount,NULL,sInfo.getData());
|
|
fctSymbol.Default();
|
|
// check if this is the currently selected player
|
|
if (pVP->GetPlayer() == pPlr->Number) iInitialSelection = GetItemCount()-1;
|
|
}
|
|
// Initial selection on followed player
|
|
if (fWasEmpty) SetSelection(iInitialSelection, false, true);
|
|
}
|
|
}
|
|
break;
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
default:
|
|
// No internal refill needed
|
|
return true;
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
}
|
|
|
|
// Successfull internal refill
|
|
rfRefilled = true;
|
|
return true;
|
|
}
|
|
|
|
void C4MainMenu::OnSelectionChanged(int32_t iNewSelection)
|
|
{
|
|
// immediate player highlight in observer menu
|
|
if (Identification == C4MN_Observer)
|
|
{
|
|
C4MenuItem *pItem = GetSelectedItem();
|
|
if (pItem)
|
|
{
|
|
if (SEqual2(pItem->GetCommand(), "Observe:"))
|
|
MenuCommand(pItem->GetCommand(), false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void C4MainMenu::OnUserSelectItem(int32_t Player, int32_t iIndex)
|
|
{
|
|
// direct selection for non-sync-menus
|
|
SetSelection(iIndex, true, true);
|
|
}
|
|
|
|
void C4MainMenu::OnUserEnter(int32_t Player, int32_t iIndex, bool fRight)
|
|
{
|
|
// direct menu control
|
|
// but ensure selection is Okay before
|
|
SetSelection(iIndex, true, false);
|
|
Enter(fRight);
|
|
}
|
|
|
|
void C4MainMenu::OnUserClose()
|
|
{
|
|
// just close
|
|
TryClose(false, true);
|
|
}
|
|
|
|
bool C4MainMenu::ActivateGoals(int32_t iPlayer, bool fDoActivate)
|
|
{
|
|
C4FacetSurface fctSymbol;
|
|
C4Facet fctGF; // goal fulfilled facet
|
|
|
|
if (fDoActivate)
|
|
{
|
|
// Menu symbol/init
|
|
InitRefSym(GfxR->fctMenu.GetPhase(4),LoadResStr("IDS_MENU_CPGOALS"),iPlayer);
|
|
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
|
|
SetPermanent(false);
|
|
fctGF.Set(NULL, C4SymbolSize-::GraphicsResource.fctCaptain.Wdt-2, 2, ::GraphicsResource.fctCaptain.Wdt, ::GraphicsResource.fctCaptain.Hgt);
|
|
}
|
|
// determine if the goals are fulfilled - do the calls even if the menu is not to be opened to ensure synchronization
|
|
C4IDList GoalList, FulfilledGoalList;
|
|
C4RoundResults::EvaluateGoals(GoalList, FulfilledGoalList, iPlayer);
|
|
// Add Items
|
|
if (fDoActivate)
|
|
{
|
|
int32_t iNumGoals = GoalList.GetNumberOfIDs(), cnt;
|
|
C4ID idGoal; C4Def *pDef;
|
|
for (int32_t i=0; i<iNumGoals; ++i)
|
|
if ((idGoal = GoalList.GetID(i, &cnt)))
|
|
if ((pDef = C4Id2Def(idGoal)))
|
|
{
|
|
fctSymbol.Create(C4SymbolSize,C4SymbolSize);
|
|
// 2do: If an object instance is known, draw the object instead?
|
|
// this would allow us to do dynamic pictures and overlays; e.g. draw the actual, required settlement score
|
|
// for settlement score goals
|
|
// Same for pDef->GetName(), pDef->GetDesc()
|
|
pDef->Draw(fctSymbol);
|
|
if (FulfilledGoalList.GetIDCount(idGoal))
|
|
{
|
|
fctGF.Surface=fctSymbol.Surface;
|
|
::GraphicsResource.fctCaptain.Draw(fctGF);
|
|
}
|
|
StdStrBuf Command; Command.Format("Player:Goal:%s", idGoal.ToString());
|
|
Add(pDef->GetName(),fctSymbol,Command.getData(),C4MN_Item_NoCount,NULL,"");
|
|
}
|
|
// Go back to options menu on close
|
|
SetCloseCommand("ActivateMenu:Main");
|
|
}
|
|
// Done
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::ActivateRules(int32_t iPlayer)
|
|
{
|
|
// Menu symbol/init
|
|
char Command[256];
|
|
C4FacetSurface fctSymbol;
|
|
InitRefSym(GfxR->fctMenu.GetPhase(5),LoadResStr("IDS_MENU_CPRULES"),iPlayer);
|
|
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
|
|
SetPermanent(false);
|
|
// Items
|
|
int32_t cnt; C4ID idGoal; C4Def *pDef;
|
|
for (cnt=0; (idGoal=::Objects.GetListID(C4D_Rule,cnt)); cnt++)
|
|
if ((pDef=C4Id2Def(idGoal)))
|
|
{
|
|
fctSymbol.Create(C4SymbolSize,C4SymbolSize); pDef->Draw(fctSymbol);
|
|
sprintf(Command, "Player:Rule:%s", idGoal.ToString());
|
|
Add(pDef->GetName(),fctSymbol,Command,C4MN_Item_NoCount,NULL,"");
|
|
}
|
|
// Go back to options menu on close
|
|
SetCloseCommand("ActivateMenu:Main");
|
|
// Done
|
|
return true;
|
|
}
|
|
|
|
bool LooksLikeInteger(const char *szInt)
|
|
{
|
|
// safety
|
|
if (!szInt) return false;
|
|
// check sign
|
|
if (*szInt == '+' || *szInt == '-') ++szInt;
|
|
// check int32_t length
|
|
if (!*szInt) return false;
|
|
// must contain only digits now
|
|
char c;
|
|
while ((c = *(szInt++))) if (!Inside<char>(c, '0', '9')) return false;
|
|
// it's an int32_t
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::ActivateSavegame(int32_t iPlayer)
|
|
{
|
|
// Check if saving is possible
|
|
if (!Game.CanQuickSave()) return false;
|
|
|
|
// Menu symbol/init
|
|
char DirPath[_MAX_PATH+1];
|
|
char ScenName[_MAX_PATH+1]; *ScenName=0;
|
|
|
|
InitRefSym(GfxR->fctMenu.GetPhase(0), LoadResStr("IDS_MENU_CPSAVEGAME"), iPlayer);
|
|
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
|
|
SetPermanent(true);
|
|
|
|
// target file name mask
|
|
// get folder & filename to store in
|
|
// some magic is needed to ensure savegames are stored properly into their folders
|
|
SCopy(GetFilename(Game.ScenarioFilename), DirPath);
|
|
if (DirPath[strlen(DirPath) - 1] == '\\') DirPath[strlen(DirPath) - 1] = 0;
|
|
RemoveExtension(DirPath);
|
|
if (LooksLikeInteger(DirPath))
|
|
{
|
|
// ScenTitle.ocf\%d.ocs-names (old-style savegames)
|
|
// get owning folder
|
|
if (Game.pParentGroup)
|
|
{
|
|
// owning folder determines filename
|
|
SCopy(GetFilenameOnly(Game.pParentGroup->GetName()), ScenName);
|
|
}
|
|
else
|
|
{
|
|
// no owning folder known: too bad
|
|
// make a vague guess based on the scenario title
|
|
SCopy(GetFilenameOnly(Game.ScenarioFilename), ScenName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// DirPath is a valid filename for now...
|
|
SCopy(DirPath, ScenName);
|
|
// but remove trailing numbers to adjust new-style savegames
|
|
char *pScenNameEnd = ScenName+SLen(ScenName);
|
|
while (Inside<char>(*--pScenNameEnd, '0', '9'))
|
|
if (pScenNameEnd == ScenName)
|
|
{
|
|
// should not happen: digit-only-filenames should have been caught earlier
|
|
SCopy("dbg_error!", ScenName);
|
|
pScenNameEnd = ScenName+SLen(ScenName)-1;
|
|
}
|
|
pScenNameEnd[1]=0;
|
|
}
|
|
|
|
// New Style 2007:
|
|
// * scenarios are saved into ScenName.ocf/ScenName123.ocs to keep umlauts out of filenames
|
|
// * language titles are stored in folders as title component
|
|
StdStrBuf strFilename, strTitle;
|
|
strFilename.Format("%s.ocf%c%s%%d.ocs", ScenName, DirectorySeparator, ScenName);
|
|
strTitle = Game.ScenarioTitle;
|
|
|
|
// Create menu items
|
|
StdStrBuf strFilenameIndexed, strCommand, strCaption, strSavePath;
|
|
for (int32_t i = 1; i <= 10; i++)
|
|
{
|
|
// Index filename
|
|
strFilenameIndexed.Format(strFilename.getData(), i);
|
|
// Compose commmand
|
|
strCommand.Format("Save:Game:%s:%s", strFilenameIndexed.getData(), strTitle.getData()); // Notice: the language title might contain ':' and thus confuse the segment list - but C4Menu::MenuCommand will try to handle this...
|
|
// Check free slot
|
|
strSavePath.Format("%s%c%s", Config.AtUserDataPath(C4CFN_Savegames), DirectorySeparator, strFilenameIndexed.getData());
|
|
bool fFree = !C4Group_IsGroup(strSavePath.getData());
|
|
// Item caption
|
|
strCaption = LoadResStr("IDS_MENU_CPSAVEGAME");
|
|
// add menu item
|
|
AddRefSym(strCaption.getData(), GfxR->fctMenu.GetPhase(i - 1, fFree ? 2 : 1), strCommand.getData(), C4MN_Item_NoCount, NULL, LoadResStr("IDS_MENU_CPSAVEGAMEINFO"));
|
|
}
|
|
|
|
// Go back to options menu on close
|
|
SetCloseCommand("ActivateMenu:Main");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::ActivateHost(int32_t iPlayer)
|
|
{
|
|
// Menu symbol/init
|
|
InitRefSym(C4GUI::Icon::GetIconFacet(C4GUI::Ico_Disconnect), LoadResStr("IDS_MENU_DISCONNECTCLIENT"), iPlayer, C4MN_Extra_None, 0, 0, C4MN_Style_Context);
|
|
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
|
|
SetPermanent(true);
|
|
// Clients
|
|
for (C4Network2Client *pClient=::Network.Clients.GetNextClient(NULL); pClient; pClient=::Network.Clients.GetNextClient(pClient))
|
|
{
|
|
bool fHost = (pClient->getID() == 0);
|
|
StdStrBuf strText, strCommand;
|
|
strText.Format("%s (%s)", pClient->getName(), pClient->getCore().getNick());
|
|
strCommand.Format("Host:Kick:%d", pClient->getID());
|
|
C4GUI::Icons iIcon = fHost ? C4GUI::Ico_Host : (pClient->isActivated() ? C4GUI::Ico_Client : C4GUI::Ico_ObserverClient);
|
|
AddRefSym(strText.getData(), C4GUI::Icon::GetIconFacet(iIcon), strCommand.getData());
|
|
}
|
|
// Go back to options menu on close
|
|
SetCloseCommand("ActivateMenu:Main");
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::ActivateClient(int32_t iPlayer)
|
|
{
|
|
// Menu symbol/init
|
|
C4FacetSurface fctSymbol;
|
|
InitRefSym(C4GUI::Icon::GetIconFacet(C4GUI::Ico_Disconnect), LoadResStr("IDS_MENU_DISCONNECTFROMSERVER"), iPlayer, C4MN_Extra_None, 0, 0, C4MN_Style_Context);
|
|
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
|
|
fctSymbol.Create(C4SymbolSize, C4SymbolSize); GfxR->fctOKCancel.Draw(fctSymbol,true,3,0);
|
|
Add(LoadResStr("IDS_BTN_YES"), fctSymbol, "Part");
|
|
fctSymbol.Create(C4SymbolSize, C4SymbolSize); GfxR->fctOKCancel.Draw(fctSymbol,true,1,0);
|
|
Add(LoadResStr("IDS_BTN_NO"), fctSymbol, "");
|
|
SetCloseCommand("ActivateMenu:Main");
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::ActivateSurrender(int32_t iPlayer)
|
|
{
|
|
C4FacetSurface fctSymbol;
|
|
InitRefSym(C4GUI::Icon::GetIconFacet(C4GUI::Ico_Surrender), LoadResStr("IDS_MENU_SURRENDER"), iPlayer, C4MN_Extra_None, 0, 0, C4MN_Style_Context);
|
|
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
|
|
fctSymbol.Create(C4SymbolSize, C4SymbolSize); GfxR->fctOKCancel.Draw(fctSymbol,true,3,0);
|
|
Add(LoadResStr("IDS_BTN_YES"), fctSymbol, "Surrender");
|
|
fctSymbol.Create(C4SymbolSize, C4SymbolSize); GfxR->fctOKCancel.Draw(fctSymbol,true,1,0);
|
|
Add(LoadResStr("IDS_BTN_NO"), fctSymbol, "");
|
|
SetCloseCommand("ActivateMenu:Main");
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::ActivateOptions(int32_t iPlayer, int32_t selection)
|
|
{
|
|
// Menu symbol/init
|
|
InitRefSym(GfxR->fctOptions.GetPhase(0), LoadResStr("IDS_MNU_OPTIONS"), iPlayer, C4MN_Extra_None, 0, 0, C4MN_Style_Context);
|
|
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
|
|
SetPermanent(true);
|
|
// Sound
|
|
AddRefSym(LoadResStr("IDS_DLG_SOUND"), GfxR->fctOptions.GetPhase(17 + Config.Sound.RXSound),"Options:Sound",C4MN_Item_NoCount);
|
|
// Music
|
|
AddRefSym(LoadResStr("IDS_MNU_MUSIC"), GfxR->fctOptions.GetPhase(1 + Config.Sound.RXMusic),"Options:Music",C4MN_Item_NoCount);
|
|
// Music
|
|
AddRefSym(LoadResStr("IDS_MENU_DISPLAY"), GfxR->fctMenu.GetPhase(8), "ActivateMenu:Display");
|
|
// Restore selection
|
|
SetSelection(selection, false, true);
|
|
// Go back to main menu on close
|
|
SetCloseCommand("ActivateMenu:Main");
|
|
// Done
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::ActivateDisplay(int32_t iPlayer, int32_t selection)
|
|
{
|
|
// Menu symbol/init
|
|
InitRefSym(GfxR->fctMenu.GetPhase(8), LoadResStr("IDS_MENU_DISPLAY"), iPlayer, C4MN_Extra_None, 0, 0, C4MN_Style_Context);
|
|
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
|
|
SetPermanent(true);
|
|
// Crew player names
|
|
AddRefSym(LoadResStr("IDS_MNU_PLAYERNAMES"), GfxR->fctOptions.GetPhase(7 + Config.Graphics.ShowCrewNames), "Display:PlayerNames", C4MN_Item_NoCount, NULL, LoadResStr("IDS_MENU_PLAYERNAMES_DESC"));
|
|
// Crew clonk names
|
|
AddRefSym(LoadResStr("IDS_MNU_CLONKNAMES"), GfxR->fctOptions.GetPhase(9 + Config.Graphics.ShowCrewCNames), "Display:ClonkNames", C4MN_Item_NoCount, NULL, LoadResStr("IDS_MENU_CLONKNAMES_DESC"));
|
|
// Upper Board
|
|
if (!Application.isEditor)
|
|
{
|
|
AddRefSym(LoadResStr("IDS_MNU_UPPERBOARD"),GfxR->fctOptions.GetPhase(3 + Config.Graphics.UpperBoard),"Display:UpperBoard",C4MN_Item_NoCount);
|
|
}
|
|
// FPS
|
|
if (!Application.isEditor)
|
|
AddRefSym(LoadResStr("IDS_MNU_FPS"), GfxR->fctOptions.GetPhase(5 + Config.General.FPS), "Display:FPS", C4MN_Item_NoCount);
|
|
// Clock
|
|
if (!Application.isEditor)
|
|
AddRefSym(LoadResStr("IDS_MNU_CLOCK"), GfxR->fctOptions.GetPhase(15 + Config.Graphics.ShowClock), "Display:Clock", C4MN_Item_NoCount);
|
|
// Restore selection
|
|
SetSelection(selection, false, true);
|
|
// Go back to options menu on close
|
|
SetCloseCommand("ActivateMenu:Options");
|
|
// Done
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::ActivateMain(int32_t iPlayer)
|
|
{
|
|
// Determine player
|
|
C4Player *pPlr = ::Players.Get(iPlayer);
|
|
// Menu symbol/init
|
|
C4FacetSurface fctSymbol;
|
|
fctSymbol.Create(C4SymbolSize, C4SymbolSize);
|
|
GfxR->fctOKCancel.Draw(fctSymbol, true, 1, 1);
|
|
Init(fctSymbol, LoadResStr(pPlr ? "IDS_MENU_CPMAIN" : "IDS_MENU_OBSERVER"), iPlayer, C4MN_Extra_None, 0, 0, C4MN_Style_Context);
|
|
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
|
|
// Goals+Rules (player menu only)
|
|
// Goal menu can't be shown because of script callbacks
|
|
// Rule menu could be shown, but rule activation would issue script callbacks and trigger client activation
|
|
// Showing rules but not showing goals would be strange anyway
|
|
if (pPlr)
|
|
{
|
|
// Goals
|
|
AddRefSym(LoadResStr("IDS_MENU_CPGOALS"),GfxR->fctMenu.GetPhase(4),"ActivateMenu:Goals",C4MN_Item_NoCount,NULL,LoadResStr("IDS_MENU_CPGOALSINFO"));
|
|
// Rules
|
|
AddRefSym(LoadResStr("IDS_MENU_CPRULES"),GfxR->fctMenu.GetPhase(5),"ActivateMenu:Rules",C4MN_Item_NoCount,NULL,LoadResStr("IDS_MENU_CPRULESINFO"));
|
|
}
|
|
// Observer menu in free viewport
|
|
if (!pPlr)
|
|
{
|
|
AddRefSym(LoadResStr("IDS_TEXT_VIEW"),C4GUI::Icon::GetIconFacet(C4GUI::Ico_View),"ActivateMenu:Observer",C4MN_Item_NoCount,NULL,LoadResStr("IDS_TEXT_DETERMINEPLAYERVIEWTOFOLL"));
|
|
}
|
|
// Hostility (player menu only)
|
|
if (pPlr && (::Players.GetCount() > 1))
|
|
{
|
|
GfxR->fctFlagClr.Surface->SetClr(0xff0000);
|
|
AddRefSym(LoadResStr("IDS_MENU_CPATTACK"),GfxR->fctMenu.GetPhase(7),"ActivateMenu:Hostility",C4MN_Item_NoCount,NULL,LoadResStr("IDS_MENU_CPATTACKINFO"));
|
|
}
|
|
// Team change
|
|
if (pPlr && Game.Teams.IsTeamSwitchAllowed())
|
|
{
|
|
C4Facet fctTeams; fctTeams = C4GUI::Icon::GetIconFacet(C4GUI::Ico_Team);
|
|
AddRefSym(LoadResStr("IDS_MSG_SELTEAM"),fctTeams,"ActivateMenu:TeamSel",C4MN_Item_NoCount,NULL,LoadResStr("IDS_MSG_ALLOWSYOUTOJOINADIFFERENT"));
|
|
}
|
|
// Player join
|
|
if ((::Players.GetCount() < Game.Parameters.MaxPlayers) && !Game.Parameters.isLeague())
|
|
{
|
|
AddRefSym(LoadResStr("IDS_MENU_CPNEWPLAYER"),GfxR->fctPlayerClr.GetPhase(),"ActivateMenu:NewPlayer",C4MN_Item_NoCount,NULL,LoadResStr("IDS_MENU_CPNEWPLAYERINFO"));
|
|
}
|
|
// Save game (player menu only - should we allow saving games with no players in it?)
|
|
if (pPlr && (!::Network.isEnabled() || ::Network.isHost()))
|
|
{
|
|
AddRefSym(LoadResStr("IDS_MENU_CPSAVEGAME"),GfxR->fctMenu.GetPhase(0),"ActivateMenu:Save:Game",C4MN_Item_NoCount,NULL,LoadResStr("IDS_MENU_CPSAVEGAMEINFO"));
|
|
}
|
|
// Options
|
|
AddRefSym(LoadResStr("IDS_MNU_OPTIONS"), GfxR->fctOptions.GetPhase(0), "ActivateMenu:Options",C4MN_Item_NoCount, NULL, LoadResStr("IDS_MNU_OPTIONSINFO"));
|
|
// Disconnect
|
|
if (::Network.isEnabled())
|
|
{
|
|
// Host
|
|
if (::Network.isHost() && Game.Clients.getClient(NULL))
|
|
AddRefSym(LoadResStr("IDS_MENU_DISCONNECT"), C4GUI::Icon::GetIconFacet(C4GUI::Ico_Disconnect), "ActivateMenu:Host", C4MN_Item_NoCount, NULL, LoadResStr("IDS_TEXT_KICKCERTAINCLIENTSFROMTHE"));
|
|
// Client
|
|
if (!::Network.isHost())
|
|
AddRefSym(LoadResStr("IDS_MENU_DISCONNECT"), C4GUI::Icon::GetIconFacet(C4GUI::Ico_Disconnect), "ActivateMenu:Client", C4MN_Item_NoCount, NULL, LoadResStr("IDS_TEXT_DISCONNECTTHEGAMEFROMTHES"));
|
|
}
|
|
// Surrender (player menu only)
|
|
if (pPlr)
|
|
AddRefSym(LoadResStr("IDS_MENU_CPSURRENDER"), C4GUI::Icon::GetIconFacet(C4GUI::Ico_Surrender), "ActivateMenu:Surrender", C4MN_Item_NoCount, NULL, LoadResStr("IDS_MENU_CPSURRENDERINFO"));
|
|
// Abort
|
|
if (!Application.isEditor)
|
|
AddRefSym(LoadResStr("IDS_MENU_ABORT"), C4GUI::Icon::GetIconFacet(C4GUI::Ico_Exit), "Abort", C4MN_Item_NoCount, NULL, LoadResStr("IDS_MENU_ABORT_DESC"));
|
|
// No empty menus
|
|
if (GetItemCount()==0) Close(false);
|
|
// Done
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::ActivateHostility(int32_t iPlayer)
|
|
{
|
|
// Init menu
|
|
C4FacetSurface fctSymbol;
|
|
fctSymbol.Create(C4SymbolSize, C4SymbolSize);
|
|
GfxR->fctMenu.GetPhase(7).Draw(fctSymbol);
|
|
Init(fctSymbol, LoadResStr("IDS_MENU_CPATTACK"), iPlayer, C4MN_Extra_None, 0, C4MN_Hostility);
|
|
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
|
|
SetPermanent(true);
|
|
Refill();
|
|
// Go back to options menu on close
|
|
SetCloseCommand("ActivateMenu:Main");
|
|
return true;
|
|
}
|
|
|
|
bool C4MainMenu::MenuCommand(const char *szCommand, bool fIsCloseCommand)
|
|
{
|
|
// Determine player
|
|
C4Player *pPlr = ::Players.Get(Player);
|
|
// Activate
|
|
if (SEqual2(szCommand,"ActivateMenu:"))
|
|
{
|
|
if (C4GameOverDlg::IsShown()) return false; // no new menus during game over dlg
|
|
if (SEqual(szCommand+13,"Main")) return ActivateMain(Player);
|
|
if (SEqual(szCommand+13,"Hostility")) return ActivateHostility(Player);
|
|
if (SEqual(szCommand+13,"NewPlayer")) return ActivateNewPlayer(Player);
|
|
if (SEqual(szCommand+13,"Goals"))
|
|
{
|
|
::Control.DoInput(CID_Script, new C4ControlScript(FormatString("ActivateGameGoalMenu(%d)", Player).getData()), CDT_Queue);
|
|
return true;
|
|
}
|
|
if (SEqual(szCommand+13,"Rules")) return ActivateRules(Player);
|
|
if (SEqual(szCommand+13,"Host")) return ActivateHost(Player);
|
|
if (SEqual(szCommand+13,"Client")) return ActivateClient(Player);
|
|
if (SEqual(szCommand+13,"Options")) return ActivateOptions(Player);
|
|
if (SEqual(szCommand+13,"Display")) return ActivateDisplay(Player);
|
|
if (SEqual(szCommand+13,"Save:Game")) return ActivateSavegame(Player);
|
|
if (SEqual(szCommand+13,"TeamSel")) return pPlr ? pPlr->ActivateMenuTeamSelection(true) : false;
|
|
if (SEqual(szCommand+13,"Surrender")) return ActivateSurrender(Player);
|
|
if (SEqual(szCommand+13,"Observer")) return ActivateObserver();
|
|
}
|
|
// JoinPlayer
|
|
if (SEqual2(szCommand,"JoinPlayer:"))
|
|
{
|
|
// not in league or replay mode
|
|
if (Game.Parameters.isLeague() || Game.C4S.Head.Replay) return false;
|
|
// join player
|
|
// 2do: not for observers and such?
|
|
Players.JoinNew(szCommand+11);
|
|
return true;
|
|
}
|
|
// SetHostility
|
|
if (SEqual2(szCommand,"SetHostility:"))
|
|
{
|
|
// only if allowed
|
|
if (!Game.Teams.IsHostilityChangeAllowed()) return false;
|
|
int32_t iOpponent; sscanf(szCommand+13,"%i",&iOpponent);
|
|
C4Player *pOpponent = ::Players.Get(iOpponent);
|
|
if (!pOpponent || pOpponent->GetType() != C4PT_User) return false;
|
|
// TODO: doesn't really work
|
|
Game.Input.Add(CID_Script, new C4ControlScript(FormatString("SetHostility(%d, %d, !Hostile(%d, %d, true))", Player, iOpponent, Player, iOpponent).getData(), C4ControlScript::SCOPE_Global, true));
|
|
return true;
|
|
}
|
|
// Abort
|
|
if (SEqual2(szCommand,"Abort"))
|
|
{
|
|
FullScreen.ShowAbortDlg();
|
|
return true;
|
|
}
|
|
// Surrender
|
|
if (SEqual2(szCommand,"Surrender"))
|
|
{
|
|
::Control.DoInput(CID_Script, new C4ControlScript(FormatString("SurrenderPlayer(%d)", Player).getData()), CDT_Queue);
|
|
return true;
|
|
}
|
|
// Save game
|
|
if (SEqual2(szCommand, "Save:Game:"))
|
|
{
|
|
char strFilename[_MAX_PATH + 1]; SCopySegment(szCommand, 2, strFilename, ':', _MAX_PATH);
|
|
char strTitle[_MAX_PATH + 1]; SCopy(szCommand + SCharPos(':', szCommand, 2) + 1, strTitle, _MAX_PATH);
|
|
Game.QuickSave(strFilename, strTitle);
|
|
ActivateSavegame(Player);
|
|
return true;
|
|
}
|
|
// Kick
|
|
if (SEqual2(szCommand,"Host:Kick:"))
|
|
{
|
|
int iClientID = atoi(szCommand+10);
|
|
if (iClientID && ::Network.isEnabled())
|
|
{
|
|
if (Game.Parameters.isLeague() && ::Players.GetAtClient(iClientID))
|
|
::Network.Vote(VT_Kick, true, iClientID);
|
|
else
|
|
{
|
|
C4Client *pClient = Game.Clients.getClientByID(iClientID);
|
|
if (pClient) Game.Clients.CtrlRemove(pClient, LoadResStr("IDS_MSG_KICKBYMENU"));
|
|
Close(true);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
// Part
|
|
if (SEqual2(szCommand,"Part"))
|
|
{
|
|
if (::Network.isEnabled())
|
|
{
|
|
if (Game.Parameters.isLeague() && ::Players.GetLocalByIndex(0))
|
|
::Network.Vote(VT_Kick, true, ::Control.ClientID());
|
|
else
|
|
{
|
|
Game.RoundResults.EvaluateNetwork(C4RoundResults::NR_NetError, LoadResStr("IDS_ERR_GAMELEFTVIAPLAYERMENU"));
|
|
::Network.Clear();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
// Options
|
|
if (SEqual2(szCommand,"Options:"))
|
|
{
|
|
// Music
|
|
if (SEqual(szCommand + 8, "Music"))
|
|
{
|
|
Application.MusicSystem.ToggleOnOff();
|
|
}
|
|
// Sound
|
|
if (SEqual(szCommand + 8, "Sound"))
|
|
{
|
|
if (Config.Sound.RXSound)
|
|
{
|
|
Application.SoundSystem.Clear();
|
|
Config.Sound.RXSound = false;
|
|
}
|
|
else
|
|
{
|
|
Config.Sound.RXSound = true;
|
|
if (!Application.SoundSystem.Init())
|
|
{ Log(LoadResStr("IDS_PRC_NOSND")); }
|
|
}
|
|
}
|
|
// Reopen with updated options
|
|
ActivateOptions(Player, GetSelection());
|
|
return true;
|
|
}
|
|
// Display
|
|
if (SEqual2(szCommand,"Display:"))
|
|
{
|
|
// Upper board
|
|
if (SEqual(szCommand + 8, "UpperBoard"))
|
|
{
|
|
Config.Graphics.UpperBoard = !Config.Graphics.UpperBoard;
|
|
::Viewports.RecalculateViewports();
|
|
}
|
|
// FPS
|
|
if (SEqual(szCommand + 8, "FPS")) Config.General.FPS = !Config.General.FPS;
|
|
// Player names
|
|
if (SEqual(szCommand + 8, "PlayerNames")) Config.Graphics.ShowCrewNames = !Config.Graphics.ShowCrewNames;
|
|
// Clonk names
|
|
if (SEqual(szCommand + 8, "ClonkNames")) Config.Graphics.ShowCrewCNames = !Config.Graphics.ShowCrewCNames;
|
|
// Clock
|
|
if (SEqual(szCommand + 8, "Clock")) Config.Graphics.ShowClock = !Config.Graphics.ShowClock;
|
|
// Reopen with updated options
|
|
ActivateDisplay(Player, GetSelection());
|
|
return true;
|
|
}
|
|
// Goal info
|
|
if (SEqual2(szCommand,"Player:Goal:") || SEqual2(szCommand,"Player:Rule:"))
|
|
{
|
|
if (!ValidPlr(Player)) return false; // observers may not look at goal/rule info, because it requires queue activation
|
|
Close(true);
|
|
C4Object *pObj; C4ID idItem(szCommand+12);
|
|
if ((pObj = ::Objects.Find(idItem)))
|
|
::Control.DoInput(CID_Script, new C4ControlScript(FormatString("Activate(%d)", Player).getData(), pObj->Number), CDT_Queue);
|
|
else
|
|
return false;
|
|
return true;
|
|
}
|
|
// Team selection
|
|
if (SEqual2(szCommand, "TeamSel:"))
|
|
{
|
|
Close(true);
|
|
int32_t idTeam = atoi(szCommand+8);
|
|
|
|
// OK, join this team
|
|
if (pPlr) pPlr->DoTeamSelection(idTeam);
|
|
return true;
|
|
}
|
|
// Team switch
|
|
if (SEqual2(szCommand, "TeamSwitch:"))
|
|
{
|
|
Close(true);
|
|
int32_t idTeam = atoi(szCommand+11);
|
|
|
|
// check if it's still allowed
|
|
if (!Game.Teams.IsTeamSwitchAllowed()) return false;
|
|
// OK, join this team
|
|
::Control.DoInput(CID_Script, new C4ControlScript(FormatString("SetPlayerTeam(%d,%d)", (int)Player, (int)idTeam).getData()), CDT_Queue);
|
|
return true;
|
|
}
|
|
// Observe
|
|
if (SEqual2(szCommand, "Observe:"))
|
|
{
|
|
const char *szObserverTarget = szCommand+8;
|
|
C4Viewport *pVP = ::Viewports.GetViewport(NO_OWNER);
|
|
if (pVP) // viewport may have closed meanwhile
|
|
{
|
|
if (SEqual(szObserverTarget, "Free"))
|
|
{
|
|
// free view
|
|
pVP->Init(NO_OWNER, true);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// view following player
|
|
int32_t iPlr = atoi(szObserverTarget);
|
|
if (ValidPlr(iPlr))
|
|
{
|
|
pVP->Init(iPlr, true);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
// No valid command
|
|
return false;
|
|
}
|
|
|
|
bool C4MainMenu::ActivateCommand(int32_t iPlayer, const char *szCommand)
|
|
{
|
|
// init menu for player and activate with command
|
|
Player = iPlayer;
|
|
return MenuCommand(szCommand, false);
|
|
}
|
|
|
|
bool C4MainMenu::ActivateObserver()
|
|
{
|
|
// Safety: Viewport lost?
|
|
if (!::Viewports.GetViewport(NO_OWNER)) return false;
|
|
// Menu symbol/init
|
|
InitRefSym(C4GUI::Icon::GetIconFacet(C4GUI::Ico_View), LoadResStr("IDS_TEXT_VIEW"), NO_OWNER, C4MN_Extra_None, 0, C4MN_Observer, C4MN_Style_Context);
|
|
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
|
|
// Players added in Refill
|
|
Refill();
|
|
// Go back to main menu on close
|
|
SetCloseCommand("ActivateMenu:Main");
|
|
return true;
|
|
}
|