mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-29 15:12:19 +00:00
7df2df5cd5
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6339 fc73d0e0-1445-4013-8a0c-d673dee63da5
474 lines
16 KiB
C++
474 lines
16 KiB
C++
//maxclients is a QW thing. NQ engines use maxplayers (which requires a disconnect to apply)
|
|
#include "../menusys/mitem_grid.qc"
|
|
|
|
static string newgameinfo;
|
|
|
|
nonstatic void(mitem_desktop desktop) M_Dir =
|
|
{ //implementing this primarily so its available in QSS. :P
|
|
string path = argv(1);
|
|
string pack = argv(2);
|
|
searchhandle h;
|
|
if (path == "")
|
|
{
|
|
print("m_dir <FILEPATH> [PACKAGE]\n");
|
|
return;
|
|
}
|
|
if (pack)
|
|
h = search_begin(path, SB_FULLPACKAGEPATH|SB_FORCESEARCH|16, 0, argv(2));
|
|
else
|
|
h = search_begin(path, SB_FULLPACKAGEPATH|16|32, 0);
|
|
print(sprintf("Directory listing of %S (%g files)\n", path, search_getsize(h)));
|
|
for (float i = 0; i < search_getsize(h); i++)
|
|
{
|
|
float sz = search_getfilesize(h,i);
|
|
string szfmt = "b";
|
|
if (sz > 512*1024*1024)
|
|
sz /= 1024*1024*1024, szfmt = "gb";
|
|
else if (sz > 512*1024)
|
|
sz /= 1024*1024, szfmt = "mb";
|
|
else if (sz > 512)
|
|
sz /= 1024, szfmt = "kb";
|
|
|
|
print(sprintf(" %S (%.4g %s, %s, %S)\n",
|
|
search_getfilename(h,i),
|
|
sz,szfmt,
|
|
search_getfilemtime(h,i),
|
|
search_getpackagename(h,i)
|
|
));
|
|
|
|
#if 1
|
|
float f = search_fopen(h,i);
|
|
if (f>=0)
|
|
{
|
|
print(sprintf("^`u8:" "\t%S\n", substring(fgets(f), 0, 64)));
|
|
fclose(f);
|
|
}
|
|
else
|
|
print("\tsearch_fopen failure\n");
|
|
#endif
|
|
}
|
|
search_end(h);
|
|
};
|
|
nonstatic void(mitem_desktop desktop) M_FOpen =
|
|
{
|
|
float f = fopen(argv(1), FILE_READ);
|
|
string s;
|
|
if (f>=0)
|
|
{
|
|
while ((s=fgets(f)))
|
|
print(sprintf("^`u8:" "%s\n", s));
|
|
fclose(f);
|
|
print("<EOF>\n");
|
|
}
|
|
else
|
|
print("fopen failure\n");
|
|
};
|
|
|
|
#define FOURCC(a,b,c,d) ((int)(a)<<0i)|((int)(b)<<8i)|((int)(c)<<16i)|((int)(d)<<24i)
|
|
static string(string name) getmapdesc =
|
|
{
|
|
if (checkbuiltin(fread) && checkbuiltin(fseek) && checkbuiltin(memalloc) && checkbuiltin(memfree) && checkbuiltin(memgetval) && checkbuiltin(itof) && checkbuiltin(ftoi))
|
|
{ //we can do it, so do it.
|
|
filestream f = fopen(name, FILE_READ);
|
|
if (f < 0)
|
|
print(sprintf("Unable to read %s\n", name));
|
|
|
|
name = substring(name, 5, -5);
|
|
if (f < 0)
|
|
return name;
|
|
|
|
int *header = memalloc(sizeof(int)*4);
|
|
int bspver = 0, entofs, entlen;
|
|
if (fread(f, (void*)header, sizeof(int)*4) == sizeof(int)*4 && ((bspver=header[0]),(
|
|
bspver == FOURCC('I','B','S','P') || //IBSP (q2/q3)
|
|
bspver == FOURCC('R','B','S','P') || //RBSP (jk2o etc)
|
|
bspver == FOURCC('F','B','S','P') || //IBSP (qfusion/warsow)
|
|
bspver == 29i || //q1
|
|
bspver == 30i || //hl
|
|
bspver == FOURCC('B','S','P','2')))) //bsp2
|
|
{
|
|
if (bspver == FOURCC('I','B','S','P'))
|
|
{ //has an actual version number! ooo...
|
|
entofs = header[2];
|
|
entlen = header[3];
|
|
}
|
|
else
|
|
{
|
|
entofs = header[1];
|
|
entlen = header[2];
|
|
}
|
|
fseek(f, entofs);
|
|
string s = (string)memalloc(entlen+1);
|
|
fread(f, (void*)s, entlen);
|
|
float argc = tokenize(s);
|
|
if (argv(0) == "{")
|
|
{
|
|
for (float p = 1; p < argc; p+=2)
|
|
{
|
|
string t = argv(p);
|
|
if (t == "message")
|
|
{ //finally found the human-readable name of the map. woo.
|
|
name = argv(p+1);
|
|
break;
|
|
}
|
|
if (t == "}") //don't read the message from some kind of trigger
|
|
break;
|
|
if (t == "{") //some sort of corruption
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
name = "ERROR";
|
|
memfree((void*)s);
|
|
}
|
|
else
|
|
name = sprintf("UNSUPPORTED %i", bspver);
|
|
memfree(header);
|
|
fclose(f);
|
|
return name;
|
|
}
|
|
return ""; //we can't do that in this engine... don't show anything, because its pointless showing the same thing twice.
|
|
};
|
|
|
|
static string(string name) packagetogamedir =
|
|
{
|
|
float so = strstrofs(name, "/");
|
|
if (so>=0)
|
|
name = substring(name, 0, so);
|
|
|
|
//don't hide id1/dm4 etc just because the user previously auto-downloaded eg qw/aerowalk
|
|
name = strtolower(name);
|
|
if (name == "qw" || name == "fte")
|
|
name = "id1";
|
|
|
|
return name;
|
|
};
|
|
class mitem_maplist : mitem_grid
|
|
{
|
|
strbuf names;
|
|
strbuf descs;
|
|
virtual void() item_remove =
|
|
{
|
|
buf_del(names);
|
|
buf_del(descs);
|
|
super::item_remove();
|
|
};
|
|
void() mitem_maplist =
|
|
{
|
|
searchhandle searchtable;
|
|
searchtable = search_begin("maps/*.bsp", SB_FULLPACKAGEPATH, 0);
|
|
float files = search_getsize(searchtable);
|
|
if (files && checkbuiltin(search_getpackagename))
|
|
{ //find which gamedir the first entry is in (highest-priority is first)
|
|
string gd = packagetogamedir(search_getpackagename(searchtable,0));
|
|
|
|
for (float count = 1; count < files; count++)
|
|
{
|
|
string mgd = packagetogamedir(search_getpackagename(searchtable,count));
|
|
if (mgd != gd)
|
|
{ //the gamedir changed... truncate the list here.
|
|
files = count;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
grid_numchildren = 0;
|
|
names = buf_create();
|
|
for (float count = 0; count < files; count++)
|
|
{
|
|
string n = search_getfilename(searchtable, count);
|
|
string shortname = substring(n, 5, -5);
|
|
if (!strncmp(shortname, "_", 1) || !strncmp(shortname, "b_", 2))
|
|
continue; //don't add b_* names
|
|
bufstr_set(names, grid_numchildren, n);
|
|
grid_numchildren++;
|
|
}
|
|
search_end(searchtable);
|
|
buf_sort(names, 0, FALSE); //make sure they're sorted now.
|
|
descs = buf_create();
|
|
|
|
item_resized(); //FIXME: is this needed?
|
|
grid_kactive = grid_numchildren?0:-1;
|
|
grid_selectionchanged(-1,grid_kactive);
|
|
};
|
|
virtual void(vector pos, float idx) grid_draw =
|
|
{
|
|
string map = bufstr_get(names, idx);
|
|
string desc = bufstr_get(descs, idx);
|
|
if not(desc)
|
|
{
|
|
desc = map;
|
|
static float lasttime;
|
|
if (time != lasttime)
|
|
{ //fix up the name and cache it.
|
|
lasttime = time;
|
|
desc = getmapdesc(map);
|
|
bufstr_set(descs, idx, desc);
|
|
}
|
|
}
|
|
map = substring(map, 5, -5);
|
|
|
|
vector col = (map == get("map"))?[1.16, 0.54, 0.41]:'1 1 1';
|
|
if (item_flags & IF_MFOCUSED && idx == grid_mactive)
|
|
col_z = 0;
|
|
if (item_flags & IF_KFOCUSED && idx == grid_kactive)
|
|
col_x = 0;
|
|
|
|
vector valuepos = [pos_x+item_size_x/2, pos_y];
|
|
pos_x = valuepos_x - stringwidth(map, TRUE, '1 1 0'*this.item_scale) - 8;
|
|
valuepos_x += 1;
|
|
ui.drawstring(pos, map, '1 1 0' * item_scale, col, item_alpha, 0);
|
|
ui.drawstring(valuepos, desc, '1 1 0' * item_scale, col, item_alpha, 0);
|
|
};
|
|
virtual float(vector pos, float scan, float chr, float down, float idx) grid_keypress =
|
|
{
|
|
if ((scan == K_ENTER || scan == K_MOUSE1) && down)
|
|
{
|
|
string map = bufstr_get(names, idx);
|
|
map = substring(map, 5, -5);
|
|
set("map", map);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
};
|
|
// virtual void(float olditem, float newitem) grid_selectionchanged;
|
|
};
|
|
|
|
class mitem_newgame : mitem_exmenu
|
|
{
|
|
virtual float(string key) isvalid =
|
|
{
|
|
return TRUE;
|
|
};
|
|
virtual string(string key) get =
|
|
{
|
|
return infoget(newgameinfo, key);
|
|
};
|
|
virtual void(string key, string newval) set =
|
|
{
|
|
string old = newgameinfo;
|
|
newgameinfo = strzone(infoadd(newgameinfo, key, newval));
|
|
if (old)
|
|
strunzone(old);
|
|
};
|
|
};
|
|
|
|
nonstatic void(mitem_desktop desktop) M_NewGame =
|
|
{
|
|
mitem_pic banner;
|
|
string gametype = argv(1);
|
|
local float pos;
|
|
mitem_exmenu m;
|
|
if (gametype == "sp")
|
|
{
|
|
//single player has no options. the start map itself gives skill+episode options.
|
|
//use 0 for maxplayers. this allows splitscreen to allocate more.
|
|
localcmd("\ndisconnect; deathmatch 0; coop 0; maxplayers 0; timelimit 0; fraglimit 0; teamplay 0; samelevel 0; startmap_sp\n");
|
|
return;
|
|
}
|
|
if (gametype == "begin")
|
|
{
|
|
cvar_set("hostname", infoget(newgameinfo, "hostname"));
|
|
cvar_set("deathmatch", infoget(newgameinfo, "deathmatch"));
|
|
cvar_set("coop", infoget(newgameinfo, "coop"));
|
|
cvar_set("teamplay", infoget(newgameinfo, "teamplay"));
|
|
cvar_set("sv_public", infoget(newgameinfo, "sv_public"));
|
|
cvar_set("timelimit", infoget(newgameinfo, "timelimit"));
|
|
cvar_set("fraglimit", infoget(newgameinfo, "fraglimit"));
|
|
string map = infoget(newgameinfo, "map");
|
|
if (map == "")
|
|
{
|
|
if (infoget(newgameinfo, "deathmatch"))
|
|
map = sprintf("dm%g", floor(random(1, 6)));
|
|
else
|
|
map = "start";
|
|
}
|
|
|
|
if (engine == E_QSS)
|
|
{
|
|
if (stof(infoget(newgameinfo, "maxclients")) != cvar("maxplayers"))
|
|
localcmd(sprintf("\ndisconnect; maxplayers %S\n", infoget(newgameinfo, "maxclients")?:"0"));
|
|
}
|
|
else
|
|
cvar_set("maxclients", infoget(newgameinfo, "maxclients"));
|
|
|
|
//'map' acts slightly differently in different engines.
|
|
//qss: kicks everyone, resets all parms
|
|
//fte: just resets parms.
|
|
//whereas 'changelevel' works more predictably, but does mean you might get more weapons than were really intended.
|
|
if (engine == E_QSS && gametype == "warp")
|
|
localcmd(sprintf("\nchangelevel %S\n", map));
|
|
else
|
|
localcmd(sprintf("\nmap %S\n", map));
|
|
return;
|
|
}
|
|
|
|
if (newgameinfo)
|
|
strunzone(newgameinfo);
|
|
newgameinfo = "";
|
|
newgameinfo = infoadd(newgameinfo, "hostname", cvar_string("hostname"));
|
|
newgameinfo = infoadd(newgameinfo, "deathmatch", cvar_string("deathmatch"));
|
|
newgameinfo = infoadd(newgameinfo, "teamplay", cvar_string("teamplay"));
|
|
newgameinfo = infoadd(newgameinfo, "sv_public", cvar_string("sv_public"));
|
|
newgameinfo = infoadd(newgameinfo, "maxclients", cvar_string((engine == E_QSS)?"maxplayers":"maxclients"));
|
|
newgameinfo = infoadd(newgameinfo, "timelimit", cvar_string("timelimit"));
|
|
newgameinfo = infoadd(newgameinfo, "fraglimit", cvar_string("fraglimit"));
|
|
newgameinfo = strzone(newgameinfo);
|
|
|
|
//create the menu
|
|
m = spawn(mitem_newgame, item_text:_("New Game"), item_flags:IF_SELECTABLE, item_command:"m_main");
|
|
desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
|
|
desktop.item_focuschange(m, IF_KFOCUSED);
|
|
m.totop();
|
|
|
|
#if 1
|
|
mitem_frame fr = m;
|
|
#else
|
|
//create a frame for its scrollbar.
|
|
float h = 100;
|
|
mitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);
|
|
m.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, -h], [160, h*2]);
|
|
#endif
|
|
|
|
switch(gametype)
|
|
{
|
|
case "tdm":
|
|
case "dm":
|
|
case "coop":
|
|
case "sp":
|
|
case "warp":
|
|
break;
|
|
default:
|
|
//show game type selection
|
|
pos = (16/-2)*(4);
|
|
banner = spawn(mitem_pic, item_text:"gfx/p_option.lmp", item_size_y:24, item_flags:IF_CENTERALIGN);
|
|
m.addm(banner, [(160-banner.item_size_x)*0.5, pos-32], [(160+banner.item_size_x)*0.5, pos-8]);
|
|
m.addm(spawn(mitem_text, item_text:"Single Player", item_command:"m_pop;m_newgame sp", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]); pos += 16;
|
|
m.addm(spawn(mitem_text, item_text:"Cooperative", item_command:"m_pop;m_newgame coop", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]); pos += 16;
|
|
m.addm(spawn(mitem_text, item_text:"Deathmatch", item_command:"m_pop;m_newgame dm", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]); pos += 16;
|
|
m.addm(spawn(mitem_text, item_text:"Team Deathmatch", item_command:"m_pop;m_newgame tdm", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]); pos += 16;
|
|
|
|
m.addm(spawn(mitem_text, item_text:"Map Selection", item_command:"m_pop;m_newgame warp", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]); pos += 16;
|
|
|
|
m.add(spawn (mitem_spinnymodel, item_text: "progs/soldier.mdl",firstframe:73, framecount:8, shootframe:81, shootframes:9), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, 12*-16/2], [0, 12*16/2]);
|
|
return;
|
|
}
|
|
|
|
pos = -100;
|
|
|
|
banner = spawn(mitem_pic, item_text:"gfx/p_multi.lmp", item_size_y:24, item_flags:IF_CENTERALIGN);
|
|
m.addm(banner, [(0-banner.item_size_x)*0.5, pos-32], [(0+banner.item_size_x)*0.5, pos-8]);
|
|
|
|
if (gametype != "warp")
|
|
{
|
|
fr.addm(menuitemeditt_spawn(_("Hostname"), "hostname", '280 8'), [-160, pos], [160, pos+8]); pos += 8;
|
|
fr.addm(menuitemcombo_spawn(_("Public"), "sv_public", '280 8', (engine == E_QSS)?_( "0 \"LAN\" "
|
|
"1 \"Internet\" "
|
|
):_( "-1 \"Reject All (Splitscreen)\" "
|
|
"0 \"Private (Manual IP Sharing)\" "
|
|
"1 \"Public (Manual Config)\" "
|
|
"2 \"Public (Holepunch)\" "
|
|
)), [-160, pos], [160, pos+8]); pos += 8;
|
|
fr.addm(menuitemcombo_spawn(_("Max Clients"), "maxclients", '280 8', _(
|
|
"2 \"Two\" "
|
|
"3 \"Three\" "
|
|
"4 \"Four\" "
|
|
"8 \"Eight\" "
|
|
"16 \"Sixteen\" "
|
|
"32 \"Thirty Two\" "
|
|
)), [-160, pos], [160, pos+8]); pos += 8;
|
|
}
|
|
|
|
if (gametype == "dm" || gametype == "tdm")
|
|
{
|
|
if (m.get("deathmatch") == "0")
|
|
{
|
|
m.set("deathmatch", "1");
|
|
m.set("coop", "0");
|
|
}
|
|
fr.addm(menuitemcombo_spawn(_("Deathmatch Mode"), "deathmatch", '280 8', _(
|
|
"1 \"Weapons Respawn\" "
|
|
"2 \"Weapons Stay\" "
|
|
"3 \"Powerups Respawn\" "
|
|
"4 \"Start With Weapons\" "
|
|
)), [-160, pos], [160, pos+8]); pos += 8;
|
|
}
|
|
else
|
|
{
|
|
if (m.get("coop") == "0")
|
|
{
|
|
m.set("deathmatch", "0");
|
|
m.set("coop", "1");
|
|
}
|
|
}
|
|
if (gametype == "tdm")
|
|
{
|
|
if (m.get("teamplay") == "0")
|
|
m.set("teamplay", "1");
|
|
}
|
|
if (gametype == "dm")
|
|
{
|
|
if (m.get("teamplay") != "0")
|
|
m.set("teamplay", "0");
|
|
}
|
|
if (gametype == "coop")
|
|
fr.addm(menuitemcheck_spawn(_("No Friendly Fire"), "teamplay", '280 8'), [-160, pos], [160, pos+8]), pos += 8;
|
|
// if (gametype == "dm" || gametype == "tdm")
|
|
|
|
if (gametype == "coop")
|
|
m.set("map", "start");
|
|
else
|
|
{
|
|
m.set("map", "");
|
|
|
|
if (gametype != "warp")
|
|
{
|
|
fr.addm(menuitemcombo_spawn(_("Time Limit"), "timelimit", '280 8', _(
|
|
"0 \"No Limit\" "
|
|
"5 \"5 minutes\" "
|
|
"10 \"10 minutes\" "
|
|
"15 \"15 minutes\" "
|
|
"20 \"20 minutes\" "
|
|
"25 \"25 minutes\" "
|
|
"30 \"30 minutes\" "
|
|
"35 \"35 minutes\" "
|
|
"40 \"40 minutes\" "
|
|
"45 \"45 minutes\" "
|
|
"50 \"50 minutes\" "
|
|
"55 \"55 minutes\" "
|
|
"60 \"1 hour\" "
|
|
)), [-160, pos], [160, pos+8]); pos += 8;
|
|
fr.addm(menuitemcombo_spawn(_("Frag Limit"), "fraglimit", '280 8', _(
|
|
"0 \"No Limit\" "
|
|
"10 \"10 frags\" "
|
|
"20 \"20 frags\" "
|
|
"30 \"30 frags\" "
|
|
"40 \"40 frags\" "
|
|
"50 \"50 frags\" "
|
|
"60 \"60 frags\" "
|
|
"70 \"70 frags\" "
|
|
"80 \"80 frags\" "
|
|
"90 \"90 frags\" "
|
|
"100 \"100 frags\" "
|
|
)), [-160, pos], [160, pos+8]); pos += 8;
|
|
}
|
|
|
|
pos += 8;
|
|
fr.addm(spawn(mitem_maplist, item_scale:8, item_flags:IF_CENTERALIGN|IF_SELECTABLE), [-160, pos], [150, 100-16-8]), pos = 100-16;
|
|
}
|
|
|
|
fr.addm(spawn(mitem_text, item_text:"BEGIN!", item_command:"m_pop;m_newgame begin", item_scale:16, item_flags:IF_CENTERALIGN), [-160, pos], [160, pos+16]);pos += 16;
|
|
|
|
|
|
//random art for style
|
|
if (gametype == "coop")
|
|
{
|
|
m.add(spawn (mitem_spinnymodel, item_text: "progs/soldier.mdl", firstframe:73, framecount:8, shootframe:81, shootframes:9), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, -240/2], [0, 240/2]);
|
|
}
|
|
else
|
|
{
|
|
m.add(spawn (mitem_spinnymodel, item_text: "progs/player.mdl", firstframe:0, framecount:6, shootframe:119, shootframes:6), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, 12*-16/2], [0, 12*16/2]);
|
|
}
|
|
|
|
addmenuback(m);
|
|
};
|