ui_startserver.c 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969
  1. /*
  2. ===========================================================================
  3. Copyright (C) 1999-2005 Id Software, Inc.
  4. This file is part of Quake III Arena source code.
  5. Quake III Arena source code is free software; you can redistribute it
  6. and/or modify it under the terms of the GNU General Public License as
  7. published by the Free Software Foundation; either version 2 of the License,
  8. or (at your option) any later version.
  9. Quake III Arena source code is distributed in the hope that it will be
  10. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Foobar; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. ===========================================================================
  17. */
  18. //
  19. /*
  20. =============================================================================
  21. START SERVER MENU *****
  22. =============================================================================
  23. */
  24. #include "ui_local.h"
  25. #define GAMESERVER_BACK0 "menu/art/back_0"
  26. #define GAMESERVER_BACK1 "menu/art/back_1"
  27. #define GAMESERVER_NEXT0 "menu/art/next_0"
  28. #define GAMESERVER_NEXT1 "menu/art/next_1"
  29. #define GAMESERVER_FRAMEL "menu/art/frame2_l"
  30. #define GAMESERVER_FRAMER "menu/art/frame1_r"
  31. #define GAMESERVER_SELECT "menu/art/maps_select"
  32. #define GAMESERVER_SELECTED "menu/art/maps_selected"
  33. #define GAMESERVER_FIGHT0 "menu/art/fight_0"
  34. #define GAMESERVER_FIGHT1 "menu/art/fight_1"
  35. #define GAMESERVER_UNKNOWNMAP "menu/art/unknownmap"
  36. #define GAMESERVER_ARROWS "menu/art/gs_arrows_0"
  37. #define GAMESERVER_ARROWSL "menu/art/gs_arrows_l"
  38. #define GAMESERVER_ARROWSR "menu/art/gs_arrows_r"
  39. #define MAX_MAPROWS 2
  40. #define MAX_MAPCOLS 2
  41. #define MAX_MAPSPERPAGE 4
  42. #define MAX_SERVERSTEXT 8192
  43. #define MAX_SERVERMAPS 64
  44. #define MAX_NAMELENGTH 16
  45. #define ID_GAMETYPE 10
  46. #define ID_PICTURES 11 // 12, 13, 14
  47. #define ID_PREVPAGE 15
  48. #define ID_NEXTPAGE 16
  49. #define ID_STARTSERVERBACK 17
  50. #define ID_STARTSERVERNEXT 18
  51. typedef struct {
  52. menuframework_s menu;
  53. menutext_s banner;
  54. menubitmap_s framel;
  55. menubitmap_s framer;
  56. menulist_s gametype;
  57. menubitmap_s mappics[MAX_MAPSPERPAGE];
  58. menubitmap_s mapbuttons[MAX_MAPSPERPAGE];
  59. menubitmap_s arrows;
  60. menubitmap_s prevpage;
  61. menubitmap_s nextpage;
  62. menubitmap_s back;
  63. menubitmap_s next;
  64. menutext_s mapname;
  65. menubitmap_s item_null;
  66. qboolean multiplayer;
  67. int currentmap;
  68. int nummaps;
  69. int page;
  70. int maxpages;
  71. char maplist[MAX_SERVERMAPS][MAX_NAMELENGTH];
  72. int mapGamebits[MAX_SERVERMAPS];
  73. } startserver_t;
  74. static startserver_t s_startserver;
  75. static const char *gametype_items[] = {
  76. "Free For All",
  77. "Team Deathmatch",
  78. "Tournament",
  79. "Capture the Flag",
  80. 0
  81. };
  82. static int gametype_remap[] = {GT_FFA, GT_TEAM, GT_TOURNAMENT, GT_CTF};
  83. static int gametype_remap2[] = {0, 2, 0, 1, 3};
  84. // use ui_servers2.c definition
  85. extern const char* punkbuster_items[];
  86. static void UI_ServerOptionsMenu( qboolean multiplayer );
  87. /*
  88. =================
  89. GametypeBits
  90. =================
  91. */
  92. static int GametypeBits( char *string ) {
  93. int bits;
  94. char *p;
  95. char *token;
  96. bits = 0;
  97. p = string;
  98. while( 1 ) {
  99. token = COM_ParseExt( &p, qfalse );
  100. if( token[0] == 0 ) {
  101. break;
  102. }
  103. if( Q_stricmp( token, "ffa" ) == 0 ) {
  104. bits |= 1 << GT_FFA;
  105. continue;
  106. }
  107. if( Q_stricmp( token, "tourney" ) == 0 ) {
  108. bits |= 1 << GT_TOURNAMENT;
  109. continue;
  110. }
  111. if( Q_stricmp( token, "single" ) == 0 ) {
  112. bits |= 1 << GT_SINGLE_PLAYER;
  113. continue;
  114. }
  115. if( Q_stricmp( token, "team" ) == 0 ) {
  116. bits |= 1 << GT_TEAM;
  117. continue;
  118. }
  119. if( Q_stricmp( token, "ctf" ) == 0 ) {
  120. bits |= 1 << GT_CTF;
  121. continue;
  122. }
  123. }
  124. return bits;
  125. }
  126. /*
  127. =================
  128. StartServer_Update
  129. =================
  130. */
  131. static void StartServer_Update( void ) {
  132. int i;
  133. int top;
  134. static char picname[MAX_MAPSPERPAGE][64];
  135. top = s_startserver.page*MAX_MAPSPERPAGE;
  136. for (i=0; i<MAX_MAPSPERPAGE; i++)
  137. {
  138. if (top+i >= s_startserver.nummaps)
  139. break;
  140. Com_sprintf( picname[i], sizeof(picname[i]), "levelshots/%s", s_startserver.maplist[top+i] );
  141. s_startserver.mappics[i].generic.flags &= ~QMF_HIGHLIGHT;
  142. s_startserver.mappics[i].generic.name = picname[i];
  143. s_startserver.mappics[i].shader = 0;
  144. // reset
  145. s_startserver.mapbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
  146. s_startserver.mapbuttons[i].generic.flags &= ~QMF_INACTIVE;
  147. }
  148. for (; i<MAX_MAPSPERPAGE; i++)
  149. {
  150. s_startserver.mappics[i].generic.flags &= ~QMF_HIGHLIGHT;
  151. s_startserver.mappics[i].generic.name = NULL;
  152. s_startserver.mappics[i].shader = 0;
  153. // disable
  154. s_startserver.mapbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
  155. s_startserver.mapbuttons[i].generic.flags |= QMF_INACTIVE;
  156. }
  157. // no servers to start
  158. if( !s_startserver.nummaps ) {
  159. s_startserver.next.generic.flags |= QMF_INACTIVE;
  160. // set the map name
  161. strcpy( s_startserver.mapname.string, "NO MAPS FOUND" );
  162. }
  163. else {
  164. // set the highlight
  165. s_startserver.next.generic.flags &= ~QMF_INACTIVE;
  166. i = s_startserver.currentmap - top;
  167. if ( i >=0 && i < MAX_MAPSPERPAGE )
  168. {
  169. s_startserver.mappics[i].generic.flags |= QMF_HIGHLIGHT;
  170. s_startserver.mapbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
  171. }
  172. // set the map name
  173. strcpy( s_startserver.mapname.string, s_startserver.maplist[s_startserver.currentmap] );
  174. }
  175. Q_strupr( s_startserver.mapname.string );
  176. }
  177. /*
  178. =================
  179. StartServer_MapEvent
  180. =================
  181. */
  182. static void StartServer_MapEvent( void* ptr, int event ) {
  183. if( event != QM_ACTIVATED) {
  184. return;
  185. }
  186. s_startserver.currentmap = (s_startserver.page*MAX_MAPSPERPAGE) + (((menucommon_s*)ptr)->id - ID_PICTURES);
  187. StartServer_Update();
  188. }
  189. /*
  190. =================
  191. StartServer_GametypeEvent
  192. =================
  193. */
  194. static void StartServer_GametypeEvent( void* ptr, int event ) {
  195. int i;
  196. int count;
  197. int gamebits;
  198. int matchbits;
  199. const char *info;
  200. if( event != QM_ACTIVATED) {
  201. return;
  202. }
  203. count = UI_GetNumArenas();
  204. s_startserver.nummaps = 0;
  205. matchbits = 1 << gametype_remap[s_startserver.gametype.curvalue];
  206. if( gametype_remap[s_startserver.gametype.curvalue] == GT_FFA ) {
  207. matchbits |= ( 1 << GT_SINGLE_PLAYER );
  208. }
  209. for( i = 0; i < count; i++ ) {
  210. info = UI_GetArenaInfoByNumber( i );
  211. gamebits = GametypeBits( Info_ValueForKey( info, "type") );
  212. if( !( gamebits & matchbits ) ) {
  213. continue;
  214. }
  215. Q_strncpyz( s_startserver.maplist[s_startserver.nummaps], Info_ValueForKey( info, "map"), MAX_NAMELENGTH );
  216. Q_strupr( s_startserver.maplist[s_startserver.nummaps] );
  217. s_startserver.mapGamebits[s_startserver.nummaps] = gamebits;
  218. s_startserver.nummaps++;
  219. }
  220. s_startserver.maxpages = (s_startserver.nummaps + MAX_MAPSPERPAGE-1)/MAX_MAPSPERPAGE;
  221. s_startserver.page = 0;
  222. s_startserver.currentmap = 0;
  223. StartServer_Update();
  224. }
  225. /*
  226. =================
  227. StartServer_MenuEvent
  228. =================
  229. */
  230. static void StartServer_MenuEvent( void* ptr, int event ) {
  231. if( event != QM_ACTIVATED ) {
  232. return;
  233. }
  234. switch( ((menucommon_s*)ptr)->id ) {
  235. case ID_PREVPAGE:
  236. if( s_startserver.page > 0 ) {
  237. s_startserver.page--;
  238. StartServer_Update();
  239. }
  240. break;
  241. case ID_NEXTPAGE:
  242. if( s_startserver.page < s_startserver.maxpages - 1 ) {
  243. s_startserver.page++;
  244. StartServer_Update();
  245. }
  246. break;
  247. case ID_STARTSERVERNEXT:
  248. trap_Cvar_SetValue( "g_gameType", gametype_remap[s_startserver.gametype.curvalue] );
  249. UI_ServerOptionsMenu( s_startserver.multiplayer );
  250. break;
  251. case ID_STARTSERVERBACK:
  252. UI_PopMenu();
  253. break;
  254. }
  255. }
  256. /*
  257. ===============
  258. StartServer_LevelshotDraw
  259. ===============
  260. */
  261. static void StartServer_LevelshotDraw( void *self ) {
  262. menubitmap_s *b;
  263. int x;
  264. int y;
  265. int w;
  266. int h;
  267. int n;
  268. b = (menubitmap_s *)self;
  269. if( !b->generic.name ) {
  270. return;
  271. }
  272. if( b->generic.name && !b->shader ) {
  273. b->shader = trap_R_RegisterShaderNoMip( b->generic.name );
  274. if( !b->shader && b->errorpic ) {
  275. b->shader = trap_R_RegisterShaderNoMip( b->errorpic );
  276. }
  277. }
  278. if( b->focuspic && !b->focusshader ) {
  279. b->focusshader = trap_R_RegisterShaderNoMip( b->focuspic );
  280. }
  281. x = b->generic.x;
  282. y = b->generic.y;
  283. w = b->width;
  284. h = b->height;
  285. if( b->shader ) {
  286. UI_DrawHandlePic( x, y, w, h, b->shader );
  287. }
  288. x = b->generic.x;
  289. y = b->generic.y + b->height;
  290. UI_FillRect( x, y, b->width, 28, colorBlack );
  291. x += b->width / 2;
  292. y += 4;
  293. n = s_startserver.page * MAX_MAPSPERPAGE + b->generic.id - ID_PICTURES;
  294. UI_DrawString( x, y, s_startserver.maplist[n], UI_CENTER|UI_SMALLFONT, color_orange );
  295. x = b->generic.x;
  296. y = b->generic.y;
  297. w = b->width;
  298. h = b->height + 28;
  299. if( b->generic.flags & QMF_HIGHLIGHT ) {
  300. UI_DrawHandlePic( x, y, w, h, b->focusshader );
  301. }
  302. }
  303. /*
  304. =================
  305. StartServer_MenuInit
  306. =================
  307. */
  308. static void StartServer_MenuInit( void ) {
  309. int i;
  310. int x;
  311. int y;
  312. static char mapnamebuffer[64];
  313. // zero set all our globals
  314. memset( &s_startserver, 0 ,sizeof(startserver_t) );
  315. StartServer_Cache();
  316. s_startserver.menu.wrapAround = qtrue;
  317. s_startserver.menu.fullscreen = qtrue;
  318. s_startserver.banner.generic.type = MTYPE_BTEXT;
  319. s_startserver.banner.generic.x = 320;
  320. s_startserver.banner.generic.y = 16;
  321. s_startserver.banner.string = "GAME SERVER";
  322. s_startserver.banner.color = color_white;
  323. s_startserver.banner.style = UI_CENTER;
  324. s_startserver.framel.generic.type = MTYPE_BITMAP;
  325. s_startserver.framel.generic.name = GAMESERVER_FRAMEL;
  326. s_startserver.framel.generic.flags = QMF_INACTIVE;
  327. s_startserver.framel.generic.x = 0;
  328. s_startserver.framel.generic.y = 78;
  329. s_startserver.framel.width = 256;
  330. s_startserver.framel.height = 329;
  331. s_startserver.framer.generic.type = MTYPE_BITMAP;
  332. s_startserver.framer.generic.name = GAMESERVER_FRAMER;
  333. s_startserver.framer.generic.flags = QMF_INACTIVE;
  334. s_startserver.framer.generic.x = 376;
  335. s_startserver.framer.generic.y = 76;
  336. s_startserver.framer.width = 256;
  337. s_startserver.framer.height = 334;
  338. s_startserver.gametype.generic.type = MTYPE_SPINCONTROL;
  339. s_startserver.gametype.generic.name = "Game Type:";
  340. s_startserver.gametype.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  341. s_startserver.gametype.generic.callback = StartServer_GametypeEvent;
  342. s_startserver.gametype.generic.id = ID_GAMETYPE;
  343. s_startserver.gametype.generic.x = 320 - 24;
  344. s_startserver.gametype.generic.y = 368;
  345. s_startserver.gametype.itemnames = gametype_items;
  346. for (i=0; i<MAX_MAPSPERPAGE; i++)
  347. {
  348. x = (i % MAX_MAPCOLS) * (128+8) + 188;
  349. y = (i / MAX_MAPROWS) * (128+8) + 96;
  350. s_startserver.mappics[i].generic.type = MTYPE_BITMAP;
  351. s_startserver.mappics[i].generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
  352. s_startserver.mappics[i].generic.x = x;
  353. s_startserver.mappics[i].generic.y = y;
  354. s_startserver.mappics[i].generic.id = ID_PICTURES+i;
  355. s_startserver.mappics[i].width = 128;
  356. s_startserver.mappics[i].height = 96;
  357. s_startserver.mappics[i].focuspic = GAMESERVER_SELECTED;
  358. s_startserver.mappics[i].errorpic = GAMESERVER_UNKNOWNMAP;
  359. s_startserver.mappics[i].generic.ownerdraw = StartServer_LevelshotDraw;
  360. s_startserver.mapbuttons[i].generic.type = MTYPE_BITMAP;
  361. s_startserver.mapbuttons[i].generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_NODEFAULTINIT;
  362. s_startserver.mapbuttons[i].generic.id = ID_PICTURES+i;
  363. s_startserver.mapbuttons[i].generic.callback = StartServer_MapEvent;
  364. s_startserver.mapbuttons[i].generic.x = x - 30;
  365. s_startserver.mapbuttons[i].generic.y = y - 32;
  366. s_startserver.mapbuttons[i].width = 256;
  367. s_startserver.mapbuttons[i].height = 248;
  368. s_startserver.mapbuttons[i].generic.left = x;
  369. s_startserver.mapbuttons[i].generic.top = y;
  370. s_startserver.mapbuttons[i].generic.right = x + 128;
  371. s_startserver.mapbuttons[i].generic.bottom = y + 128;
  372. s_startserver.mapbuttons[i].focuspic = GAMESERVER_SELECT;
  373. }
  374. s_startserver.arrows.generic.type = MTYPE_BITMAP;
  375. s_startserver.arrows.generic.name = GAMESERVER_ARROWS;
  376. s_startserver.arrows.generic.flags = QMF_INACTIVE;
  377. s_startserver.arrows.generic.x = 260;
  378. s_startserver.arrows.generic.y = 400;
  379. s_startserver.arrows.width = 128;
  380. s_startserver.arrows.height = 32;
  381. s_startserver.prevpage.generic.type = MTYPE_BITMAP;
  382. s_startserver.prevpage.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
  383. s_startserver.prevpage.generic.callback = StartServer_MenuEvent;
  384. s_startserver.prevpage.generic.id = ID_PREVPAGE;
  385. s_startserver.prevpage.generic.x = 260;
  386. s_startserver.prevpage.generic.y = 400;
  387. s_startserver.prevpage.width = 64;
  388. s_startserver.prevpage.height = 32;
  389. s_startserver.prevpage.focuspic = GAMESERVER_ARROWSL;
  390. s_startserver.nextpage.generic.type = MTYPE_BITMAP;
  391. s_startserver.nextpage.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
  392. s_startserver.nextpage.generic.callback = StartServer_MenuEvent;
  393. s_startserver.nextpage.generic.id = ID_NEXTPAGE;
  394. s_startserver.nextpage.generic.x = 321;
  395. s_startserver.nextpage.generic.y = 400;
  396. s_startserver.nextpage.width = 64;
  397. s_startserver.nextpage.height = 32;
  398. s_startserver.nextpage.focuspic = GAMESERVER_ARROWSR;
  399. s_startserver.mapname.generic.type = MTYPE_PTEXT;
  400. s_startserver.mapname.generic.flags = QMF_CENTER_JUSTIFY|QMF_INACTIVE;
  401. s_startserver.mapname.generic.x = 320;
  402. s_startserver.mapname.generic.y = 440;
  403. s_startserver.mapname.string = mapnamebuffer;
  404. s_startserver.mapname.style = UI_CENTER|UI_BIGFONT;
  405. s_startserver.mapname.color = text_color_normal;
  406. s_startserver.back.generic.type = MTYPE_BITMAP;
  407. s_startserver.back.generic.name = GAMESERVER_BACK0;
  408. s_startserver.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
  409. s_startserver.back.generic.callback = StartServer_MenuEvent;
  410. s_startserver.back.generic.id = ID_STARTSERVERBACK;
  411. s_startserver.back.generic.x = 0;
  412. s_startserver.back.generic.y = 480-64;
  413. s_startserver.back.width = 128;
  414. s_startserver.back.height = 64;
  415. s_startserver.back.focuspic = GAMESERVER_BACK1;
  416. s_startserver.next.generic.type = MTYPE_BITMAP;
  417. s_startserver.next.generic.name = GAMESERVER_NEXT0;
  418. s_startserver.next.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
  419. s_startserver.next.generic.callback = StartServer_MenuEvent;
  420. s_startserver.next.generic.id = ID_STARTSERVERNEXT;
  421. s_startserver.next.generic.x = 640;
  422. s_startserver.next.generic.y = 480-64;
  423. s_startserver.next.width = 128;
  424. s_startserver.next.height = 64;
  425. s_startserver.next.focuspic = GAMESERVER_NEXT1;
  426. s_startserver.item_null.generic.type = MTYPE_BITMAP;
  427. s_startserver.item_null.generic.flags = QMF_LEFT_JUSTIFY|QMF_MOUSEONLY|QMF_SILENT;
  428. s_startserver.item_null.generic.x = 0;
  429. s_startserver.item_null.generic.y = 0;
  430. s_startserver.item_null.width = 640;
  431. s_startserver.item_null.height = 480;
  432. Menu_AddItem( &s_startserver.menu, &s_startserver.banner );
  433. Menu_AddItem( &s_startserver.menu, &s_startserver.framel );
  434. Menu_AddItem( &s_startserver.menu, &s_startserver.framer );
  435. Menu_AddItem( &s_startserver.menu, &s_startserver.gametype );
  436. for (i=0; i<MAX_MAPSPERPAGE; i++)
  437. {
  438. Menu_AddItem( &s_startserver.menu, &s_startserver.mappics[i] );
  439. Menu_AddItem( &s_startserver.menu, &s_startserver.mapbuttons[i] );
  440. }
  441. Menu_AddItem( &s_startserver.menu, &s_startserver.arrows );
  442. Menu_AddItem( &s_startserver.menu, &s_startserver.prevpage );
  443. Menu_AddItem( &s_startserver.menu, &s_startserver.nextpage );
  444. Menu_AddItem( &s_startserver.menu, &s_startserver.back );
  445. Menu_AddItem( &s_startserver.menu, &s_startserver.next );
  446. Menu_AddItem( &s_startserver.menu, &s_startserver.mapname );
  447. Menu_AddItem( &s_startserver.menu, &s_startserver.item_null );
  448. StartServer_GametypeEvent( NULL, QM_ACTIVATED );
  449. }
  450. /*
  451. =================
  452. StartServer_Cache
  453. =================
  454. */
  455. void StartServer_Cache( void )
  456. {
  457. int i;
  458. const char *info;
  459. qboolean precache;
  460. char picname[64];
  461. trap_R_RegisterShaderNoMip( GAMESERVER_BACK0 );
  462. trap_R_RegisterShaderNoMip( GAMESERVER_BACK1 );
  463. trap_R_RegisterShaderNoMip( GAMESERVER_NEXT0 );
  464. trap_R_RegisterShaderNoMip( GAMESERVER_NEXT1 );
  465. trap_R_RegisterShaderNoMip( GAMESERVER_FRAMEL );
  466. trap_R_RegisterShaderNoMip( GAMESERVER_FRAMER );
  467. trap_R_RegisterShaderNoMip( GAMESERVER_SELECT );
  468. trap_R_RegisterShaderNoMip( GAMESERVER_SELECTED );
  469. trap_R_RegisterShaderNoMip( GAMESERVER_UNKNOWNMAP );
  470. trap_R_RegisterShaderNoMip( GAMESERVER_ARROWS );
  471. trap_R_RegisterShaderNoMip( GAMESERVER_ARROWSL );
  472. trap_R_RegisterShaderNoMip( GAMESERVER_ARROWSR );
  473. precache = trap_Cvar_VariableValue("com_buildscript");
  474. s_startserver.nummaps = UI_GetNumArenas();
  475. for( i = 0; i < s_startserver.nummaps; i++ ) {
  476. info = UI_GetArenaInfoByNumber( i );
  477. Q_strncpyz( s_startserver.maplist[i], Info_ValueForKey( info, "map"), MAX_NAMELENGTH );
  478. Q_strupr( s_startserver.maplist[i] );
  479. s_startserver.mapGamebits[i] = GametypeBits( Info_ValueForKey( info, "type") );
  480. if( precache ) {
  481. Com_sprintf( picname, sizeof(picname), "levelshots/%s", s_startserver.maplist[i] );
  482. trap_R_RegisterShaderNoMip(picname);
  483. }
  484. }
  485. s_startserver.maxpages = (s_startserver.nummaps + MAX_MAPSPERPAGE-1)/MAX_MAPSPERPAGE;
  486. }
  487. /*
  488. =================
  489. UI_StartServerMenu
  490. =================
  491. */
  492. void UI_StartServerMenu( qboolean multiplayer ) {
  493. StartServer_MenuInit();
  494. s_startserver.multiplayer = multiplayer;
  495. UI_PushMenu( &s_startserver.menu );
  496. }
  497. /*
  498. =============================================================================
  499. SERVER OPTIONS MENU *****
  500. =============================================================================
  501. */
  502. #define ID_PLAYER_TYPE 20
  503. #define ID_MAXCLIENTS 21
  504. #define ID_DEDICATED 22
  505. #define ID_GO 23
  506. #define ID_BACK 24
  507. #define PLAYER_SLOTS 12
  508. typedef struct {
  509. menuframework_s menu;
  510. menutext_s banner;
  511. menubitmap_s mappic;
  512. menubitmap_s picframe;
  513. menulist_s dedicated;
  514. menufield_s timelimit;
  515. menufield_s fraglimit;
  516. menufield_s flaglimit;
  517. menuradiobutton_s friendlyfire;
  518. menufield_s hostname;
  519. menuradiobutton_s pure;
  520. menulist_s botSkill;
  521. menutext_s player0;
  522. menulist_s playerType[PLAYER_SLOTS];
  523. menutext_s playerName[PLAYER_SLOTS];
  524. menulist_s playerTeam[PLAYER_SLOTS];
  525. menubitmap_s go;
  526. menubitmap_s next;
  527. menubitmap_s back;
  528. qboolean multiplayer;
  529. int gametype;
  530. char mapnamebuffer[32];
  531. char playerNameBuffers[PLAYER_SLOTS][16];
  532. qboolean newBot;
  533. int newBotIndex;
  534. char newBotName[16];
  535. menulist_s punkbuster;
  536. } serveroptions_t;
  537. static serveroptions_t s_serveroptions;
  538. static const char *dedicated_list[] = {
  539. "No",
  540. "LAN",
  541. "Internet",
  542. 0
  543. };
  544. static const char *playerType_list[] = {
  545. "Open",
  546. "Bot",
  547. "----",
  548. 0
  549. };
  550. static const char *playerTeam_list[] = {
  551. "Blue",
  552. "Red",
  553. 0
  554. };
  555. static const char *botSkill_list[] = {
  556. "I Can Win",
  557. "Bring It On",
  558. "Hurt Me Plenty",
  559. "Hardcore",
  560. "Nightmare!",
  561. 0
  562. };
  563. /*
  564. =================
  565. BotAlreadySelected
  566. =================
  567. */
  568. static qboolean BotAlreadySelected( const char *checkName ) {
  569. int n;
  570. for( n = 1; n < PLAYER_SLOTS; n++ ) {
  571. if( s_serveroptions.playerType[n].curvalue != 1 ) {
  572. continue;
  573. }
  574. if( (s_serveroptions.gametype >= GT_TEAM) &&
  575. (s_serveroptions.playerTeam[n].curvalue != s_serveroptions.playerTeam[s_serveroptions.newBotIndex].curvalue ) ) {
  576. continue;
  577. }
  578. if( Q_stricmp( checkName, s_serveroptions.playerNameBuffers[n] ) == 0 ) {
  579. return qtrue;
  580. }
  581. }
  582. return qfalse;
  583. }
  584. /*
  585. =================
  586. ServerOptions_Start
  587. =================
  588. */
  589. static void ServerOptions_Start( void ) {
  590. int timelimit;
  591. int fraglimit;
  592. int maxclients;
  593. int dedicated;
  594. int friendlyfire;
  595. int flaglimit;
  596. int pure;
  597. int skill;
  598. int n;
  599. char buf[64];
  600. timelimit = atoi( s_serveroptions.timelimit.field.buffer );
  601. fraglimit = atoi( s_serveroptions.fraglimit.field.buffer );
  602. flaglimit = atoi( s_serveroptions.flaglimit.field.buffer );
  603. dedicated = s_serveroptions.dedicated.curvalue;
  604. friendlyfire = s_serveroptions.friendlyfire.curvalue;
  605. pure = s_serveroptions.pure.curvalue;
  606. skill = s_serveroptions.botSkill.curvalue + 1;
  607. //set maxclients
  608. for( n = 0, maxclients = 0; n < PLAYER_SLOTS; n++ ) {
  609. if( s_serveroptions.playerType[n].curvalue == 2 ) {
  610. continue;
  611. }
  612. if( (s_serveroptions.playerType[n].curvalue == 1) && (s_serveroptions.playerNameBuffers[n][0] == 0) ) {
  613. continue;
  614. }
  615. maxclients++;
  616. }
  617. switch( s_serveroptions.gametype ) {
  618. case GT_FFA:
  619. default:
  620. trap_Cvar_SetValue( "ui_ffa_fraglimit", fraglimit );
  621. trap_Cvar_SetValue( "ui_ffa_timelimit", timelimit );
  622. break;
  623. case GT_TOURNAMENT:
  624. trap_Cvar_SetValue( "ui_tourney_fraglimit", fraglimit );
  625. trap_Cvar_SetValue( "ui_tourney_timelimit", timelimit );
  626. break;
  627. case GT_TEAM:
  628. trap_Cvar_SetValue( "ui_team_fraglimit", fraglimit );
  629. trap_Cvar_SetValue( "ui_team_timelimit", timelimit );
  630. trap_Cvar_SetValue( "ui_team_friendlt", friendlyfire );
  631. break;
  632. case GT_CTF:
  633. trap_Cvar_SetValue( "ui_ctf_fraglimit", fraglimit );
  634. trap_Cvar_SetValue( "ui_ctf_timelimit", timelimit );
  635. trap_Cvar_SetValue( "ui_ctf_friendlt", friendlyfire );
  636. break;
  637. }
  638. trap_Cvar_SetValue( "sv_maxclients", Com_Clamp( 0, 12, maxclients ) );
  639. trap_Cvar_SetValue( "dedicated", Com_Clamp( 0, 2, dedicated ) );
  640. trap_Cvar_SetValue ("timelimit", Com_Clamp( 0, timelimit, timelimit ) );
  641. trap_Cvar_SetValue ("fraglimit", Com_Clamp( 0, fraglimit, fraglimit ) );
  642. trap_Cvar_SetValue ("capturelimit", Com_Clamp( 0, flaglimit, flaglimit ) );
  643. trap_Cvar_SetValue( "g_friendlyfire", friendlyfire );
  644. trap_Cvar_SetValue( "sv_pure", pure );
  645. trap_Cvar_Set("sv_hostname", s_serveroptions.hostname.field.buffer );
  646. trap_Cvar_SetValue( "sv_punkbuster", s_serveroptions.punkbuster.curvalue );
  647. // the wait commands will allow the dedicated to take effect
  648. trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", s_startserver.maplist[s_startserver.currentmap] ) );
  649. // add bots
  650. trap_Cmd_ExecuteText( EXEC_APPEND, "wait 3\n" );
  651. for( n = 1; n < PLAYER_SLOTS; n++ ) {
  652. if( s_serveroptions.playerType[n].curvalue != 1 ) {
  653. continue;
  654. }
  655. if( s_serveroptions.playerNameBuffers[n][0] == 0 ) {
  656. continue;
  657. }
  658. if( s_serveroptions.playerNameBuffers[n][0] == '-' ) {
  659. continue;
  660. }
  661. if( s_serveroptions.gametype >= GT_TEAM ) {
  662. Com_sprintf( buf, sizeof(buf), "addbot %s %i %s\n", s_serveroptions.playerNameBuffers[n], skill,
  663. playerTeam_list[s_serveroptions.playerTeam[n].curvalue] );
  664. }
  665. else {
  666. Com_sprintf( buf, sizeof(buf), "addbot %s %i\n", s_serveroptions.playerNameBuffers[n], skill );
  667. }
  668. trap_Cmd_ExecuteText( EXEC_APPEND, buf );
  669. }
  670. // set player's team
  671. if( dedicated == 0 && s_serveroptions.gametype >= GT_TEAM ) {
  672. trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait 5; team %s\n", playerTeam_list[s_serveroptions.playerTeam[0].curvalue] ) );
  673. }
  674. }
  675. /*
  676. =================
  677. ServerOptions_InitPlayerItems
  678. =================
  679. */
  680. static void ServerOptions_InitPlayerItems( void ) {
  681. int n;
  682. int v;
  683. // init types
  684. if( s_serveroptions.multiplayer ) {
  685. v = 0; // open
  686. }
  687. else {
  688. v = 1; // bot
  689. }
  690. for( n = 0; n < PLAYER_SLOTS; n++ ) {
  691. s_serveroptions.playerType[n].curvalue = v;
  692. }
  693. if( s_serveroptions.multiplayer && (s_serveroptions.gametype < GT_TEAM) ) {
  694. for( n = 8; n < PLAYER_SLOTS; n++ ) {
  695. s_serveroptions.playerType[n].curvalue = 2;
  696. }
  697. }
  698. // if not a dedicated server, first slot is reserved for the human on the server
  699. if( s_serveroptions.dedicated.curvalue == 0 ) {
  700. // human
  701. s_serveroptions.playerType[0].generic.flags |= QMF_INACTIVE;
  702. s_serveroptions.playerType[0].curvalue = 0;
  703. trap_Cvar_VariableStringBuffer( "name", s_serveroptions.playerNameBuffers[0], sizeof(s_serveroptions.playerNameBuffers[0]) );
  704. Q_CleanStr( s_serveroptions.playerNameBuffers[0] );
  705. }
  706. // init teams
  707. if( s_serveroptions.gametype >= GT_TEAM ) {
  708. for( n = 0; n < (PLAYER_SLOTS / 2); n++ ) {
  709. s_serveroptions.playerTeam[n].curvalue = 0;
  710. }
  711. for( ; n < PLAYER_SLOTS; n++ ) {
  712. s_serveroptions.playerTeam[n].curvalue = 1;
  713. }
  714. }
  715. else {
  716. for( n = 0; n < PLAYER_SLOTS; n++ ) {
  717. s_serveroptions.playerTeam[n].generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
  718. }
  719. }
  720. }
  721. /*
  722. =================
  723. ServerOptions_SetPlayerItems
  724. =================
  725. */
  726. static void ServerOptions_SetPlayerItems( void ) {
  727. int start;
  728. int n;
  729. // types
  730. // for( n = 0; n < PLAYER_SLOTS; n++ ) {
  731. // if( (!s_serveroptions.multiplayer) && (n > 0) && (s_serveroptions.playerType[n].curvalue == 0) ) {
  732. // s_serveroptions.playerType[n].curvalue = 1;
  733. // }
  734. // }
  735. // names
  736. if( s_serveroptions.dedicated.curvalue == 0 ) {
  737. s_serveroptions.player0.string = "Human";
  738. s_serveroptions.playerName[0].generic.flags &= ~QMF_HIDDEN;
  739. start = 1;
  740. }
  741. else {
  742. s_serveroptions.player0.string = "Open";
  743. start = 0;
  744. }
  745. for( n = start; n < PLAYER_SLOTS; n++ ) {
  746. if( s_serveroptions.playerType[n].curvalue == 1 ) {
  747. s_serveroptions.playerName[n].generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
  748. }
  749. else {
  750. s_serveroptions.playerName[n].generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
  751. }
  752. }
  753. // teams
  754. if( s_serveroptions.gametype < GT_TEAM ) {
  755. return;
  756. }
  757. for( n = start; n < PLAYER_SLOTS; n++ ) {
  758. if( s_serveroptions.playerType[n].curvalue == 2 ) {
  759. s_serveroptions.playerTeam[n].generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
  760. }
  761. else {
  762. s_serveroptions.playerTeam[n].generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
  763. }
  764. }
  765. }
  766. /*
  767. =================
  768. ServerOptions_Event
  769. =================
  770. */
  771. static void ServerOptions_Event( void* ptr, int event ) {
  772. switch( ((menucommon_s*)ptr)->id ) {
  773. //if( event != QM_ACTIVATED && event != QM_LOSTFOCUS) {
  774. // return;
  775. //}
  776. case ID_PLAYER_TYPE:
  777. if( event != QM_ACTIVATED ) {
  778. break;
  779. }
  780. ServerOptions_SetPlayerItems();
  781. break;
  782. case ID_MAXCLIENTS:
  783. case ID_DEDICATED:
  784. ServerOptions_SetPlayerItems();
  785. break;
  786. case ID_GO:
  787. if( event != QM_ACTIVATED ) {
  788. break;
  789. }
  790. ServerOptions_Start();
  791. break;
  792. case ID_STARTSERVERNEXT:
  793. if( event != QM_ACTIVATED ) {
  794. break;
  795. }
  796. break;
  797. case ID_BACK:
  798. if( event != QM_ACTIVATED ) {
  799. break;
  800. }
  801. UI_PopMenu();
  802. break;
  803. }
  804. }
  805. static void ServerOptions_PlayerNameEvent( void* ptr, int event ) {
  806. int n;
  807. if( event != QM_ACTIVATED ) {
  808. return;
  809. }
  810. n = ((menutext_s*)ptr)->generic.id;
  811. s_serveroptions.newBotIndex = n;
  812. UI_BotSelectMenu( s_serveroptions.playerNameBuffers[n] );
  813. }
  814. /*
  815. =================
  816. ServerOptions_StatusBar
  817. =================
  818. */
  819. static void ServerOptions_StatusBar( void* ptr ) {
  820. switch( ((menucommon_s*)ptr)->id ) {
  821. default:
  822. UI_DrawString( 320, 440, "0 = NO LIMIT", UI_CENTER|UI_SMALLFONT, colorWhite );
  823. break;
  824. }
  825. }
  826. /*
  827. ===============
  828. ServerOptions_LevelshotDraw
  829. ===============
  830. */
  831. static void ServerOptions_LevelshotDraw( void *self ) {
  832. menubitmap_s *b;
  833. int x;
  834. int y;
  835. // strange place for this, but it works
  836. if( s_serveroptions.newBot ) {
  837. Q_strncpyz( s_serveroptions.playerNameBuffers[s_serveroptions.newBotIndex], s_serveroptions.newBotName, 16 );
  838. s_serveroptions.newBot = qfalse;
  839. }
  840. b = (menubitmap_s *)self;
  841. Bitmap_Draw( b );
  842. x = b->generic.x;
  843. y = b->generic.y + b->height;
  844. UI_FillRect( x, y, b->width, 40, colorBlack );
  845. x += b->width / 2;
  846. y += 4;
  847. UI_DrawString( x, y, s_serveroptions.mapnamebuffer, UI_CENTER|UI_SMALLFONT, color_orange );
  848. y += SMALLCHAR_HEIGHT;
  849. UI_DrawString( x, y, gametype_items[gametype_remap2[s_serveroptions.gametype]], UI_CENTER|UI_SMALLFONT, color_orange );
  850. }
  851. static void ServerOptions_InitBotNames( void ) {
  852. int count;
  853. int n;
  854. const char *arenaInfo;
  855. const char *botInfo;
  856. char *p;
  857. char *bot;
  858. char bots[MAX_INFO_STRING];
  859. if( s_serveroptions.gametype >= GT_TEAM ) {
  860. Q_strncpyz( s_serveroptions.playerNameBuffers[1], "grunt", 16 );
  861. Q_strncpyz( s_serveroptions.playerNameBuffers[2], "major", 16 );
  862. if( s_serveroptions.gametype == GT_TEAM ) {
  863. Q_strncpyz( s_serveroptions.playerNameBuffers[3], "visor", 16 );
  864. }
  865. else {
  866. s_serveroptions.playerType[3].curvalue = 2;
  867. }
  868. s_serveroptions.playerType[4].curvalue = 2;
  869. s_serveroptions.playerType[5].curvalue = 2;
  870. Q_strncpyz( s_serveroptions.playerNameBuffers[6], "sarge", 16 );
  871. Q_strncpyz( s_serveroptions.playerNameBuffers[7], "grunt", 16 );
  872. Q_strncpyz( s_serveroptions.playerNameBuffers[8], "major", 16 );
  873. if( s_serveroptions.gametype == GT_TEAM ) {
  874. Q_strncpyz( s_serveroptions.playerNameBuffers[9], "visor", 16 );
  875. }
  876. else {
  877. s_serveroptions.playerType[9].curvalue = 2;
  878. }
  879. s_serveroptions.playerType[10].curvalue = 2;
  880. s_serveroptions.playerType[11].curvalue = 2;
  881. return;
  882. }
  883. count = 1; // skip the first slot, reserved for a human
  884. // get info for this map
  885. arenaInfo = UI_GetArenaInfoByMap( s_serveroptions.mapnamebuffer );
  886. // get the bot info - we'll seed with them if any are listed
  887. Q_strncpyz( bots, Info_ValueForKey( arenaInfo, "bots" ), sizeof(bots) );
  888. p = &bots[0];
  889. while( *p && count < PLAYER_SLOTS ) {
  890. //skip spaces
  891. while( *p && *p == ' ' ) {
  892. p++;
  893. }
  894. if( !p ) {
  895. break;
  896. }
  897. // mark start of bot name
  898. bot = p;
  899. // skip until space of null
  900. while( *p && *p != ' ' ) {
  901. p++;
  902. }
  903. if( *p ) {
  904. *p++ = 0;
  905. }
  906. botInfo = UI_GetBotInfoByName( bot );
  907. bot = Info_ValueForKey( botInfo, "name" );
  908. Q_strncpyz( s_serveroptions.playerNameBuffers[count], bot, sizeof(s_serveroptions.playerNameBuffers[count]) );
  909. count++;
  910. }
  911. // set the rest of the bot slots to "---"
  912. for( n = count; n < PLAYER_SLOTS; n++ ) {
  913. strcpy( s_serveroptions.playerNameBuffers[n], "--------" );
  914. }
  915. // pad up to #8 as open slots
  916. for( ;count < 8; count++ ) {
  917. s_serveroptions.playerType[count].curvalue = 0;
  918. }
  919. // close off the rest by default
  920. for( ;count < PLAYER_SLOTS; count++ ) {
  921. if( s_serveroptions.playerType[count].curvalue == 1 ) {
  922. s_serveroptions.playerType[count].curvalue = 2;
  923. }
  924. }
  925. }
  926. /*
  927. =================
  928. ServerOptions_SetMenuItems
  929. =================
  930. */
  931. static void ServerOptions_SetMenuItems( void ) {
  932. static char picname[64];
  933. switch( s_serveroptions.gametype ) {
  934. case GT_FFA:
  935. default:
  936. Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ffa_fraglimit" ) ) );
  937. Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ffa_timelimit" ) ) );
  938. break;
  939. case GT_TOURNAMENT:
  940. Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_tourney_fraglimit" ) ) );
  941. Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_tourney_timelimit" ) ) );
  942. break;
  943. case GT_TEAM:
  944. Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_team_fraglimit" ) ) );
  945. Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_team_timelimit" ) ) );
  946. s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_team_friendly" ) );
  947. break;
  948. case GT_CTF:
  949. Com_sprintf( s_serveroptions.flaglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 100, trap_Cvar_VariableValue( "ui_ctf_capturelimit" ) ) );
  950. Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ctf_timelimit" ) ) );
  951. s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_ctf_friendly" ) );
  952. break;
  953. }
  954. Q_strncpyz( s_serveroptions.hostname.field.buffer, UI_Cvar_VariableString( "sv_hostname" ), sizeof( s_serveroptions.hostname.field.buffer ) );
  955. s_serveroptions.pure.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "sv_pure" ) );
  956. // set the map pic
  957. Com_sprintf( picname, 64, "levelshots/%s", s_startserver.maplist[s_startserver.currentmap] );
  958. s_serveroptions.mappic.generic.name = picname;
  959. // set the map name
  960. strcpy( s_serveroptions.mapnamebuffer, s_startserver.mapname.string );
  961. Q_strupr( s_serveroptions.mapnamebuffer );
  962. // get the player selections initialized
  963. ServerOptions_InitPlayerItems();
  964. ServerOptions_SetPlayerItems();
  965. // seed bot names
  966. ServerOptions_InitBotNames();
  967. ServerOptions_SetPlayerItems();
  968. }
  969. /*
  970. =================
  971. PlayerName_Draw
  972. =================
  973. */
  974. static void PlayerName_Draw( void *item ) {
  975. menutext_s *s;
  976. float *color;
  977. int x, y;
  978. int style;
  979. qboolean focus;
  980. s = (menutext_s *)item;
  981. x = s->generic.x;
  982. y = s->generic.y;
  983. style = UI_SMALLFONT;
  984. focus = (s->generic.parent->cursor == s->generic.menuPosition);
  985. if ( s->generic.flags & QMF_GRAYED )
  986. color = text_color_disabled;
  987. else if ( focus )
  988. {
  989. color = text_color_highlight;
  990. style |= UI_PULSE;
  991. }
  992. else if ( s->generic.flags & QMF_BLINK )
  993. {
  994. color = text_color_highlight;
  995. style |= UI_BLINK;
  996. }
  997. else
  998. color = text_color_normal;
  999. if ( focus )
  1000. {
  1001. // draw cursor
  1002. UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color );
  1003. UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
  1004. }
  1005. UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, style|UI_RIGHT, color );
  1006. UI_DrawString( x + SMALLCHAR_WIDTH, y, s->string, style|UI_LEFT, color );
  1007. }
  1008. /*
  1009. =================
  1010. ServerOptions_MenuInit
  1011. =================
  1012. */
  1013. #define OPTIONS_X 456
  1014. static void ServerOptions_MenuInit( qboolean multiplayer ) {
  1015. int y;
  1016. int n;
  1017. memset( &s_serveroptions, 0 ,sizeof(serveroptions_t) );
  1018. s_serveroptions.multiplayer = multiplayer;
  1019. s_serveroptions.gametype = (int)Com_Clamp( 0, 5, trap_Cvar_VariableValue( "g_gameType" ) );
  1020. s_serveroptions.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "sv_punkbuster" ) );
  1021. ServerOptions_Cache();
  1022. s_serveroptions.menu.wrapAround = qtrue;
  1023. s_serveroptions.menu.fullscreen = qtrue;
  1024. s_serveroptions.banner.generic.type = MTYPE_BTEXT;
  1025. s_serveroptions.banner.generic.x = 320;
  1026. s_serveroptions.banner.generic.y = 16;
  1027. s_serveroptions.banner.string = "GAME SERVER";
  1028. s_serveroptions.banner.color = color_white;
  1029. s_serveroptions.banner.style = UI_CENTER;
  1030. s_serveroptions.mappic.generic.type = MTYPE_BITMAP;
  1031. s_serveroptions.mappic.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
  1032. s_serveroptions.mappic.generic.x = 352;
  1033. s_serveroptions.mappic.generic.y = 80;
  1034. s_serveroptions.mappic.width = 160;
  1035. s_serveroptions.mappic.height = 120;
  1036. s_serveroptions.mappic.errorpic = GAMESERVER_UNKNOWNMAP;
  1037. s_serveroptions.mappic.generic.ownerdraw = ServerOptions_LevelshotDraw;
  1038. s_serveroptions.picframe.generic.type = MTYPE_BITMAP;
  1039. s_serveroptions.picframe.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE|QMF_HIGHLIGHT;
  1040. s_serveroptions.picframe.generic.x = 352 - 38;
  1041. s_serveroptions.picframe.generic.y = 80 - 40;
  1042. s_serveroptions.picframe.width = 320;
  1043. s_serveroptions.picframe.height = 320;
  1044. s_serveroptions.picframe.focuspic = GAMESERVER_SELECT;
  1045. y = 272;
  1046. if( s_serveroptions.gametype != GT_CTF ) {
  1047. s_serveroptions.fraglimit.generic.type = MTYPE_FIELD;
  1048. s_serveroptions.fraglimit.generic.name = "Frag Limit:";
  1049. s_serveroptions.fraglimit.generic.flags = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1050. s_serveroptions.fraglimit.generic.x = OPTIONS_X;
  1051. s_serveroptions.fraglimit.generic.y = y;
  1052. s_serveroptions.fraglimit.generic.statusbar = ServerOptions_StatusBar;
  1053. s_serveroptions.fraglimit.field.widthInChars = 3;
  1054. s_serveroptions.fraglimit.field.maxchars = 3;
  1055. }
  1056. else {
  1057. s_serveroptions.flaglimit.generic.type = MTYPE_FIELD;
  1058. s_serveroptions.flaglimit.generic.name = "Capture Limit:";
  1059. s_serveroptions.flaglimit.generic.flags = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1060. s_serveroptions.flaglimit.generic.x = OPTIONS_X;
  1061. s_serveroptions.flaglimit.generic.y = y;
  1062. s_serveroptions.flaglimit.generic.statusbar = ServerOptions_StatusBar;
  1063. s_serveroptions.flaglimit.field.widthInChars = 3;
  1064. s_serveroptions.flaglimit.field.maxchars = 3;
  1065. }
  1066. y += BIGCHAR_HEIGHT+2;
  1067. s_serveroptions.timelimit.generic.type = MTYPE_FIELD;
  1068. s_serveroptions.timelimit.generic.name = "Time Limit:";
  1069. s_serveroptions.timelimit.generic.flags = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1070. s_serveroptions.timelimit.generic.x = OPTIONS_X;
  1071. s_serveroptions.timelimit.generic.y = y;
  1072. s_serveroptions.timelimit.generic.statusbar = ServerOptions_StatusBar;
  1073. s_serveroptions.timelimit.field.widthInChars = 3;
  1074. s_serveroptions.timelimit.field.maxchars = 3;
  1075. if( s_serveroptions.gametype >= GT_TEAM ) {
  1076. y += BIGCHAR_HEIGHT+2;
  1077. s_serveroptions.friendlyfire.generic.type = MTYPE_RADIOBUTTON;
  1078. s_serveroptions.friendlyfire.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1079. s_serveroptions.friendlyfire.generic.x = OPTIONS_X;
  1080. s_serveroptions.friendlyfire.generic.y = y;
  1081. s_serveroptions.friendlyfire.generic.name = "Friendly Fire:";
  1082. }
  1083. y += BIGCHAR_HEIGHT+2;
  1084. s_serveroptions.pure.generic.type = MTYPE_RADIOBUTTON;
  1085. s_serveroptions.pure.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1086. s_serveroptions.pure.generic.x = OPTIONS_X;
  1087. s_serveroptions.pure.generic.y = y;
  1088. s_serveroptions.pure.generic.name = "Pure Server:";
  1089. if( s_serveroptions.multiplayer ) {
  1090. y += BIGCHAR_HEIGHT+2;
  1091. s_serveroptions.dedicated.generic.type = MTYPE_SPINCONTROL;
  1092. s_serveroptions.dedicated.generic.id = ID_DEDICATED;
  1093. s_serveroptions.dedicated.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1094. s_serveroptions.dedicated.generic.callback = ServerOptions_Event;
  1095. s_serveroptions.dedicated.generic.x = OPTIONS_X;
  1096. s_serveroptions.dedicated.generic.y = y;
  1097. s_serveroptions.dedicated.generic.name = "Dedicated:";
  1098. s_serveroptions.dedicated.itemnames = dedicated_list;
  1099. }
  1100. if( s_serveroptions.multiplayer ) {
  1101. y += BIGCHAR_HEIGHT+2;
  1102. s_serveroptions.hostname.generic.type = MTYPE_FIELD;
  1103. s_serveroptions.hostname.generic.name = "Hostname:";
  1104. s_serveroptions.hostname.generic.flags = QMF_SMALLFONT;
  1105. s_serveroptions.hostname.generic.x = OPTIONS_X;
  1106. s_serveroptions.hostname.generic.y = y;
  1107. s_serveroptions.hostname.field.widthInChars = 18;
  1108. s_serveroptions.hostname.field.maxchars = 64;
  1109. }
  1110. y += BIGCHAR_HEIGHT+2;
  1111. s_serveroptions.punkbuster.generic.type = MTYPE_SPINCONTROL;
  1112. s_serveroptions.punkbuster.generic.name = "Punkbuster:";
  1113. s_serveroptions.punkbuster.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1114. s_serveroptions.punkbuster.generic.id = 0;
  1115. s_serveroptions.punkbuster.generic.x = OPTIONS_X;
  1116. s_serveroptions.punkbuster.generic.y = y;
  1117. s_serveroptions.punkbuster.itemnames = punkbuster_items;
  1118. y = 80;
  1119. s_serveroptions.botSkill.generic.type = MTYPE_SPINCONTROL;
  1120. s_serveroptions.botSkill.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1121. s_serveroptions.botSkill.generic.name = "Bot Skill: ";
  1122. s_serveroptions.botSkill.generic.x = 32 + (strlen(s_serveroptions.botSkill.generic.name) + 2 ) * SMALLCHAR_WIDTH;
  1123. s_serveroptions.botSkill.generic.y = y;
  1124. s_serveroptions.botSkill.itemnames = botSkill_list;
  1125. s_serveroptions.botSkill.curvalue = 1;
  1126. y += ( 2 * SMALLCHAR_HEIGHT );
  1127. s_serveroptions.player0.generic.type = MTYPE_TEXT;
  1128. s_serveroptions.player0.generic.flags = QMF_SMALLFONT;
  1129. s_serveroptions.player0.generic.x = 32 + SMALLCHAR_WIDTH;
  1130. s_serveroptions.player0.generic.y = y;
  1131. s_serveroptions.player0.color = color_orange;
  1132. s_serveroptions.player0.style = UI_LEFT|UI_SMALLFONT;
  1133. for( n = 0; n < PLAYER_SLOTS; n++ ) {
  1134. s_serveroptions.playerType[n].generic.type = MTYPE_SPINCONTROL;
  1135. s_serveroptions.playerType[n].generic.flags = QMF_SMALLFONT;
  1136. s_serveroptions.playerType[n].generic.id = ID_PLAYER_TYPE;
  1137. s_serveroptions.playerType[n].generic.callback = ServerOptions_Event;
  1138. s_serveroptions.playerType[n].generic.x = 32;
  1139. s_serveroptions.playerType[n].generic.y = y;
  1140. s_serveroptions.playerType[n].itemnames = playerType_list;
  1141. s_serveroptions.playerName[n].generic.type = MTYPE_TEXT;
  1142. s_serveroptions.playerName[n].generic.flags = QMF_SMALLFONT;
  1143. s_serveroptions.playerName[n].generic.x = 96;
  1144. s_serveroptions.playerName[n].generic.y = y;
  1145. s_serveroptions.playerName[n].generic.callback = ServerOptions_PlayerNameEvent;
  1146. s_serveroptions.playerName[n].generic.id = n;
  1147. s_serveroptions.playerName[n].generic.ownerdraw = PlayerName_Draw;
  1148. s_serveroptions.playerName[n].color = color_orange;
  1149. s_serveroptions.playerName[n].style = UI_SMALLFONT;
  1150. s_serveroptions.playerName[n].string = s_serveroptions.playerNameBuffers[n];
  1151. s_serveroptions.playerName[n].generic.top = s_serveroptions.playerName[n].generic.y;
  1152. s_serveroptions.playerName[n].generic.bottom = s_serveroptions.playerName[n].generic.y + SMALLCHAR_HEIGHT;
  1153. s_serveroptions.playerName[n].generic.left = s_serveroptions.playerName[n].generic.x - SMALLCHAR_HEIGHT/ 2;
  1154. s_serveroptions.playerName[n].generic.right = s_serveroptions.playerName[n].generic.x + 16 * SMALLCHAR_WIDTH;
  1155. s_serveroptions.playerTeam[n].generic.type = MTYPE_SPINCONTROL;
  1156. s_serveroptions.playerTeam[n].generic.flags = QMF_SMALLFONT;
  1157. s_serveroptions.playerTeam[n].generic.x = 240;
  1158. s_serveroptions.playerTeam[n].generic.y = y;
  1159. s_serveroptions.playerTeam[n].itemnames = playerTeam_list;
  1160. y += ( SMALLCHAR_HEIGHT + 4 );
  1161. }
  1162. s_serveroptions.back.generic.type = MTYPE_BITMAP;
  1163. s_serveroptions.back.generic.name = GAMESERVER_BACK0;
  1164. s_serveroptions.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
  1165. s_serveroptions.back.generic.callback = ServerOptions_Event;
  1166. s_serveroptions.back.generic.id = ID_BACK;
  1167. s_serveroptions.back.generic.x = 0;
  1168. s_serveroptions.back.generic.y = 480-64;
  1169. s_serveroptions.back.width = 128;
  1170. s_serveroptions.back.height = 64;
  1171. s_serveroptions.back.focuspic = GAMESERVER_BACK1;
  1172. s_serveroptions.next.generic.type = MTYPE_BITMAP;
  1173. s_serveroptions.next.generic.name = GAMESERVER_NEXT0;
  1174. s_serveroptions.next.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE|QMF_GRAYED|QMF_HIDDEN;
  1175. s_serveroptions.next.generic.callback = ServerOptions_Event;
  1176. s_serveroptions.next.generic.id = ID_STARTSERVERNEXT;
  1177. s_serveroptions.next.generic.x = 640;
  1178. s_serveroptions.next.generic.y = 480-64-72;
  1179. s_serveroptions.next.generic.statusbar = ServerOptions_StatusBar;
  1180. s_serveroptions.next.width = 128;
  1181. s_serveroptions.next.height = 64;
  1182. s_serveroptions.next.focuspic = GAMESERVER_NEXT1;
  1183. s_serveroptions.go.generic.type = MTYPE_BITMAP;
  1184. s_serveroptions.go.generic.name = GAMESERVER_FIGHT0;
  1185. s_serveroptions.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
  1186. s_serveroptions.go.generic.callback = ServerOptions_Event;
  1187. s_serveroptions.go.generic.id = ID_GO;
  1188. s_serveroptions.go.generic.x = 640;
  1189. s_serveroptions.go.generic.y = 480-64;
  1190. s_serveroptions.go.width = 128;
  1191. s_serveroptions.go.height = 64;
  1192. s_serveroptions.go.focuspic = GAMESERVER_FIGHT1;
  1193. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.banner );
  1194. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.mappic );
  1195. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.picframe );
  1196. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.botSkill );
  1197. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.player0 );
  1198. for( n = 0; n < PLAYER_SLOTS; n++ ) {
  1199. if( n != 0 ) {
  1200. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.playerType[n] );
  1201. }
  1202. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.playerName[n] );
  1203. if( s_serveroptions.gametype >= GT_TEAM ) {
  1204. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.playerTeam[n] );
  1205. }
  1206. }
  1207. if( s_serveroptions.gametype != GT_CTF ) {
  1208. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.fraglimit );
  1209. }
  1210. else {
  1211. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.flaglimit );
  1212. }
  1213. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.timelimit );
  1214. if( s_serveroptions.gametype >= GT_TEAM ) {
  1215. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.friendlyfire );
  1216. }
  1217. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.pure );
  1218. if( s_serveroptions.multiplayer ) {
  1219. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.dedicated );
  1220. }
  1221. if( s_serveroptions.multiplayer ) {
  1222. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.hostname );
  1223. }
  1224. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.back );
  1225. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.next );
  1226. Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.go );
  1227. Menu_AddItem( &s_serveroptions.menu, (void*) &s_serveroptions.punkbuster );
  1228. ServerOptions_SetMenuItems();
  1229. }
  1230. /*
  1231. =================
  1232. ServerOptions_Cache
  1233. =================
  1234. */
  1235. void ServerOptions_Cache( void ) {
  1236. trap_R_RegisterShaderNoMip( GAMESERVER_BACK0 );
  1237. trap_R_RegisterShaderNoMip( GAMESERVER_BACK1 );
  1238. trap_R_RegisterShaderNoMip( GAMESERVER_FIGHT0 );
  1239. trap_R_RegisterShaderNoMip( GAMESERVER_FIGHT1 );
  1240. trap_R_RegisterShaderNoMip( GAMESERVER_SELECT );
  1241. trap_R_RegisterShaderNoMip( GAMESERVER_UNKNOWNMAP );
  1242. }
  1243. /*
  1244. =================
  1245. UI_ServerOptionsMenu
  1246. =================
  1247. */
  1248. static void UI_ServerOptionsMenu( qboolean multiplayer ) {
  1249. ServerOptions_MenuInit( multiplayer );
  1250. UI_PushMenu( &s_serveroptions.menu );
  1251. }
  1252. /*
  1253. =============================================================================
  1254. BOT SELECT MENU *****
  1255. =============================================================================
  1256. */
  1257. #define BOTSELECT_BACK0 "menu/art/back_0"
  1258. #define BOTSELECT_BACK1 "menu/art/back_1"
  1259. #define BOTSELECT_ACCEPT0 "menu/art/accept_0"
  1260. #define BOTSELECT_ACCEPT1 "menu/art/accept_1"
  1261. #define BOTSELECT_SELECT "menu/art/opponents_select"
  1262. #define BOTSELECT_SELECTED "menu/art/opponents_selected"
  1263. #define BOTSELECT_ARROWS "menu/art/gs_arrows_0"
  1264. #define BOTSELECT_ARROWSL "menu/art/gs_arrows_l"
  1265. #define BOTSELECT_ARROWSR "menu/art/gs_arrows_r"
  1266. #define PLAYERGRID_COLS 4
  1267. #define PLAYERGRID_ROWS 4
  1268. #define MAX_MODELSPERPAGE (PLAYERGRID_ROWS * PLAYERGRID_COLS)
  1269. typedef struct {
  1270. menuframework_s menu;
  1271. menutext_s banner;
  1272. menubitmap_s pics[MAX_MODELSPERPAGE];
  1273. menubitmap_s picbuttons[MAX_MODELSPERPAGE];
  1274. menutext_s picnames[MAX_MODELSPERPAGE];
  1275. menubitmap_s arrows;
  1276. menubitmap_s left;
  1277. menubitmap_s right;
  1278. menubitmap_s go;
  1279. menubitmap_s back;
  1280. int numBots;
  1281. int modelpage;
  1282. int numpages;
  1283. int selectedmodel;
  1284. int sortedBotNums[MAX_BOTS];
  1285. char boticons[MAX_MODELSPERPAGE][MAX_QPATH];
  1286. char botnames[MAX_MODELSPERPAGE][16];
  1287. } botSelectInfo_t;
  1288. static botSelectInfo_t botSelectInfo;
  1289. /*
  1290. =================
  1291. UI_BotSelectMenu_SortCompare
  1292. =================
  1293. */
  1294. static int QDECL UI_BotSelectMenu_SortCompare( const void *arg1, const void *arg2 ) {
  1295. int num1, num2;
  1296. const char *info1, *info2;
  1297. const char *name1, *name2;
  1298. num1 = *(int *)arg1;
  1299. num2 = *(int *)arg2;
  1300. info1 = UI_GetBotInfoByNumber( num1 );
  1301. info2 = UI_GetBotInfoByNumber( num2 );
  1302. name1 = Info_ValueForKey( info1, "name" );
  1303. name2 = Info_ValueForKey( info2, "name" );
  1304. return Q_stricmp( name1, name2 );
  1305. }
  1306. /*
  1307. =================
  1308. UI_BotSelectMenu_BuildList
  1309. =================
  1310. */
  1311. static void UI_BotSelectMenu_BuildList( void ) {
  1312. int n;
  1313. botSelectInfo.modelpage = 0;
  1314. botSelectInfo.numBots = UI_GetNumBots();
  1315. botSelectInfo.numpages = botSelectInfo.numBots / MAX_MODELSPERPAGE;
  1316. if( botSelectInfo.numBots % MAX_MODELSPERPAGE ) {
  1317. botSelectInfo.numpages++;
  1318. }
  1319. // initialize the array
  1320. for( n = 0; n < botSelectInfo.numBots; n++ ) {
  1321. botSelectInfo.sortedBotNums[n] = n;
  1322. }
  1323. // now sort it
  1324. qsort( botSelectInfo.sortedBotNums, botSelectInfo.numBots, sizeof(botSelectInfo.sortedBotNums[0]), UI_BotSelectMenu_SortCompare );
  1325. }
  1326. /*
  1327. =================
  1328. ServerPlayerIcon
  1329. =================
  1330. */
  1331. static void ServerPlayerIcon( const char *modelAndSkin, char *iconName, int iconNameMaxSize ) {
  1332. char *skin;
  1333. char model[MAX_QPATH];
  1334. Q_strncpyz( model, modelAndSkin, sizeof(model));
  1335. skin = Q_strrchr( model, '/' );
  1336. if ( skin ) {
  1337. *skin++ = '\0';
  1338. }
  1339. else {
  1340. skin = "default";
  1341. }
  1342. Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_%s.tga", model, skin );
  1343. if( !trap_R_RegisterShaderNoMip( iconName ) && Q_stricmp( skin, "default" ) != 0 ) {
  1344. Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_default.tga", model );
  1345. }
  1346. }
  1347. /*
  1348. =================
  1349. UI_BotSelectMenu_UpdateGrid
  1350. =================
  1351. */
  1352. static void UI_BotSelectMenu_UpdateGrid( void ) {
  1353. const char *info;
  1354. int i;
  1355. int j;
  1356. j = botSelectInfo.modelpage * MAX_MODELSPERPAGE;
  1357. for( i = 0; i < (PLAYERGRID_ROWS * PLAYERGRID_COLS); i++, j++) {
  1358. if( j < botSelectInfo.numBots ) {
  1359. info = UI_GetBotInfoByNumber( botSelectInfo.sortedBotNums[j] );
  1360. ServerPlayerIcon( Info_ValueForKey( info, "model" ), botSelectInfo.boticons[i], MAX_QPATH );
  1361. Q_strncpyz( botSelectInfo.botnames[i], Info_ValueForKey( info, "name" ), 16 );
  1362. Q_CleanStr( botSelectInfo.botnames[i] );
  1363. botSelectInfo.pics[i].generic.name = botSelectInfo.boticons[i];
  1364. if( BotAlreadySelected( botSelectInfo.botnames[i] ) ) {
  1365. botSelectInfo.picnames[i].color = color_red;
  1366. }
  1367. else {
  1368. botSelectInfo.picnames[i].color = color_orange;
  1369. }
  1370. botSelectInfo.picbuttons[i].generic.flags &= ~QMF_INACTIVE;
  1371. }
  1372. else {
  1373. // dead slot
  1374. botSelectInfo.pics[i].generic.name = NULL;
  1375. botSelectInfo.picbuttons[i].generic.flags |= QMF_INACTIVE;
  1376. botSelectInfo.botnames[i][0] = 0;
  1377. }
  1378. botSelectInfo.pics[i].generic.flags &= ~QMF_HIGHLIGHT;
  1379. botSelectInfo.pics[i].shader = 0;
  1380. botSelectInfo.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
  1381. }
  1382. // set selected model
  1383. i = botSelectInfo.selectedmodel % MAX_MODELSPERPAGE;
  1384. botSelectInfo.pics[i].generic.flags |= QMF_HIGHLIGHT;
  1385. botSelectInfo.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
  1386. if( botSelectInfo.numpages > 1 ) {
  1387. if( botSelectInfo.modelpage > 0 ) {
  1388. botSelectInfo.left.generic.flags &= ~QMF_INACTIVE;
  1389. }
  1390. else {
  1391. botSelectInfo.left.generic.flags |= QMF_INACTIVE;
  1392. }
  1393. if( botSelectInfo.modelpage < (botSelectInfo.numpages - 1) ) {
  1394. botSelectInfo.right.generic.flags &= ~QMF_INACTIVE;
  1395. }
  1396. else {
  1397. botSelectInfo.right.generic.flags |= QMF_INACTIVE;
  1398. }
  1399. }
  1400. else {
  1401. // hide left/right markers
  1402. botSelectInfo.left.generic.flags |= QMF_INACTIVE;
  1403. botSelectInfo.right.generic.flags |= QMF_INACTIVE;
  1404. }
  1405. }
  1406. /*
  1407. =================
  1408. UI_BotSelectMenu_Default
  1409. =================
  1410. */
  1411. static void UI_BotSelectMenu_Default( char *bot ) {
  1412. const char *botInfo;
  1413. const char *test;
  1414. int n;
  1415. int i;
  1416. for( n = 0; n < botSelectInfo.numBots; n++ ) {
  1417. botInfo = UI_GetBotInfoByNumber( n );
  1418. test = Info_ValueForKey( botInfo, "name" );
  1419. if( Q_stricmp( bot, test ) == 0 ) {
  1420. break;
  1421. }
  1422. }
  1423. if( n == botSelectInfo.numBots ) {
  1424. botSelectInfo.selectedmodel = 0;
  1425. return;
  1426. }
  1427. for( i = 0; i < botSelectInfo.numBots; i++ ) {
  1428. if( botSelectInfo.sortedBotNums[i] == n ) {
  1429. break;
  1430. }
  1431. }
  1432. if( i == botSelectInfo.numBots ) {
  1433. botSelectInfo.selectedmodel = 0;
  1434. return;
  1435. }
  1436. botSelectInfo.selectedmodel = i;
  1437. }
  1438. /*
  1439. =================
  1440. UI_BotSelectMenu_LeftEvent
  1441. =================
  1442. */
  1443. static void UI_BotSelectMenu_LeftEvent( void* ptr, int event ) {
  1444. if( event != QM_ACTIVATED ) {
  1445. return;
  1446. }
  1447. if( botSelectInfo.modelpage > 0 ) {
  1448. botSelectInfo.modelpage--;
  1449. botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE;
  1450. UI_BotSelectMenu_UpdateGrid();
  1451. }
  1452. }
  1453. /*
  1454. =================
  1455. UI_BotSelectMenu_RightEvent
  1456. =================
  1457. */
  1458. static void UI_BotSelectMenu_RightEvent( void* ptr, int event ) {
  1459. if( event != QM_ACTIVATED ) {
  1460. return;
  1461. }
  1462. if( botSelectInfo.modelpage < botSelectInfo.numpages - 1 ) {
  1463. botSelectInfo.modelpage++;
  1464. botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE;
  1465. UI_BotSelectMenu_UpdateGrid();
  1466. }
  1467. }
  1468. /*
  1469. =================
  1470. UI_BotSelectMenu_BotEvent
  1471. =================
  1472. */
  1473. static void UI_BotSelectMenu_BotEvent( void* ptr, int event ) {
  1474. int i;
  1475. if( event != QM_ACTIVATED ) {
  1476. return;
  1477. }
  1478. for( i = 0; i < (PLAYERGRID_ROWS * PLAYERGRID_COLS); i++ ) {
  1479. botSelectInfo.pics[i].generic.flags &= ~QMF_HIGHLIGHT;
  1480. botSelectInfo.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
  1481. }
  1482. // set selected
  1483. i = ((menucommon_s*)ptr)->id;
  1484. botSelectInfo.pics[i].generic.flags |= QMF_HIGHLIGHT;
  1485. botSelectInfo.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
  1486. botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE + i;
  1487. }
  1488. /*
  1489. =================
  1490. UI_BotSelectMenu_BackEvent
  1491. =================
  1492. */
  1493. static void UI_BotSelectMenu_BackEvent( void* ptr, int event ) {
  1494. if( event != QM_ACTIVATED ) {
  1495. return;
  1496. }
  1497. UI_PopMenu();
  1498. }
  1499. /*
  1500. =================
  1501. UI_BotSelectMenu_SelectEvent
  1502. =================
  1503. */
  1504. static void UI_BotSelectMenu_SelectEvent( void* ptr, int event ) {
  1505. if( event != QM_ACTIVATED ) {
  1506. return;
  1507. }
  1508. UI_PopMenu();
  1509. s_serveroptions.newBot = qtrue;
  1510. Q_strncpyz( s_serveroptions.newBotName, botSelectInfo.botnames[botSelectInfo.selectedmodel % MAX_MODELSPERPAGE], 16 );
  1511. }
  1512. /*
  1513. =================
  1514. UI_BotSelectMenu_Cache
  1515. =================
  1516. */
  1517. void UI_BotSelectMenu_Cache( void ) {
  1518. trap_R_RegisterShaderNoMip( BOTSELECT_BACK0 );
  1519. trap_R_RegisterShaderNoMip( BOTSELECT_BACK1 );
  1520. trap_R_RegisterShaderNoMip( BOTSELECT_ACCEPT0 );
  1521. trap_R_RegisterShaderNoMip( BOTSELECT_ACCEPT1 );
  1522. trap_R_RegisterShaderNoMip( BOTSELECT_SELECT );
  1523. trap_R_RegisterShaderNoMip( BOTSELECT_SELECTED );
  1524. trap_R_RegisterShaderNoMip( BOTSELECT_ARROWS );
  1525. trap_R_RegisterShaderNoMip( BOTSELECT_ARROWSL );
  1526. trap_R_RegisterShaderNoMip( BOTSELECT_ARROWSR );
  1527. }
  1528. static void UI_BotSelectMenu_Init( char *bot ) {
  1529. int i, j, k;
  1530. int x, y;
  1531. memset( &botSelectInfo, 0 ,sizeof(botSelectInfo) );
  1532. botSelectInfo.menu.wrapAround = qtrue;
  1533. botSelectInfo.menu.fullscreen = qtrue;
  1534. UI_BotSelectMenu_Cache();
  1535. botSelectInfo.banner.generic.type = MTYPE_BTEXT;
  1536. botSelectInfo.banner.generic.x = 320;
  1537. botSelectInfo.banner.generic.y = 16;
  1538. botSelectInfo.banner.string = "SELECT BOT";
  1539. botSelectInfo.banner.color = color_white;
  1540. botSelectInfo.banner.style = UI_CENTER;
  1541. y = 80;
  1542. for( i = 0, k = 0; i < PLAYERGRID_ROWS; i++) {
  1543. x = 180;
  1544. for( j = 0; j < PLAYERGRID_COLS; j++, k++ ) {
  1545. botSelectInfo.pics[k].generic.type = MTYPE_BITMAP;
  1546. botSelectInfo.pics[k].generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
  1547. botSelectInfo.pics[k].generic.x = x;
  1548. botSelectInfo.pics[k].generic.y = y;
  1549. botSelectInfo.pics[k].generic.name = botSelectInfo.boticons[k];
  1550. botSelectInfo.pics[k].width = 64;
  1551. botSelectInfo.pics[k].height = 64;
  1552. botSelectInfo.pics[k].focuspic = BOTSELECT_SELECTED;
  1553. botSelectInfo.pics[k].focuscolor = colorRed;
  1554. botSelectInfo.picbuttons[k].generic.type = MTYPE_BITMAP;
  1555. botSelectInfo.picbuttons[k].generic.flags = QMF_LEFT_JUSTIFY|QMF_NODEFAULTINIT|QMF_PULSEIFFOCUS;
  1556. botSelectInfo.picbuttons[k].generic.callback = UI_BotSelectMenu_BotEvent;
  1557. botSelectInfo.picbuttons[k].generic.id = k;
  1558. botSelectInfo.picbuttons[k].generic.x = x - 16;
  1559. botSelectInfo.picbuttons[k].generic.y = y - 16;
  1560. botSelectInfo.picbuttons[k].generic.left = x;
  1561. botSelectInfo.picbuttons[k].generic.top = y;
  1562. botSelectInfo.picbuttons[k].generic.right = x + 64;
  1563. botSelectInfo.picbuttons[k].generic.bottom = y + 64;
  1564. botSelectInfo.picbuttons[k].width = 128;
  1565. botSelectInfo.picbuttons[k].height = 128;
  1566. botSelectInfo.picbuttons[k].focuspic = BOTSELECT_SELECT;
  1567. botSelectInfo.picbuttons[k].focuscolor = colorRed;
  1568. botSelectInfo.picnames[k].generic.type = MTYPE_TEXT;
  1569. botSelectInfo.picnames[k].generic.flags = QMF_SMALLFONT;
  1570. botSelectInfo.picnames[k].generic.x = x + 32;
  1571. botSelectInfo.picnames[k].generic.y = y + 64;
  1572. botSelectInfo.picnames[k].string = botSelectInfo.botnames[k];
  1573. botSelectInfo.picnames[k].color = color_orange;
  1574. botSelectInfo.picnames[k].style = UI_CENTER|UI_SMALLFONT;
  1575. x += (64 + 6);
  1576. }
  1577. y += (64 + SMALLCHAR_HEIGHT + 6);
  1578. }
  1579. botSelectInfo.arrows.generic.type = MTYPE_BITMAP;
  1580. botSelectInfo.arrows.generic.name = BOTSELECT_ARROWS;
  1581. botSelectInfo.arrows.generic.flags = QMF_INACTIVE;
  1582. botSelectInfo.arrows.generic.x = 260;
  1583. botSelectInfo.arrows.generic.y = 440;
  1584. botSelectInfo.arrows.width = 128;
  1585. botSelectInfo.arrows.height = 32;
  1586. botSelectInfo.left.generic.type = MTYPE_BITMAP;
  1587. botSelectInfo.left.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
  1588. botSelectInfo.left.generic.callback = UI_BotSelectMenu_LeftEvent;
  1589. botSelectInfo.left.generic.x = 260;
  1590. botSelectInfo.left.generic.y = 440;
  1591. botSelectInfo.left.width = 64;
  1592. botSelectInfo.left.height = 32;
  1593. botSelectInfo.left.focuspic = BOTSELECT_ARROWSL;
  1594. botSelectInfo.right.generic.type = MTYPE_BITMAP;
  1595. botSelectInfo.right.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
  1596. botSelectInfo.right.generic.callback = UI_BotSelectMenu_RightEvent;
  1597. botSelectInfo.right.generic.x = 321;
  1598. botSelectInfo.right.generic.y = 440;
  1599. botSelectInfo.right.width = 64;
  1600. botSelectInfo.right.height = 32;
  1601. botSelectInfo.right.focuspic = BOTSELECT_ARROWSR;
  1602. botSelectInfo.back.generic.type = MTYPE_BITMAP;
  1603. botSelectInfo.back.generic.name = BOTSELECT_BACK0;
  1604. botSelectInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
  1605. botSelectInfo.back.generic.callback = UI_BotSelectMenu_BackEvent;
  1606. botSelectInfo.back.generic.x = 0;
  1607. botSelectInfo.back.generic.y = 480-64;
  1608. botSelectInfo.back.width = 128;
  1609. botSelectInfo.back.height = 64;
  1610. botSelectInfo.back.focuspic = BOTSELECT_BACK1;
  1611. botSelectInfo.go.generic.type = MTYPE_BITMAP;
  1612. botSelectInfo.go.generic.name = BOTSELECT_ACCEPT0;
  1613. botSelectInfo.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
  1614. botSelectInfo.go.generic.callback = UI_BotSelectMenu_SelectEvent;
  1615. botSelectInfo.go.generic.x = 640;
  1616. botSelectInfo.go.generic.y = 480-64;
  1617. botSelectInfo.go.width = 128;
  1618. botSelectInfo.go.height = 64;
  1619. botSelectInfo.go.focuspic = BOTSELECT_ACCEPT1;
  1620. Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.banner );
  1621. for( i = 0; i < MAX_MODELSPERPAGE; i++ ) {
  1622. Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.pics[i] );
  1623. Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.picbuttons[i] );
  1624. Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.picnames[i] );
  1625. }
  1626. Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.arrows );
  1627. Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.left );
  1628. Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.right );
  1629. Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.back );
  1630. Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.go );
  1631. UI_BotSelectMenu_BuildList();
  1632. UI_BotSelectMenu_Default( bot );
  1633. botSelectInfo.modelpage = botSelectInfo.selectedmodel / MAX_MODELSPERPAGE;
  1634. UI_BotSelectMenu_UpdateGrid();
  1635. }
  1636. /*
  1637. =================
  1638. UI_BotSelectMenu
  1639. =================
  1640. */
  1641. void UI_BotSelectMenu( char *bot ) {
  1642. UI_BotSelectMenu_Init( bot );
  1643. UI_PushMenu( &botSelectInfo.menu );
  1644. }