diff --git a/Source/FreeCS-CE.prj b/Source/FreeCS-CE.prj
index 719cb093..f3ffcd11 100755
--- a/Source/FreeCS-CE.prj
+++ b/Source/FreeCS-CE.prj
@@ -1,5 +1,5 @@
-
+
@@ -65,6 +65,7 @@
+
@@ -110,15 +111,9 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/Source/Menu/Defs.h b/Source/Menu/Defs.h
index fcb404ea..85a672fb 100755
--- a/Source/Menu/Defs.h
+++ b/Source/Menu/Defs.h
@@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
var vector vVideoSize;
var vector vMenuOffset;
+var vector autocvar_menu_color = '1 0.59 0.19';
var vector vMousePos;
var float fInputKeyCode;
@@ -35,9 +36,18 @@ var float fInputKeyASCII;
var float fInputKeyDown;
var float fMouseClick;
var float fButtonAlpha[8];
+var float fScrollWheel;
+
+enum {
+ SCROLL_NONE,
+ SCROLL_UP,
+ SCROLL_DOWN
+};
var int iMenuActive;
+var int iScrollbarHold; // Because of this, don't put more than one scrollbar per screen :)
+
float frametime;
float fLastTime;
@@ -47,6 +57,9 @@ int iMapCount;
enum {
MENU_MAIN,
+ MENU_MULTIPLAYER,
+ MENU_MULTIPLAYER_CREATE,
+ MENU_MULTIPLAYER_OPTIONS,
MENU_QUIT
};
@@ -121,4 +134,7 @@ enum {
BTN_DEACTIVATE,
BTN_SPECTATEGAME,
BTN_SPECTATEGAMES
-};
\ No newline at end of file
+};
+
+void Menu_SetClipArea( vector vPosition, vector vRegion );
+void Menu_ResetClipArea( void );
\ No newline at end of file
diff --git a/Source/Menu/Draw.c b/Source/Menu/Draw.c
index dd236e9a..b2b39608 100755
--- a/Source/Menu/Draw.c
+++ b/Source/Menu/Draw.c
@@ -36,11 +36,8 @@ Responsible for the fancy menu background
=================
*/
void m_drawback( void ) {
- if ( clientstate() == 2 ) {
- drawfill( '0 0', vVideoSize, '0 0 0', 0.75f );
- } else {
- drawpic( vMenuOffset, "gfx/shell/splash", '640 480', '1 1 1', 1.0f, 0 );
- }
+ drawfill( '0 0', vVideoSize, '0 0 0', 0.75f );
+ drawpic( vMenuOffset, "gfx/shell/splash", '640 480', '1 1 1', 1.0f, 0 );
}
/*
@@ -59,6 +56,7 @@ void m_draw( vector vScreenSize ) {
vVideoSize = vScreenSize;
vMenuOffset_x = vVideoSize_x / 2 - 320;
vMenuOffset_y = vVideoSize_y / 2 - 240;
+ Menu_ResetClipArea();
}
// we have to create frametime ourselves because menuqc is primitive
@@ -73,6 +71,10 @@ void m_draw( vector vScreenSize ) {
if ( iMenu == MENU_MAIN ) {
Menu_Main();
+ } else if ( iMenu == MENU_MULTIPLAYER ) {
+ Menu_Multiplayer();
+ } else if ( iMenu == MENU_MULTIPLAYER_CREATE ) {
+ Menu_Multiplayer_Create();
} else if ( iMenu == MENU_QUIT ) {
Menu_Quit();
}
diff --git a/Source/Menu/Init.c b/Source/Menu/Init.c
index c1e8a6ae..7f1ae0b3 100755
--- a/Source/Menu/Init.c
+++ b/Source/Menu/Init.c
@@ -38,6 +38,8 @@ void m_init( void ) {
}
search_end( shMaps );
+
+ Menu_ResetClipArea();
}
/*
diff --git a/Source/Menu/Input.c b/Source/Menu/Input.c
index ba8501ed..2496db90 100755
--- a/Source/Menu/Input.c
+++ b/Source/Menu/Input.c
@@ -18,6 +18,10 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+// Temporary state
+var vector vMenuClickMins;
+var vector vMenuClickMaxs;
+
/*
=================
Menu_InputCheckMouse
@@ -25,26 +29,48 @@ Menu_InputCheckMouse
Checks a specified region and returns TRUE if the mouse is above it.
=================
*/
-int Menu_InputCheckMouse( vector vPos, vector vReg ) {
+int Menu_InputCheckMouse( vector vPosition, vector vRegion ) {
vector vSMins, vSMaxs;
- vSMins = vPos;
- vSMaxs = vPos;
- vSMins_x = vPos_x;
- vSMaxs_y = vPos_y - 1;
-
- vSMaxs_x = vPos_x + vReg_x;
- vSMaxs_y = vPos_y + vReg_y;
-
- if ( vMousePos_x >= vSMins_x && vMousePos_x <= vSMaxs_x ) {
- if ( vMousePos_y >= vSMins_y && vMousePos_y <= vSMaxs_y ) {
- return TRUE;
+ // Some elements will be blocked (scrolling lists) outside of this region
+ if ( vMousePos_x >= vMenuClickMins_x && vMousePos_x <= vMenuClickMaxs_x ) {
+ if ( vMousePos_y >= vMenuClickMins_y && vMousePos_y <= vMenuClickMaxs_y ) {
+ vSMins = vSMaxs = vPosition;
+ vSMaxs_y = vPosition_y - 1;
+
+ vSMaxs_x = vPosition_x + vRegion_x;
+ vSMaxs_y = vPosition_y + vRegion_y;
+
+ if ( vMousePos_x >= vSMins_x && vMousePos_x <= vSMaxs_x ) {
+ if ( vMousePos_y >= vSMins_y && vMousePos_y <= vSMaxs_y ) {
+ return TRUE;
+ }
+ }
}
}
return FALSE;
}
+void Menu_SetClipArea( vector vPosition, vector vRegion ) {
+ vPosition += vMenuOffset;
+ vMenuClickMins = vPosition;
+ vMenuClickMaxs = vPosition;
+ vMenuClickMins_x = vPosition_x;
+ vMenuClickMaxs_y = vPosition_y - 1;
+
+ vMenuClickMaxs_x = vPosition_x + vRegion_x;
+ vMenuClickMaxs_y = vPosition_y + vRegion_y;
+
+ drawsetcliparea( vPosition_x, vPosition_y, vRegion_x, vRegion_y );
+}
+
+void Menu_ResetClipArea( void ) {
+ vMenuClickMins = vMenuOffset;
+ vMenuClickMaxs = vMenuOffset + '640 480';
+ drawresetcliparea();
+}
+
/*
=================
Menu_InputEvent
@@ -64,11 +90,18 @@ float Menu_InputEvent( float fEventType, float fKey, float fCharacter, float fDe
fInputKeyDown = 1;
}
+ if ( fKey == K_MWHEELDOWN ) {
+ fScrollWheel = SCROLL_DOWN;
+ } else if ( fKey == K_MWHEELUP ) {
+ fScrollWheel = SCROLL_UP;
+ }
+
fInputKeyCode = fKey;
fInputKeyASCII = fCharacter;
} else if ( fEventType == IE_KEYUP ) {
if ( fKey == K_MOUSE1 ) {
fMouseClick = 0;
+ iScrollbarHold = FALSE;
} else {
fInputKeyDown = 0;
}
diff --git a/Source/Menu/MenuMain.c b/Source/Menu/MenuMain.c
index 09aff56b..139e8858 100755
--- a/Source/Menu/MenuMain.c
+++ b/Source/Menu/MenuMain.c
@@ -22,6 +22,9 @@ void Menu_Main( void ) {
static void Main_ButtonConsole( void ) {
localcmd( "toggleconsole\n" );
}
+ static void Main_ButtonMultiplayer( void ) {
+ iMenu = MENU_MULTIPLAYER;
+ }
static void Main_ButtonQuit( void ) {
iMenu = MENU_QUIT;
}
@@ -29,7 +32,7 @@ void Menu_Main( void ) {
Object_Button( '72 188', BTN_CONSOLE, Main_ButtonConsole, fButtonAlpha[0] );
Object_Button( '72 272', BTN_CONFIG, __NULL__, fButtonAlpha[1] );
- Object_Button( '72 328', BTN_MULTIPLAYER, __NULL__, fButtonAlpha[2] );
+ Object_Button( '72 328', BTN_MULTIPLAYER, Main_ButtonMultiplayer, fButtonAlpha[2] );
Object_Button( '72 356', BTN_CUSTOMGAME, __NULL__, fButtonAlpha[3] );
Object_Button( '72 384', BTN_README, __NULL__, fButtonAlpha[4] );
Object_Button( '72 412', BTN_VISITWEB, __NULL__, fButtonAlpha[5] );
@@ -46,7 +49,7 @@ void Menu_Quit( void ) {
}
Object_Frame( '192 192', '256 96' );
- drawstring( vMenuOffset + '200 216', "FREECS_QUITMSG", '8 8', MENU_FGCOLOR, 1.0f, 0 );
+ Object_Label( '200 216', "FREECS_QUITMSG", '8 8' );
Object_Button( '200 248', BTN_QUIT, Quit_Exit, fButtonAlpha[0] );
Object_Button_Right( '440 248', BTN_CANCEL, Quit_Cancel, fButtonAlpha[1] );
diff --git a/Source/Menu/MenuMultiplayer.c b/Source/Menu/MenuMultiplayer.c
new file mode 100755
index 00000000..5d37144d
--- /dev/null
+++ b/Source/Menu/MenuMultiplayer.c
@@ -0,0 +1,207 @@
+/*
+FreeCS Project
+Copyright (C) 2016, 2017 Marco "eukara" Hladik
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+// Server fields!
+float fldName;
+float fldAddress;
+float fldPing;
+float fldPlayers;
+float fldMaxplayers;
+float fldMap;
+float fldTimelimit;
+float fldFraglimit;
+float fServerClickTime;
+
+void Menu_Multiplayer_Connect( int iServerID ) {
+ if ( gethostcachenumber( fldMaxplayers, iServerID ) <= 0 ) {
+ return;
+ }
+
+ if ( iServerID >= 0 ) {
+ localcmd( sprintf( "connect %s\n", gethostcachestring( fldAddress, iServerID ) ) );
+ m_hide();
+ }
+}
+
+int Menu_Multiplayer_Find_Item( vector vPosition, int i, __inout int iSelected ) {
+ float fItemAlpha = 1.0f;
+
+ vPosition += vMenuOffset;
+
+ if ( Menu_InputCheckMouse( [ vPosition_x, vPosition_y ], [ 397, 8 ] ) == TRUE ) {
+ if ( fMouseClick == TRUE ) {
+ if ( iSelected != i ) {
+ iSelected = i;
+ fInputKeyCode = 0;
+ fMouseClick = FALSE;
+ fServerClickTime = time + 0.2;
+ } else {
+ if ( fServerClickTime > time ) {
+ Menu_Multiplayer_Connect( i );
+ }
+ iSelected = -2;
+ fInputKeyCode = 0;
+ fMouseClick = FALSE;
+ }
+ }
+ } else {
+ fItemAlpha = 0.8;
+ }
+
+ if ( gethostcachenumber( fldPing, i ) == -1 ) {
+ return FALSE;
+ }
+
+ if ( iSelected == i ) {
+ drawfill( [ vPosition_x, vPosition_y - 1 ], [ 397, 10 ], '1 1 1', 0.5, 2 );
+ drawstring( [vPosition_x + 8, vPosition_y], sprintf( "%.25s", gethostcachestring( fldName, i ) ), '8 8 0', '1 1 1', 1.0f, FALSE );
+ drawstring( [vPosition_x + 218, vPosition_y], sprintf( "%.10s", gethostcachestring( fldMap, i ) ), '8 8 0', '1 1 1', 1.0f, FALSE );
+ drawstring( [vPosition_x + 298, vPosition_y], sprintf( "%d/%d", gethostcachenumber( fldPlayers, i ), gethostcachenumber( fldMaxplayers, i ) ), '8 8 0', '1 1 1', 1.0f, FALSE );
+ drawstring( [vPosition_x + 362, vPosition_y], sprintf( "%.3s", ftos( gethostcachenumber( fldPing, i ) ) ), '8 8 0', '1 1 1', 1.0f, FALSE );
+ } else {
+ drawstring( [vPosition_x + 8, vPosition_y], sprintf( "^3%.25s", gethostcachestring( fldName, i ) ), '8 8 0', '1 1 1', fItemAlpha, FALSE );
+ drawstring( [vPosition_x + 218, vPosition_y], sprintf( "%.10s", gethostcachestring( fldMap, i ) ), '8 8 0', '1 1 1', fItemAlpha, FALSE );
+ drawstring( [vPosition_x + 298, vPosition_y], sprintf( "%d/%d", gethostcachenumber( fldPlayers, i ), gethostcachenumber( fldMaxplayers, i ) ), '8 8 0', '1 1 1', fItemAlpha, FALSE );
+ drawstring( [vPosition_x + 362, vPosition_y], sprintf( "%.3s", ftos( gethostcachenumber( fldPing, i ) ) ), '8 8 0', '1 1 1', fItemAlpha, FALSE );
+ }
+
+ return TRUE;
+}
+
+void Menu_Multiplayer( void ) {
+ static int iSelectedServer = -1;
+ static int iScrollServer;
+ static int iServersTotal;
+
+ static void Multiplayer_ButtonCreate( void ) {
+ iMenu = MENU_MULTIPLAYER_CREATE;
+ }
+ static void Multiplayer_ButtonRefresh( void ) {
+ refreshhostcache();
+ resorthostcache();
+ }
+ static void Multiplayer_ButtonDone( void ) {
+ iMenu = MENU_MAIN;
+ }
+
+ // Initialize it on the first run
+ if ( iSelectedServer == -1 ) {
+ localcmd( "com_protocolname FTE-Quake\n" );
+ //clear the filter
+ resethostcachemasks();
+ //sethostcachemaskstring( 0, gethostcacheindexforkey( "gamedir" ), "freecs", SLIST_TEST_EQUAL );
+ sethostcachesort( gethostcacheindexforkey( "ping" ), FALSE );
+ refreshhostcache();
+ resorthostcache();
+ iSelectedServer = 0;
+ }
+
+ fldName = gethostcacheindexforkey("name");
+ fldAddress = gethostcacheindexforkey("cname");
+ fldPing = gethostcacheindexforkey("ping");
+ fldPlayers = gethostcacheindexforkey("numhumans");
+ fldMaxplayers = gethostcacheindexforkey("maxplayers");
+ fldMap = gethostcacheindexforkey("map");
+ fldTimelimit = gethostcacheindexforkey("timelimit");
+ fldFraglimit = gethostcacheindexforkey("fraglimit");
+
+ iServersTotal = gethostcachevalue( SLIST_HOSTCACHEVIEWCOUNT );
+
+ Menu_SetClipArea( '32 148', '164 160' );
+ Object_Button( '32 148', BTN_JOINGAME, __NULL__, fButtonAlpha[0] );
+ Object_Button( '32 180', BTN_CREATE, Multiplayer_ButtonCreate, fButtonAlpha[1] );
+ Object_Button( '32 212', BTN_GAMEINFO, __NULL__, fButtonAlpha[2] );
+ Object_Button( '32 244', BTN_REFRESHLIST, Multiplayer_ButtonRefresh, fButtonAlpha[3] );
+ Object_Button( '32 276', BTN_DONE, Multiplayer_ButtonDone, fButtonAlpha[4] );
+ Menu_ResetClipArea();
+
+ Object_Frame( '196 140', '404 308' );
+ Object_Scrollbar( '604 140', 308, iScrollServer );
+
+ Object_Label( '208 124', "Game", '8 8' );
+ Object_Label( '418 124', "Map", '8 8' );
+ Object_Label( '498 124', "Players", '8 8' );
+ Object_Label( '562 124', "Ping", '8 8' );
+
+ Menu_SetClipArea( '196 141', '404 306' );
+ vector vListPos = '200 145';
+ vListPos_y -= fabs( ( ( iServersTotal - 8 ) * 10 ) * ( iScrollServer / 308 ) );
+ for ( float i = 0; i < iServersTotal; i++ ) {
+ if ( Menu_Multiplayer_Find_Item( vListPos, i, iSelectedServer ) == TRUE ) {
+ vListPos_y += 10;
+ } else {
+ break;
+ }
+ }
+ Menu_ResetClipArea();
+}
+
+void Menu_Multiplayer_Create( void ) {
+ static int iSelectedMap;
+ static int iScrollMap;
+
+ static void Create_ListMap( vector vPosition, int iIndex ) {
+ float fAlpha = 0.8;
+ vPosition += vMenuOffset;
+
+ if ( Menu_InputCheckMouse( vPosition, '182 10' ) == TRUE ) {
+ if ( fMouseClick == TRUE ) {
+ iSelectedMap = iIndex;
+ fMouseClick = FALSE;
+ }
+ fAlpha = 1.0f;
+ }
+
+ if ( iSelectedMap == iIndex ) {
+ drawstring( vPosition, sMapList[ iIndex ], '8 8', '1 1 1', 1.0f, 0 );
+ } else {
+ drawstring( vPosition, sMapList[ iIndex ], '8 8', '0.9 0.9 0.9', fAlpha, 0 );
+ }
+ }
+ static void Create_ButtonAdvanced( void ) {
+ // Advanced options
+ iMenu = MENU_MULTIPLAYER_OPTIONS;
+ }
+ static void Create_ButtonOK( void ) {
+ // Start server
+ localcmd( sprintf( "map %s\n", sMapList[ iSelectedMap ] ) );
+ }
+ static void Create_ButtonCancel( void ) {
+ iMenu = MENU_MULTIPLAYER;
+ }
+
+ Object_Button( '32 148', BTN_ADVOPTIONS, Create_ButtonAdvanced, fButtonAlpha[0] );
+ Object_Button( '32 180', BTN_OK, Create_ButtonOK, fButtonAlpha[1] );
+ Object_Button( '32 212', BTN_CANCEL, Create_ButtonCancel, fButtonAlpha[2] );
+
+ Object_Label( '384 148', "Maps:", '8 8' );
+ Object_Frame( '384 164', '190 288' );
+ Object_Scrollbar( '576 164', 288, iScrollMap );
+
+ // Maplist
+ vector vListPos = '392 172';
+ Menu_SetClipArea( '386 166', '188 286' );
+ vListPos_y -= fabs( ( ( iMapCount - 21 ) * 10 ) * ( iScrollMap / 288 ) );
+ for ( int i = 0; i < iMapCount; i++ ) {
+ Create_ListMap( vListPos, i );
+ vListPos_y += 10;
+ }
+ Menu_ResetClipArea();
+}
\ No newline at end of file
diff --git a/Source/Menu/Objects.c b/Source/Menu/Objects.c
index eb8868ea..f05ae3bf 100755
--- a/Source/Menu/Objects.c
+++ b/Source/Menu/Objects.c
@@ -90,16 +90,15 @@ string sButtonLabels[ MENU_BUTTONS ] = {
"BTN_SPECTATEGAMES"
};
-#define MENU_FGCOLOR '1 0.59 0.19'
+#define autocvar_menu_color '1 0.59 0.19'
/*
=================
-drawmenupic
+Object_Button
-Wrapper for drawpic that cares about resolution and scales.
+Used for the (used to be) bitmap buttons in the menu
=================
*/
-
void Object_Button( vector vPosition, int iButtonID, void() vFunction, __inout float fAlpha ) {
static int iLastButton = -1;
@@ -127,25 +126,102 @@ void Object_Button( vector vPosition, int iButtonID, void() vFunction, __inout f
}
}
- drawstring( vPosition, sButtonLabels[ iButtonID ], '16 16', MENU_FGCOLOR, 1.0f, 0 );
+ drawstring( vPosition, sButtonLabels[ iButtonID ], '16 16', autocvar_menu_color, 1.0f, 0 );
drawstring( vPosition, sButtonLabels[ iButtonID ], '16 16', '1 1 1', fAlpha, 0 );
}
+/*
+=================
+Object_Button_Right
+
+A right-aligned version of Object_Button
+=================
+*/
void Object_Button_Right( vector vPosition, int iButtonID, void() vFunction, __inout float fAlpha ) {
vPosition_x -= stringwidth( sButtonLabels[ iButtonID ], TRUE, '16 16' );
Object_Button( vPosition, iButtonID, vFunction, fAlpha );
}
-// Draws window with outline, border and title
-void Object_Frame( vector vPos, vector vSize ) {
- vPos += vMenuOffset;
+/*
+=================
+Object_Frame
+
+A filled "window" of sorts
+=================
+*/
+void Object_Frame( vector vPosition, vector vSize ) {
+ vPosition += vMenuOffset;
// Draw the background
- drawfill( vPos, vSize, '0 0 0', 1.0f );
+ drawfill( vPosition, vSize, '0 0 0', 1.0f );
- drawfill( vPos, [vSize_x, 1], MENU_FGCOLOR, 1.0f ); // Top
- drawfill( [vPos_x, vPos_y + vSize_y], [vSize_x, 1], MENU_FGCOLOR, 1.0f ); // Bottom
+ drawfill( vPosition, [vSize_x, 1], autocvar_menu_color, 1.0f ); // Top
+ drawfill( [vPosition_x, vPosition_y + vSize_y], [vSize_x, 1], autocvar_menu_color, 1.0f ); // Bottom
- drawfill( vPos, [1, vSize_y], MENU_FGCOLOR, 1.0f ); // Left
- drawfill( [vPos_x + vSize_x, vPos_y], [1, vSize_y], MENU_FGCOLOR, 1.0f ); // Right
+ drawfill( vPosition, [1, vSize_y], autocvar_menu_color, 1.0f ); // Left
+ drawfill( [vPosition_x + vSize_x, vPosition_y], [1, vSize_y + 1], autocvar_menu_color, 1.0f ); // Right
}
+
+/*
+=================
+Object_Label
+
+A label in a cvar driven color scheme
+=================
+*/
+void Object_Label( vector vPosition, string sLabel, vector vSize ) {
+ vPosition += vMenuOffset;
+ drawstring( vPosition, sLabel, vSize, autocvar_menu_color, 1.0f, 0 );
+}
+
+/*
+=================
+Object_Label_Right
+
+A right-aligned version of Object_Label
+=================
+*/
+void Object_Label_Right( vector vPosition, string sLabel, vector vSize ) {
+ vPosition_x -= stringwidth( sLabel, TRUE, vSize );
+ Object_Label( vPosition, sLabel, vSize );
+}
+
+/*
+=================
+Object_Scrollbar
+
+A scrollbar, for different types of purposes.
+Note: Only have one at a time.
+=================
+*/
+void Object_Scrollbar( vector vPosition, int iHeight, __inout int iProgress ) {
+ Object_Frame( vPosition, [ 16, iHeight ] );
+
+ vPosition += vMenuOffset;
+ iHeight -= 16;
+ if ( ( iScrollbarHold == TRUE ) || ( Menu_InputCheckMouse( [vPosition_x, vPosition_y + iProgress ], '16 16' ) == TRUE ) ) {
+ if ( fMouseClick == TRUE ) {
+ iProgress = ( vMousePos_y - vPosition_y ) - 8;
+ iScrollbarHold = TRUE;
+ }
+ }
+
+ if ( fScrollWheel == SCROLL_DOWN ) {
+ iProgress += 2;
+ fScrollWheel = SCROLL_NONE;
+ } else if ( fScrollWheel == SCROLL_UP ) {
+ iProgress -= 2;
+ fScrollWheel = SCROLL_NONE;
+ }
+
+ if ( iProgress < 0 ) {
+ iProgress = 0;
+ } else if ( iProgress > iHeight ) {
+ iProgress = iHeight;
+ }
+
+ iHeight += 16;
+
+ drawfill( [vPosition_x, vPosition_y + iProgress], [ 16, 16 ], autocvar_menu_color, 1.0f );
+}
+
diff --git a/Source/Menu/progs.src b/Source/Menu/progs.src
index 642847f3..6e041de3 100755
--- a/Source/Menu/progs.src
+++ b/Source/Menu/progs.src
@@ -12,5 +12,6 @@ Init.c
Input.c
Objects.c
MenuMain.c
+MenuMultiplayer.c
Draw.c
#endlist
diff --git a/freecs/csprogs.dat b/freecs/csprogs.dat
index 95e928e5..27a21aa7 100644
Binary files a/freecs/csprogs.dat and b/freecs/csprogs.dat differ
diff --git a/freecs/default.cfg b/freecs/default.cfg
index b8fd4783..2863d15a 100755
--- a/freecs/default.cfg
+++ b/freecs/default.cfg
@@ -60,13 +60,13 @@ hostname "FreeCS Server"
seta vid_conautoscale "1"
seta snd_device "sdl"
-seta r_polygonoffset_submodel_offset 0
-seta r_polygonoffset_submodel_factor 0
-seta r_fullbrightSkins 0
-seta r_fb_models 0
-seta con_logcenterprint 0
-seta v_contentblend 0
-seta com_nogamedirnativecode 0
-
+seta r_polygonoffset_submodel_offset "0"
+seta r_polygonoffset_submodel_factor "0"
+seta r_fullbrightSkins "0"
+seta r_fb_models "0"
+seta con_logcenterprint "0"
+seta v_contentblend "0"
+seta com_nogamedirnativecode "0"
+seta cl_cursor_scale "1"
seta r_shadow_realtime_world_shadows "0"
seta r_shadow_realtime_dlight_shadows "0"
diff --git a/freecs/gfx/menuchars.tga b/freecs/gfx/menuchars.tga
new file mode 100755
index 00000000..b68b86d5
Binary files /dev/null and b/freecs/gfx/menuchars.tga differ
diff --git a/freecs/menu.dat b/freecs/menu.dat
index adaf0364..f539d25e 100755
Binary files a/freecs/menu.dat and b/freecs/menu.dat differ
diff --git a/freecs/progs.dat b/freecs/progs.dat
index 6a4c2b88..af809926 100644
Binary files a/freecs/progs.dat and b/freecs/progs.dat differ