openclonk/src/gui/C4MainMenu.cpp

872 lines
29 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 1998-2000, Matthes Bender
* Copyright (c) 2008-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2009-2013, 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.
*/
// Engine internal C4Menus: Main menu, Options, Player join, Hostility, etc.
#include <C4Include.h>
#include <C4MainMenu.h>
#include <C4Application.h>
#include <C4DefList.h>
#include <C4FullScreen.h>
#include <C4Viewport.h>
#include <C4Player.h>
#include <C4GameOverDlg.h>
#include <C4GraphicsResource.h>
#include <C4Game.h>
#include <C4PlayerList.h>
#include <C4GameObjects.h>
#include <C4Network2.h>
#include <C4GameControl.h>
#include <C4Def.h>
// -----------------------------------------------------------
// C4MainMenu
C4MainMenu::C4MainMenu() : C4Menu() // will be re-adjusted later
{
Clear();
}
void C4MainMenu::Default()
{
C4Menu::Default();
Player=NO_OWNER;
}
bool C4MainMenu::InitRefSym(const C4Facet &fctSymbol, const char *szEmpty, int32_t iPlayer, int32_t iId, int32_t iStyle)
{
if (!DoInitRefSym(fctSymbol, szEmpty, 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_CPNEWPLAYER"), iPlayer, 0, C4MN_Style_Context);
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; }
// Load custom portrait
C4FacetSurface fctSymbol;
if (!fctSymbol.Load(hGroup, C4CFN_BigIcon, C4FCT_Full, C4FCT_Full, false, true))
fctSymbol.Load(hGroup, C4CFN_BigIcon, C4FCT_Full, C4FCT_Full, false, true);
// Close group
hGroup.Close();
// Add player item
sprintf(szCommand, "JoinPlayer:%s", szFilename);
StdStrBuf sItemText;
sItemText.Format(LoadResStr("IDS_MENU_NEWPLAYER"), C4P.PrefName);
// No custom portrait: use default player image
if (!fctSymbol.Surface)
{
fctSymbol.Create(C4SymbolSize, C4SymbolSize);
GfxR->fctPlayerClr.DrawClr(fctSymbol, true, C4P.PrefColorDw);
}
// 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,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)
{
fHasIcon = Game.DrawTextSpecImage(fctSymbol, szIconSpec, NULL, pTeam->GetColor());
}
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(),
FormatString(LoadResStr("IDS_MSG_JOINTEAM"), sTeamName.getData()).getData());
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", 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(),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)
{
// direct menu control
// but ensure selection is Okay before
SetSelection(iIndex, true, false);
Enter();
}
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(),"");
}
// 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,"");
}
// 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(), 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, 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)
{
InitRefSym(C4GUI::Icon::GetIconFacet(C4GUI::Ico_Disconnect), LoadResStr("IDS_MENU_DISCONNECTFROMSERVER"), iPlayer, 0, C4MN_Style_Context);
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
AddRefSym(LoadResStr("IDS_BTN_YES"), GfxR->fctOKCancel.GetPhase(3,0), "Part");
AddRefSym(LoadResStr("IDS_BTN_NO"), C4GUI::Icon::GetIconFacet(C4GUI::Ico_Close), "");
SetCloseCommand("ActivateMenu:Main");
return true;
}
bool C4MainMenu::ActivateSurrender(int32_t iPlayer)
{
InitRefSym(C4GUI::Icon::GetIconFacet(C4GUI::Ico_Surrender), LoadResStr("IDS_MENU_SURRENDER"), iPlayer, 0, C4MN_Style_Context);
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
AddRefSym(LoadResStr("IDS_BTN_YES"), GfxR->fctOKCancel.GetPhase(3), "Surrender");
AddRefSym(LoadResStr("IDS_BTN_NO"), C4GUI::Icon::GetIconFacet(C4GUI::Ico_Close), "");
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, 0, C4MN_Style_Context);
SetAlignment(C4MN_Align_Left | C4MN_Align_Bottom);
SetPermanent(true);
AddRefSym(LoadResStr("IDS_DLG_SOUND"), GfxR->fctOptions.GetPhase(17 + Config.Sound.RXSound),"Options:Sound");
AddRefSym(LoadResStr("IDS_MNU_MUSIC"), GfxR->fctOptions.GetPhase(1 + Config.Sound.RXMusic),"Options:Music");
AddRefSym(LoadResStr("IDS_MENU_DISPLAY"), C4GUI::Icon::GetIconFacet(C4GUI::Ico_View), "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(C4GUI::Icon::GetIconFacet(C4GUI::Ico_View), LoadResStr("IDS_MENU_DISPLAY"), iPlayer, 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", LoadResStr("IDS_MENU_PLAYERNAMES_DESC"));
// Crew clonk names
AddRefSym(LoadResStr("IDS_MNU_CLONKNAMES"), GfxR->fctOptions.GetPhase(9 + Config.Graphics.ShowCrewCNames), "Display:ClonkNames", LoadResStr("IDS_MENU_CLONKNAMES_DESC"));
// Upper Board
if (!Application.isEditor)
{
AddRefSym(LoadResStr("IDS_MNU_UPPERBOARD"),GfxR->fctOptions.GetPhase(3 + Config.Graphics.UpperBoard),"Display:UpperBoard");
}
// FPS
if (!Application.isEditor)
AddRefSym(LoadResStr("IDS_MNU_FPS"), GfxR->fctOptions.GetPhase(5 + Config.General.FPS), "Display:FPS");
// Clock
if (!Application.isEditor)
AddRefSym(LoadResStr("IDS_MNU_CLOCK"), GfxR->fctOptions.GetPhase(15 + Config.Graphics.ShowClock), "Display:Clock");
// 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
InitRefSym(C4GUI::Icon::GetIconFacet(C4GUI::Ico_Player), LoadResStr(pPlr ? "IDS_MENU_CPMAIN" : "IDS_MENU_OBSERVER"), iPlayer, 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",LoadResStr("IDS_MENU_CPGOALSINFO"));
// Rules
AddRefSym(LoadResStr("IDS_MENU_CPRULES"),GfxR->fctMenu.GetPhase(5),"ActivateMenu:Rules",LoadResStr("IDS_MENU_CPRULESINFO"));
}
// Observer menu in free viewport
if (!pPlr)
{
AddRefSym(LoadResStr("IDS_TEXT_VIEW"),C4GUI::Icon::GetIconFacet(C4GUI::Ico_View),"ActivateMenu:Observer",LoadResStr("IDS_TEXT_DETERMINEPLAYERVIEWTOFOLL"));
}
// Hostility (player menu only)
if (pPlr && (::Players.GetCount() > 1))
{
GfxR->fctFlagClr.Surface->SetClr(0xff0000);
AddRefSym(LoadResStr("IDS_MENU_CPATTACK"),C4GUI::Icon::GetIconFacet(C4GUI::Ico_MeleeLeague),"ActivateMenu:Hostility",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",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",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",LoadResStr("IDS_MENU_CPSAVEGAMEINFO"));
}
// Options
AddRefSym(LoadResStr("IDS_MNU_OPTIONS"), GfxR->fctOptions.GetPhase(0), "ActivateMenu:Options", 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", LoadResStr("IDS_TEXT_KICKCERTAINCLIENTSFROMTHE"));
// Client
if (!::Network.isHost())
AddRefSym(LoadResStr("IDS_MENU_DISCONNECT"), C4GUI::Icon::GetIconFacet(C4GUI::Ico_Disconnect), "ActivateMenu:Client", LoadResStr("IDS_TEXT_DISCONNECTTHEGAMEFROMTHES"));
}
// Surrender (player menu only)
if (pPlr)
AddRefSym(LoadResStr("IDS_MENU_CPSURRENDER"), C4GUI::Icon::GetIconFacet(C4GUI::Ico_Surrender), "ActivateMenu:Surrender", LoadResStr("IDS_MENU_CPSURRENDERINFO"));
// Abort
if (!Application.isEditor)
AddRefSym(LoadResStr("IDS_MENU_ABORT"), C4GUI::Icon::GetIconFacet(C4GUI::Ico_Exit), "Abort", LoadResStr("IDS_MENU_ABORT_DESC"));
// No empty menus
if (GetItemCount()==0) Close(false);
// Done
return true;
}
bool C4MainMenu::ActivateHostility(int32_t iPlayer)
{
// Init menu
InitRefSym(GfxR->fctMenu.GetPhase(7), LoadResStr("IDS_MENU_CPATTACK"), iPlayer, 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_PlrAction, C4ControlPlayerAction::ActivateGoalMenu(::Players.Get(Player)), 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;
::Control.DoInput(CID_PlrAction, C4ControlPlayerAction::SetHostility(::Players.Get(Player), pOpponent, !::Players.HostilityDeclared(Player, pOpponent->Number)), CDT_Queue);
return true;
}
// Abort
if (SEqual2(szCommand,"Abort"))
{
FullScreen.ShowAbortDlg();
return true;
}
// Surrender
if (SEqual2(szCommand,"Surrender"))
{
::Control.DoInput(CID_PlrAction, C4ControlPlayerAction::Surrender(::Players.Get(Player)), 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); C4Def * pDef = C4Id2Def(idItem);
if (pDef && (pObj = ::Objects.Find(pDef)))
::Control.DoInput(CID_PlrAction, C4ControlPlayerAction::ActivateGoal(::Players.Get(Player), pObj), 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_PlrAction, C4ControlPlayerAction::SetTeam(::Players.Get(Player), idTeam), 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_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;
}