Serious-Engine/Sources/SeriousSam/MenuGadgets.cpp

1928 lines
54 KiB
C++
Raw Normal View History

2016-03-12 01:20:51 +01:00
/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
2016-03-11 14:57:17 +01:00
#include "StdH.h"
#include <Engine/Base/KeyNames.h>
#include <Engine/CurrentVersion.h>
#include "MenuPrinting.h"
#include <GameMP/LCDDrawing.h>
#include "LevelInfo.h"
#include "VarList.h"
extern CFontData _fdBig;
extern CFontData _fdMedium;
extern CFontData _fdSmall;
extern CSoundData *_psdSelect;
CMenuGadget *_pmgLastActivatedGadget = NULL;
extern CSoundData *_psdPress;
extern PIX _pixCursorPosI;
extern PIX _pixCursorPosJ;
extern BOOL _bDefiningKey = FALSE;
extern BOOL _bEditingString = FALSE;
extern INDEX sam_bWideScreen;
CMenuGadget::CMenuGadget( void)
{
mg_pmgLeft = NULL;
mg_pmgRight = NULL;
mg_pmgUp = NULL;
mg_pmgDown = NULL;
mg_bVisible = TRUE;
mg_bEnabled = TRUE;
mg_bLabel = FALSE;
mg_bFocused = FALSE;
mg_iInList = -1; // not in list
}
void CMenuGadget::OnActivate( void)
{
NOTHING;
}
// return TRUE if handled
BOOL CMenuGadget::OnKeyDown( int iVKey)
{
// if return pressed
if( iVKey==VK_RETURN || iVKey==VK_LBUTTON) {
// activate
OnActivate();
// key is handled
return TRUE;
}
// key is not handled
return FALSE;
}
BOOL CMenuGadget::OnChar(MSG msg)
{
// key is not handled
return FALSE;
}
void CMenuGadget::OnSetFocus( void)
{
mg_bFocused = TRUE;
if( !IsSeparator())
{
PlayMenuSound(_psdSelect);
IFeel_PlayEffect("Menu_select");
}
}
void CMenuGadget::OnKillFocus( void)
{
mg_bFocused = FALSE;
}
void CMenuGadget::Appear( void)
{
mg_bVisible = TRUE;
}
void CMenuGadget::Disappear( void)
{
mg_bVisible = FALSE;
mg_bFocused = FALSE;
}
void CMenuGadget::Think( void)
{
}
void CMenuGadget::OnMouseOver(PIX pixI, PIX pixJ)
{
}
// get current color for the gadget
COLOR CMenuGadget::GetCurrentColor(void)
{
// use normal colors
COLOR colUnselected = LCDGetColor(C_GREEN, "unselected");
COLOR colSelected = LCDGetColor(C_WHITE, "selected");
// if disabled
if (!mg_bEnabled) {
// use a bit darker colors
colUnselected = LCDGetColor(C_dGREEN, "disabled unselected");
colSelected = LCDGetColor(C_GRAY, "disabled selected");
// if label
if (mg_bLabel) {
// use white
colUnselected = colSelected = LCDGetColor(C_WHITE, "label");
}
}
// use unselected color
COLOR colRet = colUnselected;
// if selected
if( mg_bFocused) {
// oscilate towards selected color
FLOAT tmNow = _pTimer->GetHighPrecisionTimer().GetSeconds();
colRet = LerpColor( (colUnselected>>1)&0x7F7F7F7F, colSelected, sin(tmNow*10.0f)*0.5f+0.5f);
}
return colRet|CT_OPAQUE;
}
void CMenuGadget::Render( CDrawPort *pdp)
{
}
void CMGTitle::Render( CDrawPort *pdp)
{
SetFontTitle(pdp);
PIXaabbox2D box = FloatBoxToPixBox(pdp, mg_boxOnScreen);
PIX pixI = box.Center()(1);
PIX pixJ = box.Min()(2);
pdp->PutTextC( mg_strText, pixI, pixJ, LCDGetColor(C_WHITE|CT_OPAQUE, "title"));
}
CMGButton::CMGButton( void)
{
mg_pActivatedFunction = NULL;
mg_iIndex = 0;
mg_iCenterI = 0;
mg_iTextMode = 1;
mg_bfsFontSize = BFS_MEDIUM;
mg_iCursorPos = -1;
mg_bRectangle = FALSE;
mg_bMental = FALSE;
}
void CMGButton::SetText( CTString strNew)
{
mg_strText = strNew;
}
void CMGButton::OnActivate( void)
{
if( mg_pActivatedFunction!=NULL && mg_bEnabled)
{
PlayMenuSound(_psdPress);
IFeel_PlayEffect("Menu_press");
_pmgLastActivatedGadget = this;
(*mg_pActivatedFunction)();
}
}
BOOL CMGVarButton::IsSeparator(void)
{
if( mg_pvsVar==NULL) return FALSE;
return mg_pvsVar->vs_bSeparator;
}
BOOL CMGVarButton::IsEnabled(void)
{
return( _gmRunningGameMode==GM_NONE
|| mg_pvsVar==NULL
|| mg_pvsVar->vs_bCanChangeInGame);
}
void CMGButton::Render( CDrawPort *pdp)
{
if (mg_bfsFontSize==BFS_LARGE) {
SetFontBig(pdp);
} else if (mg_bfsFontSize==BFS_MEDIUM) {
SetFontMedium(pdp);
} else {
ASSERT(mg_bfsFontSize==BFS_SMALL);
SetFontSmall(pdp);
}
pdp->SetTextMode(mg_iTextMode);
PIXaabbox2D box = FloatBoxToPixBox(pdp, mg_boxOnScreen);
COLOR col = GetCurrentColor();
if(mg_bEditing) {
col = LCDGetColor(C_GREEN|0xFF, "editing");
}
COLOR colRectangle = col;
if( mg_bHighlighted) {
col = LCDGetColor(C_WHITE|0xFF, "hilited");
if( !mg_bFocused) {
colRectangle = LCDGetColor(C_WHITE|0xFF, "hilited rectangle");
}
}
if (mg_bMental) {
FLOAT tmIn = 0.2f;
FLOAT tmOut = 1.0f;
FLOAT tmFade = 0.1f;
FLOAT tmExist = tmFade+tmIn+tmFade;
FLOAT tmTotal = tmFade+tmIn+tmFade+tmOut;
FLOAT tmTime = _pTimer->GetHighPrecisionTimer().GetSeconds();
FLOAT fFactor = 1;
if (tmTime>0.1f) {
tmTime = fmod(tmTime, tmTotal);
fFactor = CalculateRatio(tmTime, 0, tmExist, tmFade/tmExist, tmFade/tmExist);
}
col = (col&~0xFF)|INDEX(0xFF*fFactor);
}
if( mg_bRectangle) {
// put border
const PIX pixLeft = box.Min()(1);
const PIX pixUp = box.Min()(2)-3;
const PIX pixWidth = box.Size()(1)+1;
const PIX pixHeight = box.Size()(2);
pdp->DrawBorder( pixLeft, pixUp, pixWidth, pixHeight, colRectangle);
}
if( mg_bEditing) {
// put border
PIX pixLeft = box.Min()(1);
PIX pixUp = box.Min()(2)-3;
PIX pixWidth = box.Size()(1)+1;
PIX pixHeight = box.Size()(2);
if (mg_strLabel!="") {
pixLeft = box.Min()(1)+box.Size()(1)*0.55f;
pixWidth = box.Size()(1)*0.45f+1;
}
pdp->Fill( pixLeft, pixUp, pixWidth, pixHeight, LCDGetColor(C_dGREEN|0x40, "edit fill"));
}
INDEX iCursor = mg_iCursorPos;
// print text
if (mg_strLabel!="") {
PIX pixIL = box.Min()(1)+box.Size()(1)*0.45f;
PIX pixIR = box.Min()(1)+box.Size()(1)*0.55f;
PIX pixJ = box.Min()(2);
pdp->PutTextR( mg_strLabel, pixIL, pixJ, col);
pdp->PutText( mg_strText, pixIR, pixJ, col);
} else {
CTString str = mg_strText;
if (pdp->dp_FontData->fd_bFixedWidth) {
str = str.Undecorated();
INDEX iLen = str.Length();
INDEX iMaxLen = ClampDn(box.Size()(1)/(pdp->dp_pixTextCharSpacing+pdp->dp_FontData->fd_pixCharWidth), 1L);
if (iCursor>=iMaxLen) {
str.TrimRight(iCursor);
str.TrimLeft(iMaxLen);
iCursor = iMaxLen;
} else {
str.TrimRight(iMaxLen);
}
}
if( mg_iCenterI==-1) pdp->PutText( str, box.Min()(1), box.Min()(2), col);
else if( mg_iCenterI==+1) pdp->PutTextR( str, box.Max()(1), box.Min()(2), col);
else pdp->PutTextC( str, box.Center()(1), box.Min()(2), col);
}
// put cursor if editing
if( mg_bEditing && (((ULONG)(_pTimer->GetRealTimeTick()*2))&1)) {
PIX pixX = box.Min()(1) + GetCharOffset( pdp, iCursor);
if (mg_strLabel!="") {
pixX += box.Size()(1)*0.55f;
}
PIX pixY = box.Min()(2);
if (!pdp->dp_FontData->fd_bFixedWidth) {
pixY -= pdp->dp_fTextScaling *2;
}
pdp->PutText( "|", pixX, pixY, LCDGetColor(C_WHITE|0xFF, "editing cursor"));
}
}
PIX CMGButton::GetCharOffset( CDrawPort *pdp, INDEX iCharNo)
{
if (pdp->dp_FontData->fd_bFixedWidth) {
return (pdp->dp_FontData->fd_pixCharWidth+pdp->dp_pixTextCharSpacing)*(iCharNo-0.5f);
}
CTString strCut(mg_strText);
strCut.TrimLeft( strlen(mg_strText)-iCharNo);
PIX pixFullWidth = pdp->GetTextWidth(mg_strText);
PIX pixCutWidth = pdp->GetTextWidth(strCut);
// !!!! not implemented for different centering
return pixFullWidth-pixCutWidth;
}
CMGModel::CMGModel(void)
{
mg_fFloorY = 0;
}
void CMGModel::Render( CDrawPort *pdp)
{
// if no model
if (mg_moModel.GetData()==NULL) {
// just render text
mg_strText = TRANS("No model");
CMGButton::Render(pdp);
return;
}
// get position on screen
PIXaabbox2D box = FloatBoxToPixBox(pdp, mg_boxOnScreen);
CDrawPort dpModel(pdp, box);
dpModel.Lock();
dpModel.FillZBuffer(1.0f);
LCDSetDrawport(&dpModel);
// clear menu here
dpModel.Fill(C_BLACK|255);
LCDRenderClouds1();
LCDRenderClouds2();
// prepare projection
CRenderModel rmRenderModel;
CPerspectiveProjection3D pr;
pr.FOVL() = sam_bWideScreen ? AngleDeg(45.0f) : AngleDeg(30.0f);
pr.ScreenBBoxL() = FLOATaabbox2D(
FLOAT2D(0.0f, 0.0f),
FLOAT2D((float)dpModel.GetWidth(), (float)dpModel.GetHeight())
);
pr.AspectRatioL() = 1.0f;
pr.FrontClipDistanceL() = 0.3f;
pr.ViewerPlacementL() = CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0));
// initialize remdering
CAnyProjection3D apr;
apr = pr;
BeginModelRenderingView(apr, &dpModel);
rmRenderModel.rm_vLightDirection = FLOAT3D( 0.2f, -0.2f, -0.2f);
// if model needs floor
if (mg_moFloor.GetData()!=NULL) {
// set floor's position
CPlacement3D pl = mg_plModel;
pl.pl_OrientationAngle = ANGLE3D(0,0,0);
pl.pl_PositionVector = mg_plModel.pl_PositionVector;
pl.pl_PositionVector(2) += mg_fFloorY;
rmRenderModel.SetObjectPlacement(pl);
// render the floor
rmRenderModel.rm_colLight = C_WHITE;
rmRenderModel.rm_colAmbient = C_WHITE;
mg_moFloor.SetupModelRendering( rmRenderModel);
mg_moFloor.RenderModel( rmRenderModel);
}
// set model's position
CPlacement3D pl;
pl.pl_OrientationAngle = mg_plModel.pl_OrientationAngle;
pl.pl_PositionVector = mg_plModel.pl_PositionVector;
extern FLOAT sam_fPlayerOffset;
pl.pl_PositionVector(3) += sam_fPlayerOffset;
rmRenderModel.SetObjectPlacement(pl);
// render the model
rmRenderModel.rm_colLight = LerpColor(C_BLACK, C_WHITE, 0.4f)|CT_OPAQUE;
rmRenderModel.rm_colAmbient = LerpColor(C_BLACK, C_WHITE, 0.2f)|CT_OPAQUE;
mg_moModel.SetupModelRendering( rmRenderModel);
FLOATplane3D plFloorPlane = FLOATplane3D( FLOAT3D( 0.0f, 1.0f, 0.0f),
mg_plModel.pl_PositionVector(2)+mg_fFloorY);
FLOAT3D vShadowLightDir = FLOAT3D( -0.2f, -0.4f, -0.6f);
CPlacement3D plLightPlacement = CPlacement3D(
mg_plModel.pl_PositionVector+
vShadowLightDir*mg_plModel.pl_PositionVector(3)*5,
ANGLE3D(0,0,0));
mg_moModel.RenderShadow( rmRenderModel, plLightPlacement, 200.0f, 200.0f, 1.0f, plFloorPlane);
mg_moModel.RenderModel( rmRenderModel);
EndModelRenderingView();
LCDScreenBox(LCDGetColor(C_GREEN, "model box")|GetCurrentColor());
dpModel.Unlock();
pdp->Unlock();
pdp->Lock();
LCDSetDrawport(pdp);
// print the model name
{
PIXaabbox2D box = FloatBoxToPixBox(pdp, BoxPlayerModelName());
COLOR col = GetCurrentColor();
PIX pixI = box.Min()(1);
PIX pixJ = box.Max()(2);
pdp->PutText( mg_strText, pixI, pixJ, col);
}
}
// ------- Edit gadget implementation
CMGEdit::CMGEdit(void)
{
mg_pstrToChange = NULL;
mg_ctMaxStringLen = 70;
Clear();
}
void CMGEdit::Clear(void)
{
mg_iCursorPos = 0;
mg_bEditing = FALSE;
_bEditingString = FALSE;
}
void CMGEdit::OnActivate(void)
{
if (!mg_bEnabled) {
return;
}
ASSERT( mg_pstrToChange != NULL);
PlayMenuSound( _psdPress);
IFeel_PlayEffect("Menu_press");
SetText( mg_strText);
mg_iCursorPos = strlen(mg_strText);
mg_bEditing = TRUE;
_bEditingString = TRUE;
}
// focus lost
void CMGEdit::OnKillFocus(void)
{
// go out of editing mode
if( mg_bEditing) {
OnKeyDown(VK_RETURN);
Clear();
}
// proceed
CMenuGadget::OnKillFocus();
}
// helper function for deleting char(s) from string
static void Key_BackDel( CTString &str, INDEX &iPos, BOOL bShift, BOOL bRight)
{
// do nothing if string is empty
INDEX ctChars = strlen(str);
if( ctChars==0) return;
if( bRight && iPos<ctChars) { // DELETE key
if( bShift) {
// delete to end of line
str.TrimRight(iPos);
} else {
// delete only one char
str.DeleteChar(iPos);
}
}
if( !bRight && iPos>0) { // BACKSPACE key
if( bShift) {
// delete to start of line
str.TrimLeft(ctChars-iPos);
iPos=0;
} else {
// delete only one char
str.DeleteChar(iPos-1);
iPos--;
}
}
}
// key/mouse button pressed
BOOL CMGEdit::OnKeyDown( int iVKey)
{
// if not in edit mode
if( !mg_bEditing) {
// behave like normal gadget
return CMenuGadget::OnKeyDown(iVKey);
}
// finish editing?
BOOL bShift = GetKeyState(VK_SHIFT) & 0x8000;
switch( iVKey) {
case VK_UP: case VK_DOWN:
case VK_RETURN: case VK_LBUTTON: *mg_pstrToChange = mg_strText; Clear(); OnStringChanged(); break;
case VK_ESCAPE: case VK_RBUTTON: mg_strText = *mg_pstrToChange; Clear(); OnStringCanceled(); break;
case VK_LEFT: if( mg_iCursorPos > 0) mg_iCursorPos--; break;
case VK_RIGHT: if( mg_iCursorPos < strlen(mg_strText)) mg_iCursorPos++; break;
case VK_HOME: mg_iCursorPos = 0; break;
case VK_END: mg_iCursorPos = strlen(mg_strText); break;
case VK_BACK: Key_BackDel( mg_strText, mg_iCursorPos, bShift, FALSE); break;
case VK_DELETE: Key_BackDel( mg_strText, mg_iCursorPos, bShift, TRUE); break;
default: break; // ignore all other special keys
}
// key is handled
return TRUE;
}
// char typed
BOOL CMGEdit::OnChar( MSG msg)
{
// if not in edit mode
if( !mg_bEditing) {
// behave like normal gadget
return CMenuGadget::OnChar(msg);
}
// only chars are allowed
const INDEX ctFullLen = mg_strText.Length();
const INDEX ctNakedLen = mg_strText.LengthNaked();
mg_iCursorPos = Clamp( mg_iCursorPos, 0L, ctFullLen);
int iVKey = msg.wParam;
if( isprint(iVKey) && ctNakedLen<=mg_ctMaxStringLen) {
mg_strText.InsertChar( mg_iCursorPos, (char)iVKey);
mg_iCursorPos++;
}
// key is handled
return TRUE;
}
void CMGEdit::Render( CDrawPort *pdp)
{
if( mg_bEditing) {
mg_iTextMode = -1;
} else if( mg_bFocused) {
mg_iTextMode = 0;
} else {
mg_iTextMode = 1;
}
if (mg_strText=="" && !mg_bEditing) {
if (mg_bfsFontSize==BFS_SMALL) {
mg_strText="*";
} else {
mg_strText=TRANS("<none>");
}
CMGButton::Render(pdp);
mg_strText="";
} else {
CMGButton::Render(pdp);
}
}
void CMGEdit::OnStringChanged(void)
{
}
void CMGEdit::OnStringCanceled(void)
{
}
void CMGArrow::Render( CDrawPort *pdp)
{
SetFontMedium(pdp);
PIXaabbox2D box = FloatBoxToPixBox(pdp, mg_boxOnScreen);
COLOR col = GetCurrentColor();
CTString str;
if (mg_adDirection==AD_NONE) {
str = "???";
} else if (mg_adDirection==AD_UP) {
str = TRANS("Page Up");
} else if (mg_adDirection==AD_DOWN) {
str = TRANS("Page Down");
} else {
ASSERT(FALSE);
}
PIX pixI = box.Min()(1);
PIX pixJ = box.Min()(2);
pdp->PutText( str, pixI, pixJ, col);
}
void CMGArrow::OnActivate(void)
{
if (mg_adDirection==AD_UP) {
pgmCurrentMenu->ScrollList(-3);
} else if (mg_adDirection==AD_DOWN) {
pgmCurrentMenu->ScrollList(+3);
}
}
#define HSCOLUMNS 6
CTString strHighScores[HIGHSCORE_COUNT+1][HSCOLUMNS];
FLOAT afI[HSCOLUMNS] = {
0.12f, 0.15f, 0.6f, 0.7f, 0.78f, 0.9f
};
void CMGHighScore::Render( CDrawPort *pdp)
{
SetFontMedium(pdp);
COLOR colHeader = LCDGetColor(C_GREEN|255, "hiscore header");
COLOR colData = LCDGetColor(C_mdGREEN|255, "hiscore data");
COLOR colLastSet = LCDGetColor(C_mlGREEN|255, "hiscore last set");
INDEX iLastSet = _pGame->gm_iLastSetHighScore;
CTString strText;
strHighScores[0][0] = TRANS("No.");
strHighScores[0][1] = TRANS("Player Name");
strHighScores[0][2] = TRANS("Difficulty");
strHighScores[0][3] = TRANS("Time");
strHighScores[0][4] = TRANS("Kills");
strHighScores[0][5] = TRANS("Score");
{for (INDEX i=0; i<HIGHSCORE_COUNT; i++) {
switch(_pGame->gm_ahseHighScores[i].hse_gdDifficulty) {
default:
ASSERT(FALSE);
case (CSessionProperties::GameDifficulty)-100:
strHighScores[i+1][1] = "---";
continue;
break;
case CSessionProperties::GD_TOURIST:
strHighScores[i+1][2] = TRANS("Tourist");
break;
case CSessionProperties::GD_EASY:
strHighScores[i+1][2] = TRANS("Easy");
break;
case CSessionProperties::GD_NORMAL:
strHighScores[i+1][2] = TRANS("Normal");
break;
case CSessionProperties::GD_HARD:
strHighScores[i+1][2] = TRANS("Hard");
break;
case CSessionProperties::GD_EXTREME:
strHighScores[i+1][2] = TRANS("Serious");
break;
case CSessionProperties::GD_EXTREME+1:
strHighScores[i+1][2] = TRANS("Mental");
break;
}
strHighScores[i+1][0].PrintF("%d", i+1);
strHighScores[i+1][1] = _pGame->gm_ahseHighScores[i].hse_strPlayer;
strHighScores[i+1][3] = TimeToString(_pGame->gm_ahseHighScores[i].hse_tmTime);
strHighScores[i+1][4].PrintF("%03d", _pGame->gm_ahseHighScores[i].hse_ctKills);
strHighScores[i+1][5].PrintF("%9d", _pGame->gm_ahseHighScores[i].hse_ctScore);
}}
PIX pixJ = pdp->GetHeight()*0.25f;
{for (INDEX iRow=0; iRow<HIGHSCORE_COUNT+1; iRow++) {
COLOR col = (iRow==0) ? colHeader : colData;
if (iLastSet!=-1 && iRow-1==iLastSet) {
col = colLastSet;
}
{for (INDEX iColumn=0; iColumn<HSCOLUMNS; iColumn++) {
PIX pixI = pdp->GetWidth()*afI[iColumn];
if (iColumn==1) {
pdp->PutText(strHighScores[iRow][iColumn], pixI, pixJ, col);
} else {
pdp->PutTextR(strHighScores[iRow][iColumn], pixI, pixJ, col);
}
}}
if (iRow==0) {
pixJ+=pdp->GetHeight()*0.06f;
} else {
pixJ+=pdp->GetHeight()*0.04f;
}
}}
}
// ------- Trigger button implementation
INDEX GetNewLoopValue( int iVKey, INDEX iCurrent, INDEX ctMembers)
{
INDEX iPrev = (iCurrent+ctMembers-1)%ctMembers;
INDEX iNext = (iCurrent+1)%ctMembers;
// return and right arrow set new text
if( iVKey == VK_RETURN || iVKey==VK_LBUTTON || iVKey==VK_RIGHT )
{
return iNext;
}
// left arrow and backspace sets prev text
else if( (iVKey == VK_BACK || iVKey==VK_RBUTTON) || (iVKey == VK_LEFT) )
{
return iPrev;
}
return iCurrent;
}
CMGTrigger::CMGTrigger( void)
{
mg_pOnTriggerChange = NULL;
mg_iCenterI = 0;
mg_bVisual = FALSE;
}
void CMGTrigger::ApplyCurrentSelection(void)
{
mg_iSelected = Clamp(mg_iSelected, 0L, mg_ctTexts-1L);
mg_strValue = mg_astrTexts[ mg_iSelected];
}
void CMGTrigger::OnSetNextInList(int iVKey)
{
if( mg_pPreTriggerChange != NULL) {
mg_pPreTriggerChange(mg_iSelected);
}
mg_iSelected = GetNewLoopValue( iVKey, mg_iSelected, mg_ctTexts);
mg_strValue = mg_astrTexts[ mg_iSelected];
if( mg_pOnTriggerChange != NULL) {
(*mg_pOnTriggerChange)(mg_iSelected);
}
}
BOOL CMGTrigger::OnKeyDown( int iVKey)
{
if( (iVKey == VK_RETURN || iVKey==VK_LBUTTON) ||
(iVKey == VK_LEFT) ||
(iVKey == VK_BACK || iVKey==VK_RBUTTON) ||
(iVKey == VK_RIGHT) )
{
// key is handled
if( mg_bEnabled) OnSetNextInList(iVKey);
return TRUE;
}
// key is not handled
return FALSE;
}
void CMGTrigger::Render( CDrawPort *pdp)
{
SetFontMedium(pdp);
PIXaabbox2D box = FloatBoxToPixBox(pdp, mg_boxOnScreen);
PIX pixIL = box.Min()(1)+box.Size()(1)*0.45f;
PIX pixIR = box.Min()(1)+box.Size()(1)*0.55f;
PIX pixJ = box.Min()(2);
COLOR col = GetCurrentColor();
if (!mg_bVisual || mg_strValue=="") {
CTString strValue = mg_strValue;
if (mg_bVisual) {
strValue = TRANS("none");
}
if (mg_iCenterI==-1) {
pdp->PutText( mg_strLabel, box.Min()(1), pixJ, col);
pdp->PutTextR( strValue, box.Max()(1), pixJ, col);
} else {
pdp->PutTextR( mg_strLabel, pixIL, pixJ, col);
pdp->PutText( strValue, pixIR, pixJ, col);
}
} else {
CTString strLabel = mg_strLabel+": ";
pdp->PutText(strLabel, box.Min()(1), pixJ, col);
CTextureObject to;
try {
to.SetData_t(mg_strValue);
CTextureData *ptd = (CTextureData *)to.GetData();
PIX pixSize = box.Size()(2);
PIX pixCX = box.Max()(1)-pixSize/2;
PIX pixCY = box.Center()(2);
pdp->PutTexture( &to, PIXaabbox2D(
PIX2D(pixCX-pixSize/2, pixCY-pixSize/2),
PIX2D(pixCX-pixSize/2+pixSize, pixCY-pixSize/2+pixSize)), C_WHITE|255);
} catch (char *strError) {
CPrintF("%s\n", strError);
}
to.SetData(NULL);
}
}
CMGSlider::CMGSlider()
{
mg_iMinPos = 0;
mg_iMaxPos = 16;
mg_iCurPos = 8;
mg_pOnSliderChange = NULL;
mg_fFactor = 1.0f;
}
void CMGSlider::ApplyCurrentPosition( void)
{
mg_iCurPos = Clamp(mg_iCurPos, mg_iMinPos, mg_iMaxPos);
FLOAT fStretch = FLOAT(mg_iCurPos)/(mg_iMaxPos-mg_iMinPos);
mg_fFactor = fStretch;
if (mg_pOnSliderChange!=NULL) {
mg_pOnSliderChange(mg_iCurPos);
}
}
void CMGSlider::ApplyGivenPosition( INDEX iMin, INDEX iMax, INDEX iCur)
{
mg_iMinPos = iMin;
mg_iMaxPos = iMax;
mg_iCurPos = iCur;
ApplyCurrentPosition();
}
BOOL CMGSlider::OnKeyDown( int iVKey)
{
// if scrolling left
if( (iVKey==VK_BACK || iVKey==VK_LEFT) && mg_iCurPos>mg_iMinPos) {
mg_iCurPos --;
ApplyCurrentPosition();
return TRUE;
// if scrolling right
} else if( (iVKey==VK_RETURN || iVKey==VK_RIGHT) && mg_iCurPos<mg_iMaxPos) {
mg_iCurPos++;
ApplyCurrentPosition();
return TRUE;
// if lmb pressed
} else if (iVKey==VK_LBUTTON) {
// get position of slider box on screen
PIXaabbox2D boxSlider = GetSliderBox();
// if mouse is within
if (boxSlider>=PIX2D(_pixCursorPosI, _pixCursorPosJ)) {
// set new position exactly where mouse pointer is
FLOAT fRatio = FLOAT(_pixCursorPosI-boxSlider.Min()(1))/boxSlider.Size()(1);
fRatio = (fRatio-0.01f)/(0.99f-0.01f);
fRatio = Clamp(fRatio, 0.0f, 1.0f);
mg_iCurPos = fRatio*(mg_iMaxPos-mg_iMinPos) + mg_iMinPos;
ApplyCurrentPosition();
return TRUE;
}
}
return CMenuGadget::OnKeyDown( iVKey);
}
PIXaabbox2D CMGSlider::GetSliderBox(void)
{
extern CDrawPort *pdp;
PIXaabbox2D box = FloatBoxToPixBox(pdp, mg_boxOnScreen);
PIX pixIR = box.Min()(1)+box.Size()(1)*0.55f;
PIX pixJ = box.Min()(2);
PIX pixJSize = box.Size()(2)*0.95f;
PIX pixISizeR = box.Size()(1)*0.45f;
if( sam_bWideScreen) pixJSize++;
return PIXaabbox2D( PIX2D(pixIR+1, pixJ+1), PIX2D(pixIR+pixISizeR-2, pixJ+pixJSize-2));
}
void CMGSlider::Render( CDrawPort *pdp)
{
SetFontMedium(pdp);
// get geometry
COLOR col = GetCurrentColor();
PIXaabbox2D box = FloatBoxToPixBox(pdp, mg_boxOnScreen);
PIX pixIL = box.Min()(1)+box.Size()(1)*0.45f;
PIX pixIR = box.Min()(1)+box.Size()(1)*0.55f;
PIX pixJ = box.Min()(2);
PIX pixJSize = box.Size()(2)*0.95f;
PIX pixISizeR = box.Size()(1)*0.45f;
if( sam_bWideScreen) pixJSize++;
// print text left of slider
pdp->PutTextR( mg_strText, pixIL, pixJ, col);
// draw box around slider
LCDDrawBox(0, -1, PIXaabbox2D( PIX2D(pixIR+1, pixJ), PIX2D(pixIR+pixISizeR-2, pixJ+pixJSize-2)),
LCDGetColor(C_GREEN|255, "slider box"));
// draw filled part of slider
pdp->Fill( pixIR+2, pixJ+1, (pixISizeR-5)*mg_fFactor, (pixJSize-4), col);
// print percentage text
CTString strPercentage;
strPercentage.PrintF("%d%%", (int)floor(mg_fFactor*100+0.5f) );
pdp->PutTextC( strPercentage, pixIR+pixISizeR/2, pixJ+1, col);
}
void CMGLevelButton::OnActivate(void)
{
PlayMenuSound(_psdPress);
IFeel_PlayEffect("Menu_press");
_pGame->gam_strCustomLevel = mg_fnmLevel;
extern void (*_pAfterLevelChosen)(void);
_pAfterLevelChosen();
}
void CMGLevelButton::OnSetFocus(void)
{
SetThumbnail(mg_fnmLevel);
CMGButton::OnSetFocus();
}
// return slider position on scren
PIXaabbox2D CMGVarButton::GetSliderBox(void)
{
extern CDrawPort *pdp;
PIXaabbox2D box = FloatBoxToPixBox(pdp, mg_boxOnScreen);
PIX pixIR = box.Min()(1)+box.Size()(1)*0.55f;
PIX pixJ = box.Min()(2);
PIX pixISize = box.Size()(1)*0.13f;
PIX pixJSize = box.Size()(2);
return PIXaabbox2D( PIX2D(pixIR, pixJ+1), PIX2D(pixIR+pixISize-4, pixJ+pixJSize-6));
}
extern BOOL _bVarChanged;
BOOL CMGVarButton::OnKeyDown(int iVKey)
{
if (mg_pvsVar==NULL || mg_pvsVar->vs_bSeparator || !mg_pvsVar->Validate() || !mg_bEnabled) {
return CMenuGadget::OnKeyDown(iVKey);
}
// handle slider
if( mg_pvsVar->vs_iSlider && !mg_pvsVar->vs_bCustom) {
// ignore RMB
if( iVKey==VK_RBUTTON) return TRUE;
// handle LMB
if( iVKey==VK_LBUTTON) {
// get position of slider box on screen
PIXaabbox2D boxSlider = GetSliderBox();
// if mouse is within
if( boxSlider>=PIX2D(_pixCursorPosI, _pixCursorPosJ)) {
// set new position exactly where mouse pointer is
mg_pvsVar->vs_iValue = (FLOAT)(_pixCursorPosI-boxSlider.Min()(1))/boxSlider.Size()(1) * (mg_pvsVar->vs_ctValues);
_bVarChanged = TRUE;
}
// handled
return TRUE;
}
}
if( iVKey==VK_RETURN) {
FlushVarSettings(TRUE);
void MenuGoToParent(void);
MenuGoToParent();
return TRUE;
}
if( iVKey==VK_LBUTTON || iVKey==VK_RIGHT) {
if (mg_pvsVar!=NULL) {
INDEX iOldValue = mg_pvsVar->vs_iValue;
mg_pvsVar->vs_iValue++;
if( mg_pvsVar->vs_iValue>=mg_pvsVar->vs_ctValues) {
// wrap non-sliders, clamp sliders
if( mg_pvsVar->vs_iSlider) mg_pvsVar->vs_iValue = mg_pvsVar->vs_ctValues-1L;
else mg_pvsVar->vs_iValue = 0;
}
if( iOldValue != mg_pvsVar->vs_iValue) {
_bVarChanged = TRUE;
mg_pvsVar->vs_bCustom = FALSE;
mg_pvsVar->Validate();
}
}
return TRUE;
}
if( iVKey==VK_LEFT || iVKey==VK_RBUTTON) {
if (mg_pvsVar!=NULL) {
INDEX iOldValue = mg_pvsVar->vs_iValue;
mg_pvsVar->vs_iValue--;
if( mg_pvsVar->vs_iValue<0) {
// wrap non-sliders, clamp sliders
if( mg_pvsVar->vs_iSlider) mg_pvsVar->vs_iValue = 0;
else mg_pvsVar->vs_iValue = mg_pvsVar->vs_ctValues-1L;
}
if( iOldValue != mg_pvsVar->vs_iValue) {
_bVarChanged = TRUE;
mg_pvsVar->vs_bCustom = FALSE;
mg_pvsVar->Validate();
}
}
return TRUE;
}
// not handled
return CMenuGadget::OnKeyDown(iVKey);
}
void CMGVarButton::Render( CDrawPort *pdp)
{
if (mg_pvsVar==NULL) {
return;
}
SetFontMedium(pdp);
PIXaabbox2D box = FloatBoxToPixBox(pdp, mg_boxOnScreen);
PIX pixIL = box.Min()(1)+box.Size()(1)*0.45f;
PIX pixIR = box.Min()(1)+box.Size()(1)*0.55f;
PIX pixIC = box.Center()(1);
PIX pixJ = box.Min()(2);
if (mg_pvsVar->vs_bSeparator)
{
mg_bEnabled = FALSE;
COLOR col = LCDGetColor(C_WHITE|255, "separator");
CTString strText = mg_pvsVar->vs_strName;
pdp->PutTextC(strText, pixIC, pixJ, col);
}
else if (mg_pvsVar->Validate())
{
// check whether the variable is disabled
if( mg_pvsVar->vs_strFilter!="") mg_bEnabled = _pShell->GetINDEX(mg_pvsVar->vs_strFilter);
COLOR col = GetCurrentColor();
pdp->PutTextR( mg_pvsVar->vs_strName, pixIL, pixJ, col);
// custom is by default
CTString strText = TRANS("Custom");
if( !mg_pvsVar->vs_bCustom)
{ // not custom!
strText = mg_pvsVar->vs_astrTexts[mg_pvsVar->vs_iValue];
// need slider?
if( mg_pvsVar->vs_iSlider>0) {
// draw box around slider
PIX pixISize = box.Size()(1)*0.13f;
PIX pixJSize = box.Size()(2);
LCDDrawBox( 0,-1, PIXaabbox2D( PIX2D(pixIR, pixJ+1), PIX2D(pixIR+pixISize-4, pixJ+pixJSize-6)),
LCDGetColor(C_GREEN|255, "slider box"));
// draw filled part of slider
if( mg_pvsVar->vs_iSlider==1) {
// fill slider
FLOAT fFactor = (FLOAT)(mg_pvsVar->vs_iValue+1) / mg_pvsVar->vs_ctValues;
pdp->Fill( pixIR+1, pixJ+2, (pixISize-6)*fFactor, pixJSize-9, col);
} else {
// ratio slider
ASSERT( mg_pvsVar->vs_iSlider==2);
FLOAT fUnitWidth = (FLOAT)(pixISize-5) / mg_pvsVar->vs_ctValues;
pdp->Fill( pixIR+1+(mg_pvsVar->vs_iValue*fUnitWidth), pixJ+2, fUnitWidth, pixJSize-9, col);
}
// move text printout to the right of slider
pixIR += box.Size()(1)*0.15f;
}
}
// write right text
pdp->PutText(strText, pixIR, pixJ, col);
}
}
CMGFileButton::CMGFileButton(void)
{
mg_iState = FBS_NORMAL;
}
// refresh current text from description
void CMGFileButton::RefreshText(void)
{
mg_strText = mg_strDes;
mg_strText.OnlyFirstLine();
mg_strInfo = mg_strDes;
mg_strInfo.RemovePrefix(mg_strText);
mg_strInfo.DeleteChar(0);
}
void CMGFileButton::SaveDescription(void)
{
CTFileName fnFileNameDescription = mg_fnm.NoExt()+".des";
try {
mg_strDes.Save_t(fnFileNameDescription);
} catch( char *strError) {
CPrintF("%s\n", strError);
}
}
CMGFileButton *_pmgFileToSave = NULL;
void OnFileSaveOK(void)
{
if (_pmgFileToSave!=NULL) {
_pmgFileToSave->SaveYes();
}
}
void CMGFileButton::DoSave(void)
{
if (FileExistsForWriting(mg_fnm)) {
_pmgFileToSave = this;
extern void SaveConfirm(void);
SaveConfirm();
} else {
SaveYes();
}
}
void CMGFileButton::SaveYes(void)
{
ASSERT(gmLoadSaveMenu.gm_bSave);
// call saving function
BOOL bSucceeded = gmLoadSaveMenu.gm_pAfterFileChosen(mg_fnm);
// if saved
if (bSucceeded) {
// save the description too
SaveDescription();
}
}
void CMGFileButton::DoLoad(void)
{
ASSERT(!gmLoadSaveMenu.gm_bSave);
// if no file
if(!FileExists(mg_fnm)) {
// do nothing
return;
}
if (gmLoadSaveMenu.gm_pgmNextMenu!=NULL) {
gmLoadSaveMenu.gm_pgmParentMenu = gmLoadSaveMenu.gm_pgmNextMenu;
}
// call loading function
BOOL bSucceeded = gmLoadSaveMenu.gm_pAfterFileChosen(mg_fnm);
ASSERT(bSucceeded);
}
static CTString _strTmpDescription;
static CTString _strOrgDescription;
void CMGFileButton::StartEdit(void)
{
CMGEdit::OnActivate();
}
void CMGFileButton::OnActivate(void)
{
if (mg_fnm=="") {
return;
}
PlayMenuSound(_psdPress);
IFeel_PlayEffect("Menu_press");
// if loading
if (!gmLoadSaveMenu.gm_bSave) {
// load now
DoLoad();
// if saving
} else {
// switch to editing mode
BOOL bWasEmpty = mg_strText==EMPTYSLOTSTRING;
mg_strDes = gmLoadSaveMenu.gm_strSaveDes;
RefreshText();
_strOrgDescription = _strTmpDescription = mg_strText;
if (bWasEmpty) {
_strOrgDescription = EMPTYSLOTSTRING;
}
mg_pstrToChange = &_strTmpDescription;
StartEdit();
mg_iState = FBS_SAVENAME;
}
}
BOOL CMGFileButton::OnKeyDown(int iVKey)
{
if (mg_iState == FBS_NORMAL) {
if (gmLoadSaveMenu.gm_bSave || gmLoadSaveMenu.gm_bManage) {
if (iVKey == VK_F2) {
if (FileExistsForWriting(mg_fnm)) {
// switch to renaming mode
_strOrgDescription = mg_strText;
_strTmpDescription = mg_strText;
mg_pstrToChange = &_strTmpDescription;
StartEdit();
mg_iState = FBS_RENAME;
}
return TRUE;
} else if (iVKey == VK_DELETE) {
if (FileExistsForWriting(mg_fnm)) {
// delete the file, its description and thumbnail
RemoveFile(mg_fnm);
RemoveFile(mg_fnm.NoExt()+".des");
RemoveFile(mg_fnm.NoExt()+"Tbn.tex");
// refresh menu
gmLoadSaveMenu.EndMenu();
gmLoadSaveMenu.StartMenu();
OnSetFocus();
}
return TRUE;
}
}
return CMenuGadget::OnKeyDown(iVKey);
} else {
// go out of editing mode
if(mg_bEditing) {
if (iVKey==VK_UP || iVKey==VK_DOWN) {
CMGEdit::OnKeyDown(VK_ESCAPE);
}
}
return CMGEdit::OnKeyDown(iVKey);
}
}
void CMGFileButton::OnSetFocus(void)
{
mg_iState = FBS_NORMAL;
if (gmLoadSaveMenu.gm_bAllowThumbnails && mg_bEnabled) {
SetThumbnail(mg_fnm);
} else {
ClearThumbnail();
}
pgmCurrentMenu->KillAllFocuses();
CMGButton::OnSetFocus();
}
void CMGFileButton::OnKillFocus(void)
{
// go out of editing mode
if(mg_bEditing) {
OnKeyDown(VK_ESCAPE);
}
CMGEdit::OnKillFocus();
}
// override from edit gadget
void CMGFileButton::OnStringChanged(void)
{
// if saving
if (mg_iState == FBS_SAVENAME) {
// do the save
mg_strDes = _strTmpDescription+"\n"+mg_strInfo;
DoSave();
// if renaming
} else if (mg_iState == FBS_RENAME) {
// do the rename
mg_strDes = _strTmpDescription+"\n"+mg_strInfo;
SaveDescription();
// refresh menu
gmLoadSaveMenu.EndMenu();
gmLoadSaveMenu.StartMenu();
OnSetFocus();
}
}
void CMGFileButton::OnStringCanceled(void)
{
mg_strText = _strOrgDescription;
}
void CMGFileButton::Render( CDrawPort *pdp)
{
// render original gadget first
CMGEdit::Render(pdp);
// if currently selected
if (mg_bFocused && mg_bEnabled) {
// add info at the bottom if screen
SetFontMedium(pdp);
PIXaabbox2D box = FloatBoxToPixBox(pdp, BoxSaveLoad(15.0));
PIX pixI = box.Min()(1);
PIX pixJ = box.Min()(2);
COLOR col = LCDGetColor(C_mlGREEN|255, "file info");
pdp->PutText( mg_strInfo, pixI, pixJ, col);
}
}
FLOATaabbox2D GetBoxPartHoriz(const FLOATaabbox2D &box, FLOAT fMin, FLOAT fMax)
{
FLOAT fBoxMin = box.Min()(1);
FLOAT fBoxSize = box.Size()(1);
return FLOATaabbox2D(
FLOAT2D(fBoxMin+fBoxSize*fMin, box.Min()(2)),
FLOAT2D(fBoxMin+fBoxSize*fMax, box.Max()(2)));
}
void PrintInBox(CDrawPort *pdp, PIX pixI, PIX pixJ, PIX pixSizeI, CTString str, COLOR col)
{
str = str.Undecorated();
PIX pixCharSize = pdp->dp_pixTextCharSpacing+pdp->dp_FontData->fd_pixCharWidth;
str.TrimRight(pixSizeI/pixCharSize);
// print text
pdp->PutText(str, pixI, pixJ, col);
}
CMGServerList::CMGServerList()
{
mg_iSelected = 0;
mg_iFirstOnScreen = 0;
mg_ctOnScreen = 10;
mg_pixMinI = 0;
mg_pixMaxI = 0;
mg_pixListMinJ = 0;
mg_pixListStepJ = 0;
mg_pixDragJ = -1;
mg_iDragLine = -1;
mg_pixMouseDrag = -1;
// by default, sort by ping, best on top
mg_iSort = 2;
mg_bSortDown = FALSE;
}
void CMGServerList::AdjustFirstOnScreen(void)
{
INDEX ctSessions = _lhServers.Count();
mg_iSelected = Clamp(mg_iSelected, 0L, ClampDn(ctSessions-1L, 0L));
mg_iFirstOnScreen = Clamp(mg_iFirstOnScreen, 0L, ClampDn(ctSessions-mg_ctOnScreen, 0L));
if (mg_iSelected<mg_iFirstOnScreen) {
mg_iFirstOnScreen = ClampUp(mg_iSelected, ClampDn(ctSessions-mg_ctOnScreen-1L, 0L));
} else if (mg_iSelected>=mg_iFirstOnScreen+mg_ctOnScreen) {
mg_iFirstOnScreen = ClampDn(mg_iSelected-mg_ctOnScreen+1L, 0L);
}
}
BOOL _iSort = 0;
BOOL _bSortDown = FALSE;
int CompareSessions(const void *pv0, const void *pv1)
{
const CNetworkSession &ns0 = **(const CNetworkSession **)pv0;
const CNetworkSession &ns1 = **(const CNetworkSession **)pv1;
int iResult = 0;
switch(_iSort) {
case 0: iResult = stricmp(ns0.ns_strSession, ns1.ns_strSession); break;
case 1: iResult = stricmp(ns0.ns_strWorld, ns1.ns_strWorld); break;
case 2: iResult = Sgn(ns0.ns_tmPing-ns1.ns_tmPing); break;
case 3: iResult = Sgn(ns0.ns_ctPlayers-ns1.ns_ctPlayers); break;
case 4: iResult = stricmp(ns0.ns_strGameType, ns1.ns_strGameType); break;
case 5: iResult = stricmp(ns0.ns_strMod, ns1.ns_strMod ); break;
case 6: iResult = stricmp(ns0.ns_strVer, ns1.ns_strVer ); break;
}
if (iResult==0) { // make sure we always have unique order when resorting
return stricmp(ns0.ns_strAddress, ns1.ns_strAddress);;
}
return _bSortDown?-iResult:iResult;
}
extern CMGButton mgServerColumn[7];
extern CMGEdit mgServerFilter[7];
void SortAndFilterServers(void)
{
{FORDELETELIST(CNetworkSession, ns_lnNode, _lhServers, itns) {
delete &*itns;
}}
{FOREACHINLIST(CNetworkSession, ns_lnNode, _pNetwork->ga_lhEnumeratedSessions, itns) {
CNetworkSession &ns = *itns;
extern CTString _strServerFilter[7];
if (_strServerFilter[0]!="" && !ns.ns_strSession.Matches("*"+_strServerFilter[0]+"*")) continue;
if (_strServerFilter[1]!="" && !ns.ns_strWorld.Matches("*"+_strServerFilter[1]+"*")) continue;
if (_strServerFilter[2]!="") {
char strCompare[3] = {0,0,0};
int iPing = 0;
_strServerFilter[2].ScanF("%2[<>=]%d", strCompare, &iPing);
if (strcmp(strCompare, "<" )==0 && !(int(ns.ns_tmPing*1000)< iPing)) continue;
if (strcmp(strCompare, "<=")==0 && !(int(ns.ns_tmPing*1000)<=iPing)) continue;
if (strcmp(strCompare, ">" )==0 && !(int(ns.ns_tmPing*1000)> iPing)) continue;
if (strcmp(strCompare, ">=")==0 && !(int(ns.ns_tmPing*1000)>=iPing)) continue;
if (strcmp(strCompare, "=" )==0 && !(int(ns.ns_tmPing*1000)==iPing)) continue;
}
if (_strServerFilter[3]!="") {
char strCompare[3] = {0,0,0};
int iPlayers = 0;
_strServerFilter[3].ScanF("%2[<>=]%d", strCompare, &iPlayers);
if (strcmp(strCompare, "<" )==0 && !(ns.ns_ctPlayers< iPlayers)) continue;
if (strcmp(strCompare, "<=")==0 && !(ns.ns_ctPlayers<=iPlayers)) continue;
if (strcmp(strCompare, ">" )==0 && !(ns.ns_ctPlayers> iPlayers)) continue;
if (strcmp(strCompare, ">=")==0 && !(ns.ns_ctPlayers>=iPlayers)) continue;
if (strcmp(strCompare, "=" )==0 && !(ns.ns_ctPlayers==iPlayers)) continue;
}
if (_strServerFilter[4]!="" && !ns.ns_strGameType.Matches("*"+_strServerFilter[4]+"*")) continue;
if (_strServerFilter[5]!="" && !ns.ns_strMod.Matches("*"+_strServerFilter[5]+"*")) continue;
if (_strServerFilter[6]!="" && !ns.ns_strVer.Matches("*"+_strServerFilter[6]+"*")) continue;
CNetworkSession *pnsNew = new CNetworkSession;
pnsNew->Copy(*itns);
_lhServers.AddTail(pnsNew->ns_lnNode);
}}
_lhServers.Sort(CompareSessions, offsetof(CNetworkSession, ns_lnNode));
}
void CMGServerList::Render(CDrawPort *pdp)
{
_iSort = mg_iSort ;
_bSortDown = mg_bSortDown;
SortAndFilterServers();
SetFontSmall(pdp);
BOOL bFocusedBefore = mg_bFocused;
mg_bFocused = FALSE;
PIXaabbox2D box = FloatBoxToPixBox(pdp, mg_boxOnScreen);
COLOR col = GetCurrentColor();
PIX pixDPSizeI = pdp->GetWidth();
PIX pixDPSizeJ = pdp->GetHeight();
PIX pixCharSizeI = pdp->dp_pixTextCharSpacing+pdp->dp_FontData->fd_pixCharWidth;
PIX pixCharSizeJ = pdp->dp_pixTextLineSpacing+pdp->dp_FontData->fd_pixCharHeight+1;
PIX pixLineSize = 1;
PIX pixSliderSizeI = 10;
PIX pixOuterMargin = 20;
INDEX ctSessions = _lhServers.Count();
INDEX iSession=0;
INDEX ctColumns[7];
{for (INDEX i=0; i<ARRAYCOUNT(ctColumns); i++) {
ctColumns[i] = mgServerColumn[i].mg_strText.Length()+1;
}}
PIX pixSizePing = Max(PIX(pixCharSizeI*5), pixCharSizeI*ctColumns[2])+pixLineSize*2;
PIX pixSizePlayerCt = Max(PIX(pixCharSizeI*5),pixCharSizeI*ctColumns[3])+pixLineSize*2;
PIX pixSizeGameType = Max(Min(PIX(pixCharSizeI*20), PIX(pixDPSizeI*0.2f)), pixCharSizeI*ctColumns[4])+pixLineSize*2;
PIX pixSizeMapName = Max(PIX(pixDPSizeI*0.25f), pixCharSizeI*ctColumns[1])+pixLineSize*2;
PIX pixSizeMod = Max(Min(PIX(pixCharSizeI*11), PIX(pixDPSizeI*0.2f)), pixCharSizeI*ctColumns[5])+pixLineSize*2;
PIX pixSizeVer = Max(PIX(pixCharSizeI*7), pixCharSizeI*ctColumns[6])+pixLineSize*2;
PIX apixSeparatorI[9];
apixSeparatorI[0] = pixOuterMargin;
apixSeparatorI[8] = pixDPSizeI-pixOuterMargin-pixLineSize;
apixSeparatorI[7] = apixSeparatorI[8]-pixSliderSizeI-pixLineSize;
apixSeparatorI[6] = apixSeparatorI[7]-pixSizeVer-pixLineSize;
apixSeparatorI[5] = apixSeparatorI[6]-pixSizeMod-pixLineSize;
apixSeparatorI[4] = apixSeparatorI[5]-pixSizeGameType-pixLineSize;
apixSeparatorI[3] = apixSeparatorI[4]-pixSizePlayerCt-pixLineSize;
apixSeparatorI[2] = apixSeparatorI[3]-pixSizePing-pixLineSize;
apixSeparatorI[1] = apixSeparatorI[2]-pixSizeMapName-pixLineSize;
apixSeparatorI[1] = apixSeparatorI[2]-pixSizeMapName-pixLineSize;
PIX pixSizeServerName = apixSeparatorI[1]-apixSeparatorI[0]-pixLineSize;
PIX pixTopJ = pixDPSizeJ*0.15f;
PIX pixBottomJ = pixDPSizeJ*0.82f;
PIX pixFilterTopJ = pixTopJ+pixLineSize*3+pixCharSizeJ+pixLineSize*3;
PIX pixListTopJ = pixFilterTopJ+pixLineSize+pixCharSizeJ+pixLineSize;
INDEX ctSessionsOnScreen = (pixBottomJ-pixListTopJ)/pixCharSizeJ;
pixBottomJ = pixListTopJ + pixCharSizeJ*ctSessionsOnScreen + pixLineSize*2;
mg_pixMinI = apixSeparatorI[0];
mg_pixMaxI = apixSeparatorI[5];
mg_pixListMinJ = pixListTopJ;
mg_pixListStepJ = pixCharSizeJ;
mg_pixSBMinI = apixSeparatorI[7];
mg_pixSBMaxI = apixSeparatorI[8];
mg_pixSBMinJ = pixListTopJ;
mg_pixSBMaxJ = pixBottomJ;
mg_pixHeaderMinJ = pixTopJ;
mg_pixHeaderMidJ = pixTopJ+pixLineSize+pixCharSizeJ;
mg_pixHeaderMaxJ = pixTopJ+(pixLineSize+pixCharSizeJ)*2;
memcpy(mg_pixHeaderI, apixSeparatorI, sizeof(mg_pixHeaderI));
{for (INDEX i=0; i<ARRAYCOUNT(mgServerFilter); i++) {
mgServerColumn[i].mg_boxOnScreen = PixBoxToFloatBox(pdp,
PIXaabbox2D( PIX2D(apixSeparatorI[i]+pixCharSizeI/2,pixTopJ+pixLineSize*4), PIX2D(apixSeparatorI[i+1]-pixCharSizeI/2,pixTopJ+pixLineSize*4+pixCharSizeJ) ));
mgServerFilter[i].mg_boxOnScreen = PixBoxToFloatBox(pdp,
PIXaabbox2D( PIX2D(apixSeparatorI[i]+pixCharSizeI/2,pixFilterTopJ), PIX2D(apixSeparatorI[i+1]-pixCharSizeI/2,pixFilterTopJ+pixCharSizeJ) ));
}}
for (INDEX i=0; i<ARRAYCOUNT(apixSeparatorI); i++) {
pdp->DrawLine(apixSeparatorI[i], pixTopJ, apixSeparatorI[i], pixBottomJ, col|CT_OPAQUE);
}
pdp->DrawLine(apixSeparatorI[0], pixTopJ, apixSeparatorI[8], pixTopJ, col|CT_OPAQUE);
pdp->DrawLine(apixSeparatorI[0], pixListTopJ-pixLineSize, apixSeparatorI[8], pixListTopJ-pixLineSize, col|CT_OPAQUE);
pdp->DrawLine(apixSeparatorI[0], pixBottomJ, apixSeparatorI[8], pixBottomJ, col|CT_OPAQUE);
PIXaabbox2D boxHandle = GetScrollBarHandleBox();
pdp->Fill(boxHandle.Min()(1)+2, boxHandle.Min()(2)+2, boxHandle.Size()(1)-3, boxHandle.Size()(2)-3, col|CT_OPAQUE);
PIX pixJ = pixTopJ+pixLineSize*2+1;
mg_ctOnScreen = ctSessionsOnScreen;
AdjustFirstOnScreen();
if (_lhServers.Count()==0) {
if (_pNetwork->ga_strEnumerationStatus!="") {
mg_bFocused = TRUE;
COLOR colItem = GetCurrentColor();
PrintInBox(pdp, apixSeparatorI[0]+pixCharSizeI, pixListTopJ+pixCharSizeJ+pixLineSize+1, apixSeparatorI[1]-apixSeparatorI[0],
TRANS("searching..."), colItem);
}
} else {
FOREACHINLIST(CNetworkSession, ns_lnNode, _lhServers, itns) {
CNetworkSession &ns = *itns;
if (iSession<mg_iFirstOnScreen || iSession>=mg_iFirstOnScreen+ctSessionsOnScreen) {
iSession++;
continue;
}
PIX pixJ = pixListTopJ+(iSession-mg_iFirstOnScreen)*pixCharSizeJ+pixLineSize+1;
mg_bFocused = bFocusedBefore&&iSession==mg_iSelected;
COLOR colItem = GetCurrentColor();
if (ns.ns_strVer!=_SE_VER_STRING) {
colItem = MulColors(colItem, 0xA0A0A0FF);
}
CTString strPing(0,"%4d", INDEX(ns.ns_tmPing*1000));
CTString strPlayersCt(0, "%2d/%2d", ns.ns_ctPlayers, ns.ns_ctMaxPlayers);
CTString strMod = ns.ns_strMod;
if (strMod=="") {
strMod = "SeriousSam";
}
PrintInBox(pdp, apixSeparatorI[0]+pixCharSizeI/2, pixJ, apixSeparatorI[1]-apixSeparatorI[0]-pixCharSizeI, ns.ns_strSession, colItem);
PrintInBox(pdp, apixSeparatorI[1]+pixCharSizeI/2, pixJ, apixSeparatorI[2]-apixSeparatorI[1]-pixCharSizeI, TranslateConst(ns.ns_strWorld), colItem);
PrintInBox(pdp, apixSeparatorI[2]+pixCharSizeI/2, pixJ, apixSeparatorI[3]-apixSeparatorI[2]-pixCharSizeI, strPing, colItem);
PrintInBox(pdp, apixSeparatorI[3]+pixCharSizeI/2, pixJ, apixSeparatorI[4]-apixSeparatorI[3]-pixCharSizeI, strPlayersCt, colItem);
PrintInBox(pdp, apixSeparatorI[4]+pixCharSizeI/2, pixJ, apixSeparatorI[5]-apixSeparatorI[4]-pixCharSizeI, TranslateConst(ns.ns_strGameType), colItem);
PrintInBox(pdp, apixSeparatorI[5]+pixCharSizeI/2, pixJ, apixSeparatorI[6]-apixSeparatorI[5]-pixCharSizeI, TranslateConst(strMod), colItem);
PrintInBox(pdp, apixSeparatorI[6]+pixCharSizeI/2, pixJ, apixSeparatorI[7]-apixSeparatorI[6]-pixCharSizeI, ns.ns_strVer, colItem);
iSession++;
}
}
mg_bFocused = bFocusedBefore;
}
static INDEX SliderPixToIndex(PIX pixOffset, INDEX iVisible, INDEX iTotal, PIXaabbox2D boxFull)
{
FLOAT fSize = ClampUp(FLOAT(iVisible)/iTotal, 1.0f);
PIX pixFull = boxFull.Size()(2);
PIX pixSize = PIX(pixFull*fSize);
if (pixSize>=boxFull.Size()(2)) {
return 0;
}
return (iTotal*pixOffset)/pixFull;
}
static PIXaabbox2D GetSliderBox(INDEX iFirst, INDEX iVisible, INDEX iTotal,
PIXaabbox2D boxFull)
{
if (iTotal<=0) {
return boxFull;
}
FLOAT fSize = ClampUp(FLOAT(iVisible)/iTotal, 1.0f);
PIX pixFull = boxFull.Size()(2);
PIX pixSize = PIX(pixFull*fSize);
pixSize = ClampDn(pixSize, boxFull.Size()(1));
PIX pixTop = pixFull*(FLOAT(iFirst)/iTotal)+boxFull.Min()(2);
PIX pixI0 = boxFull.Min()(1);
PIX pixI1 = boxFull.Max()(1);
return PIXaabbox2D(PIX2D(pixI0, pixTop), PIX2D(pixI1, pixTop+pixSize));
}
PIXaabbox2D CMGServerList::GetScrollBarFullBox(void)
{
return PIXaabbox2D(PIX2D(mg_pixSBMinI, mg_pixSBMinJ), PIX2D(mg_pixSBMaxI, mg_pixSBMaxJ));
}
PIXaabbox2D CMGServerList::GetScrollBarHandleBox(void)
{
return GetSliderBox(mg_iFirstOnScreen, mg_ctOnScreen, _lhServers.Count(), GetScrollBarFullBox());
}
void CMGServerList::OnMouseOver(PIX pixI, PIX pixJ)
{
mg_pixMouseI = pixI;
mg_pixMouseJ = pixJ;
if (!(GetKeyState(VK_LBUTTON)&0x8000)) {
mg_pixDragJ = -1;
}
BOOL bInSlider = (pixI>=mg_pixSBMinI && pixI<=mg_pixSBMaxI && pixJ>=mg_pixSBMinJ && pixJ<=mg_pixSBMaxJ);
if (mg_pixDragJ>=0 && bInSlider) {
PIX pixDelta = pixJ-mg_pixDragJ;
INDEX ctSessions = _lhServers.Count();
INDEX iWantedLine = mg_iDragLine+
SliderPixToIndex(pixDelta, mg_ctOnScreen, ctSessions, GetScrollBarFullBox());
mg_iFirstOnScreen = Clamp(iWantedLine, 0L, ClampDn(ctSessions-mg_ctOnScreen, 0L));
mg_iSelected = Clamp(mg_iSelected, mg_iFirstOnScreen, mg_iFirstOnScreen+mg_ctOnScreen-1L);
// AdjustFirstOnScreen();
return;
}
// if some server is selected
if (pixI>=mg_pixMinI && pixI<=mg_pixMaxI) {
INDEX iOnScreen = (pixJ-mg_pixListMinJ)/mg_pixListStepJ;
if (iOnScreen>=0 && iOnScreen<mg_ctOnScreen) {
// put focus on it
mg_iSelected = mg_iFirstOnScreen+iOnScreen;
AdjustFirstOnScreen();
mg_pixMouseDrag = -1;
}
} else if (bInSlider) {
mg_pixMouseDrag = pixJ;
}
}
BOOL CMGServerList::OnKeyDown(int iVKey)
{
switch(iVKey) {
case VK_UP:
mg_iSelected-=1;
AdjustFirstOnScreen();
break;
case VK_DOWN:
mg_iSelected+=1;
AdjustFirstOnScreen();
break;
case VK_PRIOR:
mg_iSelected-=mg_ctOnScreen-1;
mg_iFirstOnScreen-=mg_ctOnScreen-1;
AdjustFirstOnScreen();
break;
case VK_NEXT:
mg_iSelected+=mg_ctOnScreen-1;
mg_iFirstOnScreen+=mg_ctOnScreen-1;
AdjustFirstOnScreen();
break;
case 11:
mg_iSelected-=3;
mg_iFirstOnScreen-=3;
AdjustFirstOnScreen();
break;
case 10:
mg_iSelected+=3;
mg_iFirstOnScreen+=3;
AdjustFirstOnScreen();
break;
case VK_LBUTTON:
/* if (mg_pixMouseJ>=mg_pixHeaderMinJ && mg_pixMouseJ<=mg_pixHeaderMidJ
&& mg_pixMouseI>=mg_pixHeaderI[0] && mg_pixMouseI<=mg_pixHeaderI[7]) {
INDEX iNewSort = mg_iSort;
if (mg_pixMouseI<=mg_pixHeaderI[1]) {
iNewSort = 0;
} else if (mg_pixMouseI<=mg_pixHeaderI[2]) {
iNewSort = 1;
} else if (mg_pixMouseI<=mg_pixHeaderI[3]) {
iNewSort = 2;
} else if (mg_pixMouseI<=mg_pixHeaderI[4]) {
iNewSort = 3;
} else if (mg_pixMouseI<=mg_pixHeaderI[5]) {
iNewSort = 4;
} else if (mg_pixMouseI<=mg_pixHeaderI[6]) {
iNewSort = 5;
} else if (mg_pixMouseI<=mg_pixHeaderI[7]) {
iNewSort = 6;
}
if (iNewSort==mg_iSort) {
mg_bSortDown = !mg_bSortDown;
} else {
mg_bSortDown = FALSE;
}
mg_iSort = iNewSort;
break;
} else */if (mg_pixMouseDrag>=0) {
mg_pixDragJ = mg_pixMouseDrag;
mg_iDragLine = mg_iFirstOnScreen;
break;
}
case VK_RETURN:
PlayMenuSound(_psdPress);
IFeel_PlayEffect("Menu_press");
{INDEX i=0;
FOREACHINLIST(CNetworkSession, ns_lnNode, _lhServers, itns) {
if (i==mg_iSelected) {
char strAddress[256];
int iPort;
itns->ns_strAddress.ScanF("%200[^:]:%d", &strAddress, &iPort);
_pGame->gam_strJoinAddress = strAddress;
_pShell->SetINDEX("net_iPort", iPort);
extern void StartSelectPlayersMenuFromServers(void );
StartSelectPlayersMenuFromServers();
return TRUE;
}
i++;
}}
break;
default:
return FALSE;
}
return TRUE;
}
void CMGServerList::OnSetFocus(void)
{
mg_bFocused = TRUE;
}
void CMGServerList::OnKillFocus(void)
{
mg_bFocused = FALSE;
}
// -------------------------------- Buttons for player selecting implementation
void CMGChangePlayer::OnActivate(void)
{
PlayMenuSound(_psdPress);
IFeel_PlayEffect("Menu_press");
_iLocalPlayer = mg_iLocalPlayer;
if( _pGame->gm_aiMenuLocalPlayers[ mg_iLocalPlayer] < 0)
_pGame->gm_aiMenuLocalPlayers[ mg_iLocalPlayer] = 0;
gmPlayerProfile.gm_piCurrentPlayer = &_pGame->gm_aiMenuLocalPlayers[ mg_iLocalPlayer];
gmPlayerProfile.gm_pgmParentMenu = &gmSelectPlayersMenu;
extern BOOL _bPlayerMenuFromSinglePlayer;
_bPlayerMenuFromSinglePlayer = FALSE;
ChangeToMenu( &gmPlayerProfile);
}
void CMGChangePlayer::SetPlayerText(void)
{
INDEX iPlayer = _pGame->gm_aiMenuLocalPlayers[ mg_iLocalPlayer];
CPlayerCharacter &pc = _pGame->gm_apcPlayers[ iPlayer];
if (iPlayer<0 || iPlayer>7) {
mg_strText = "????";
} else {
mg_strText.PrintF(TRANS("Player %d: %s\n"), mg_iLocalPlayer+1, pc.GetNameForPrinting());
}
}
// ------- Key (from customize keyboard) implementation
CMGKeyDefinition::CMGKeyDefinition( void)
{
mg_iState = DOING_NOTHING;
}
void CMGKeyDefinition::OnActivate(void)
{
PlayMenuSound(_psdPress);
IFeel_PlayEffect("Menu_press");
SetBindingNames(/*bDefining=*/TRUE);
mg_iState = RELEASE_RETURN_WAITING;
}
BOOL CMGKeyDefinition::OnKeyDown( int iVKey)
{
// if waiting for a key definition
if( mg_iState == PRESS_KEY_WAITING) {
// do nothing
return TRUE;
}
// if backspace pressed
if(iVKey == VK_BACK) {
// clear both keys
DefineKey(KID_NONE);
// message is processed
return TRUE;
}
return CMenuGadget::OnKeyDown( iVKey);
}
// set names for both key bindings
void CMGKeyDefinition::SetBindingNames(BOOL bDefining)
{
// find the button
INDEX ict=0;
INDEX iDik=0;
FOREACHINLIST( CButtonAction, ba_lnNode, _pGame->gm_ctrlControlsExtra.ctrl_lhButtonActions, itba) {
if( ict == mg_iControlNumber) {
CButtonAction &ba = *itba;
// get the current bindings and names
INDEX iKey1 = ba.ba_iFirstKey;
INDEX iKey2 = ba.ba_iSecondKey;
BOOL bKey1Bound = iKey1!=KID_NONE;
BOOL bKey2Bound = iKey2!=KID_NONE;
CTString strKey1 = _pInput->GetButtonTransName(iKey1);
CTString strKey2 = _pInput->GetButtonTransName(iKey2);
// if defining
if (bDefining) {
// if only first key is defined
if (bKey1Bound && !bKey2Bound) {
// put question mark for second key
mg_strBinding = strKey1+TRANS(" or ")+"?";
// otherwise
} else {
// put question mark only
mg_strBinding = "?";
}
// if not defining
} else {
// if second key is defined
if (bKey2Bound) {
// add both
mg_strBinding = strKey1+TRANS(" or ")+strKey2;
// if second key is undefined
} else {
// display only first one
mg_strBinding = strKey1;
}
}
return ;
}
ict++;
}
// if not found, put errorneous string
mg_strBinding = "???";
}
void CMGKeyDefinition::Appear(void)
{
SetBindingNames(/*bDefining=*/FALSE);
CMenuGadget::Appear();
}
void CMGKeyDefinition::Disappear(void)
{
CMenuGadget::Disappear();
}
void CMGKeyDefinition::DefineKey(INDEX iDik)
{
// for each button in controls
INDEX ict=0;
FOREACHINLIST(CButtonAction, ba_lnNode, _pGame->gm_ctrlControlsExtra.ctrl_lhButtonActions, itba) {
CButtonAction &ba = *itba;
// if it is this one
if (ict == mg_iControlNumber) {
// if should clear
if (iDik == KID_NONE) {
// unbind both
ba.ba_iFirstKey = KID_NONE;
ba.ba_iSecondKey = KID_NONE;
}
// if first key is unbound, or both keys are bound
if (ba.ba_iFirstKey==KID_NONE || ba.ba_iSecondKey!=KID_NONE) {
// bind first key
ba.ba_iFirstKey = iDik;
// clear second key
ba.ba_iSecondKey = KID_NONE;
// if only first key bound
} else {
// bind second key
ba.ba_iSecondKey = iDik;
}
// if it is not this one
} else {
// clear bindings that contain this key
if (ba.ba_iFirstKey == iDik) {
ba.ba_iFirstKey = KID_NONE;
}
if (ba.ba_iSecondKey == iDik) {
ba.ba_iSecondKey = KID_NONE;
}
}
ict++;
}
SetBindingNames(/*bDefining=*/FALSE);
}
void CMGKeyDefinition::Think( void)
{
if( mg_iState == RELEASE_RETURN_WAITING)
{
_bDefiningKey = TRUE;
extern BOOL _bMouseUsedLast;
_bMouseUsedLast = FALSE;
_pInput->SetJoyPolling(TRUE);
_pInput->GetInput(FALSE);
if( _pInput->IsInputEnabled() &&
!_pInput->GetButtonState( KID_ENTER) &&
!_pInput->GetButtonState( KID_MOUSE1 ) )
{
mg_iState = PRESS_KEY_WAITING;
}
}
else if( mg_iState == PRESS_KEY_WAITING)
{
_pInput->SetJoyPolling(TRUE);
_pInput->GetInput(FALSE);
for( INDEX iDik = 0; iDik<MAX_OVERALL_BUTTONS; iDik++)
{
if( _pInput->GetButtonState( iDik))
{
// skip keys that cannot be defined
if (iDik == KID_TILDE) {
continue;
}
// if escape not pressed
if (iDik != KID_ESCAPE) {
// define the new key
DefineKey(iDik);
// if escape pressed
} else {
// undefine the key
DefineKey(KID_NONE);
}
// end defining loop
mg_iState = DOING_NOTHING;
_bDefiningKey = FALSE;
// refresh all buttons
pgmCurrentMenu->FillListItems();
break;
}
}
}
}
void CMGKeyDefinition::Render( CDrawPort *pdp)
{
SetFontMedium(pdp);
PIXaabbox2D box = FloatBoxToPixBox(pdp, mg_boxOnScreen);
PIX pixIL = box.Min()(1)+box.Size()(1)*0.45f;
PIX pixIR = box.Min()(1)+box.Size()(1)*0.55f;
PIX pixJ = box.Min()(2);
COLOR col = GetCurrentColor();
pdp->PutTextR( mg_strLabel, pixIL, pixJ, col);
pdp->PutText( mg_strBinding, pixIR, pixJ, col);
}