1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22 //
23 /*
24 =======================================================================
25
26 MAIN MENU
27
28 =======================================================================
29 */
30
31
32 #include "ui_local.h"
33
34
35 #define ID_SINGLEPLAYER 10
36 #define ID_MULTIPLAYER 11
37 #define ID_SETUP 12
38 #define ID_DEMOS 13
39 #define ID_CINEMATICS 14
40 #define ID_TEAMARENA 15
41 #define ID_MODS 16
42 #define ID_EXIT 17
43
44 #define MAIN_BANNER_MODEL "models/mapobjects/banner/banner5.md3"
45 #define MAIN_MENU_VERTICAL_SPACING 34
46
47
48 typedef struct {
49 menuframework_s menu;
50
51 menutext_s singleplayer;
52 menutext_s multiplayer;
53 menutext_s setup;
54 menutext_s demos;
55 menutext_s cinematics;
56 menutext_s teamArena;
57 menutext_s mods;
58 menutext_s exit;
59
60 qhandle_t bannerModel;
61 } mainmenu_t;
62
63
64 static mainmenu_t s_main;
65
66 typedef struct {
67 menuframework_s menu;
68 char errorMessage[4096];
69 } errorMessage_t;
70
71 static errorMessage_t s_errorMessage;
72
73 /*
74 =================
75 MainMenu_ExitAction
76 =================
77 */
MainMenu_ExitAction(qboolean result)78 static void MainMenu_ExitAction( qboolean result ) {
79 if( !result ) {
80 return;
81 }
82 UI_PopMenu();
83 UI_CreditMenu();
84 }
85
86
87
88 /*
89 =================
90 Main_MenuEvent
91 =================
92 */
Main_MenuEvent(void * ptr,int event)93 void Main_MenuEvent (void* ptr, int event) {
94 if( event != QM_ACTIVATED ) {
95 return;
96 }
97
98 switch( ((menucommon_s*)ptr)->id ) {
99 case ID_SINGLEPLAYER:
100 UI_SPLevelMenu();
101 break;
102
103 case ID_MULTIPLAYER:
104 UI_ArenaServersMenu();
105 break;
106
107 case ID_SETUP:
108 UI_SetupMenu();
109 break;
110
111 case ID_DEMOS:
112 UI_DemosMenu();
113 break;
114
115 case ID_CINEMATICS:
116 UI_CinematicsMenu();
117 break;
118
119 case ID_MODS:
120 UI_ModsMenu();
121 break;
122
123 case ID_TEAMARENA:
124 trap_Cvar_Set( "fs_game", "missionpack");
125 trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" );
126 break;
127
128 case ID_EXIT:
129 UI_ConfirmMenu( "EXIT GAME?", 0, MainMenu_ExitAction );
130 break;
131 }
132 }
133
134
135 /*
136 ===============
137 MainMenu_Cache
138 ===============
139 */
MainMenu_Cache(void)140 void MainMenu_Cache( void ) {
141 s_main.bannerModel = trap_R_RegisterModel( MAIN_BANNER_MODEL );
142 }
143
ErrorMessage_Key(int key)144 sfxHandle_t ErrorMessage_Key(int key)
145 {
146 trap_Cvar_Set( "com_errorMessage", "" );
147 UI_MainMenu();
148 return (menu_null_sound);
149 }
150
151 /*
152 ===============
153 Main_MenuDraw
154 TTimo: this function is common to the main menu and errorMessage menu
155 ===============
156 */
157
Main_MenuDraw(void)158 static void Main_MenuDraw( void ) {
159 refdef_t refdef;
160 refEntity_t ent;
161 vec3_t origin;
162 vec3_t angles;
163 float adjust;
164 float x, y, w, h;
165 vec4_t color = {0.5, 0, 0, 1};
166
167 // setup the refdef
168
169 memset( &refdef, 0, sizeof( refdef ) );
170
171 refdef.rdflags = RDF_NOWORLDMODEL;
172
173 AxisClear( refdef.viewaxis );
174
175 x = 0;
176 y = 0;
177 w = 640;
178 h = 120;
179 UI_AdjustFrom640( &x, &y, &w, &h );
180 refdef.x = x;
181 refdef.y = y;
182 refdef.width = w;
183 refdef.height = h;
184
185 adjust = 0; // JDC: Kenneth asked me to stop this 1.0 * sin( (float)uis.realtime / 1000 );
186 refdef.fov_x = 60 + adjust;
187 refdef.fov_y = 19.6875 + adjust;
188
189 refdef.time = uis.realtime;
190
191 origin[0] = 300;
192 origin[1] = 0;
193 origin[2] = -32;
194
195 trap_R_ClearScene();
196
197 // add the model
198
199 memset( &ent, 0, sizeof(ent) );
200
201 adjust = 5.0 * sin( (float)uis.realtime / 5000 );
202 VectorSet( angles, 0, 180 + adjust, 0 );
203 AnglesToAxis( angles, ent.axis );
204 ent.hModel = s_main.bannerModel;
205 VectorCopy( origin, ent.origin );
206 VectorCopy( origin, ent.lightingOrigin );
207 ent.renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW;
208 VectorCopy( ent.origin, ent.oldorigin );
209
210 trap_R_AddRefEntityToScene( &ent );
211
212 trap_R_RenderScene( &refdef );
213
214 if (strlen(s_errorMessage.errorMessage))
215 {
216 UI_DrawProportionalString_AutoWrapped( 320, 192, 600, 20, s_errorMessage.errorMessage, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color );
217 }
218 else
219 {
220 // standard menu drawing
221 Menu_Draw( &s_main.menu );
222 }
223
224 if (uis.demoversion) {
225 UI_DrawProportionalString( 320, 372, "DEMO FOR MATURE AUDIENCES DEMO", UI_CENTER|UI_SMALLFONT, color );
226 UI_DrawString( 320, 400, "Quake III Arena(c) 1999-2000, Id Software, Inc. All Rights Reserved", UI_CENTER|UI_SMALLFONT, color );
227 } else {
228 UI_DrawString( 320, 450, "Quake III Arena(c) 1999-2000, Id Software, Inc. All Rights Reserved", UI_CENTER|UI_SMALLFONT, color );
229 }
230 }
231
232
233 /*
234 ===============
235 UI_TeamArenaExists
236 ===============
237 */
UI_TeamArenaExists(void)238 static qboolean UI_TeamArenaExists( void ) {
239 int numdirs;
240 char dirlist[2048];
241 char *dirptr;
242 char *descptr;
243 int i;
244 int dirlen;
245
246 numdirs = trap_FS_GetFileList( "$modlist", "", dirlist, sizeof(dirlist) );
247 dirptr = dirlist;
248 for( i = 0; i < numdirs; i++ ) {
249 dirlen = strlen( dirptr ) + 1;
250 descptr = dirptr + dirlen;
251 if (Q_stricmp(dirptr, "missionpack") == 0) {
252 return qtrue;
253 }
254 dirptr += dirlen + strlen(descptr) + 1;
255 }
256 return qfalse;
257 }
258
259
260 /*
261 ===============
262 UI_MainMenu
263
264 The main menu only comes up when not in a game,
265 so make sure that the attract loop server is down
266 and that local cinematics are killed
267 ===============
268 */
UI_MainMenu(void)269 void UI_MainMenu( void ) {
270 int y;
271 qboolean teamArena = qfalse;
272 int style = UI_CENTER | UI_DROPSHADOW;
273
274 trap_Cvar_Set( "sv_killserver", "1" );
275
276 if( !uis.demoversion && !ui_cdkeychecked.integer ) {
277 char key[17];
278
279 trap_GetCDKey( key, sizeof(key) );
280 if( trap_VerifyCDKey( key, NULL ) == qfalse ) {
281 UI_CDKeyMenu();
282 return;
283 }
284 }
285
286 memset( &s_main, 0 ,sizeof(mainmenu_t) );
287 memset( &s_errorMessage, 0 ,sizeof(errorMessage_t) );
288
289 // com_errorMessage would need that too
290 MainMenu_Cache();
291
292 trap_Cvar_VariableStringBuffer( "com_errorMessage", s_errorMessage.errorMessage, sizeof(s_errorMessage.errorMessage) );
293 if (strlen(s_errorMessage.errorMessage))
294 {
295 s_errorMessage.menu.draw = Main_MenuDraw;
296 s_errorMessage.menu.key = ErrorMessage_Key;
297 s_errorMessage.menu.fullscreen = qtrue;
298 s_errorMessage.menu.wrapAround = qtrue;
299 s_errorMessage.menu.showlogo = qtrue;
300
301 trap_Key_SetCatcher( KEYCATCH_UI );
302 uis.menusp = 0;
303 UI_PushMenu ( &s_errorMessage.menu );
304
305 return;
306 }
307
308 s_main.menu.draw = Main_MenuDraw;
309 s_main.menu.fullscreen = qtrue;
310 s_main.menu.wrapAround = qtrue;
311 s_main.menu.showlogo = qtrue;
312
313 y = 134;
314 s_main.singleplayer.generic.type = MTYPE_PTEXT;
315 s_main.singleplayer.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
316 s_main.singleplayer.generic.x = 320;
317 s_main.singleplayer.generic.y = y;
318 s_main.singleplayer.generic.id = ID_SINGLEPLAYER;
319 s_main.singleplayer.generic.callback = Main_MenuEvent;
320 s_main.singleplayer.string = "SINGLE PLAYER";
321 s_main.singleplayer.color = color_red;
322 s_main.singleplayer.style = style;
323
324 y += MAIN_MENU_VERTICAL_SPACING;
325 s_main.multiplayer.generic.type = MTYPE_PTEXT;
326 s_main.multiplayer.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
327 s_main.multiplayer.generic.x = 320;
328 s_main.multiplayer.generic.y = y;
329 s_main.multiplayer.generic.id = ID_MULTIPLAYER;
330 s_main.multiplayer.generic.callback = Main_MenuEvent;
331 s_main.multiplayer.string = "MULTIPLAYER";
332 s_main.multiplayer.color = color_red;
333 s_main.multiplayer.style = style;
334
335 y += MAIN_MENU_VERTICAL_SPACING;
336 s_main.setup.generic.type = MTYPE_PTEXT;
337 s_main.setup.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
338 s_main.setup.generic.x = 320;
339 s_main.setup.generic.y = y;
340 s_main.setup.generic.id = ID_SETUP;
341 s_main.setup.generic.callback = Main_MenuEvent;
342 s_main.setup.string = "SETUP";
343 s_main.setup.color = color_red;
344 s_main.setup.style = style;
345
346 y += MAIN_MENU_VERTICAL_SPACING;
347 s_main.demos.generic.type = MTYPE_PTEXT;
348 s_main.demos.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
349 s_main.demos.generic.x = 320;
350 s_main.demos.generic.y = y;
351 s_main.demos.generic.id = ID_DEMOS;
352 s_main.demos.generic.callback = Main_MenuEvent;
353 s_main.demos.string = "DEMOS";
354 s_main.demos.color = color_red;
355 s_main.demos.style = style;
356
357 y += MAIN_MENU_VERTICAL_SPACING;
358 s_main.cinematics.generic.type = MTYPE_PTEXT;
359 s_main.cinematics.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
360 s_main.cinematics.generic.x = 320;
361 s_main.cinematics.generic.y = y;
362 s_main.cinematics.generic.id = ID_CINEMATICS;
363 s_main.cinematics.generic.callback = Main_MenuEvent;
364 s_main.cinematics.string = "CINEMATICS";
365 s_main.cinematics.color = color_red;
366 s_main.cinematics.style = style;
367
368 if (UI_TeamArenaExists()) {
369 teamArena = qtrue;
370 y += MAIN_MENU_VERTICAL_SPACING;
371 s_main.teamArena.generic.type = MTYPE_PTEXT;
372 s_main.teamArena.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
373 s_main.teamArena.generic.x = 320;
374 s_main.teamArena.generic.y = y;
375 s_main.teamArena.generic.id = ID_TEAMARENA;
376 s_main.teamArena.generic.callback = Main_MenuEvent;
377 s_main.teamArena.string = "TEAM ARENA";
378 s_main.teamArena.color = color_red;
379 s_main.teamArena.style = style;
380 }
381
382 y += MAIN_MENU_VERTICAL_SPACING;
383 s_main.mods.generic.type = MTYPE_PTEXT;
384 s_main.mods.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
385 s_main.mods.generic.x = 320;
386 s_main.mods.generic.y = y;
387 s_main.mods.generic.id = ID_MODS;
388 s_main.mods.generic.callback = Main_MenuEvent;
389 s_main.mods.string = "MODS";
390 s_main.mods.color = color_red;
391 s_main.mods.style = style;
392
393 y += MAIN_MENU_VERTICAL_SPACING;
394 s_main.exit.generic.type = MTYPE_PTEXT;
395 s_main.exit.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
396 s_main.exit.generic.x = 320;
397 s_main.exit.generic.y = y;
398 s_main.exit.generic.id = ID_EXIT;
399 s_main.exit.generic.callback = Main_MenuEvent;
400 s_main.exit.string = "EXIT";
401 s_main.exit.color = color_red;
402 s_main.exit.style = style;
403
404 Menu_AddItem( &s_main.menu, &s_main.singleplayer );
405 Menu_AddItem( &s_main.menu, &s_main.multiplayer );
406 Menu_AddItem( &s_main.menu, &s_main.setup );
407 Menu_AddItem( &s_main.menu, &s_main.demos );
408 Menu_AddItem( &s_main.menu, &s_main.cinematics );
409 if (teamArena) {
410 Menu_AddItem( &s_main.menu, &s_main.teamArena );
411 }
412 Menu_AddItem( &s_main.menu, &s_main.mods );
413 Menu_AddItem( &s_main.menu, &s_main.exit );
414
415 trap_Key_SetCatcher( KEYCATCH_UI );
416 uis.menusp = 0;
417 UI_PushMenu ( &s_main.menu );
418
419 }
420