// commented out commands are really only intended for internal use, or already have declaration in the engine
alias autoswitch "qc_cmd_cmd autoswitch ${* ?}" // Whether or not to switch automatically when getting a better weapon
alias clientversion "qc_cmd_cmd clientversion ${* ?}" // Release version of the game
+//alias getserverpic "qc_cmd_cmd getserverpic ${* ?}" // Retrieve server banner from the server
//alias mv_getpicture "qc_cmd_cmd mv_getpicture ${* ?}" // Retrieve mapshot picture from the server
alias join "qc_cmd_cmd join ${* ?}" // Become a player in the game
alias ready "qc_cmd_cmd ready ${* ?}" // Qualify as ready to end warmup stage (or restart server if allowed)
COLOR_DIALOG_MODEL '1 1 1'
COLOR_DIALOG_CROSSHAIR '1 1 1'
COLOR_DIALOG_HUD '1 1 1'
-COLOR_DIALOG_SCREENSHOTVIEWER '1 1 1'
COLOR_DIALOG_SERVERINFO '1 1 1'
+COLOR_DIALOG_SCREENSHOTVIEWER '1 1 1'
COLOR_DIALOG_FIRSTRUN '1 1 1'
+COLOR_DIALOG_WELCOME '1 1 1'
COLOR_DIALOG_CVARS '1 0.2 0.15'
COLOR_DIALOG_HUDCONFIRM '1 0.2 0.15'
COLOR_DIALOG_HUD '1 1 1'
COLOR_DIALOG_SERVERINFO '1 1 1'
COLOR_DIALOG_SCREENSHOTVIEWER '1 1 1'
+COLOR_DIALOG_FIRSTRUN '1 1 1'
+COLOR_DIALOG_WELCOME '1 1 1'
COLOR_DIALOG_CVARS '1 0 0'
COLOR_DIALOG_HUDCONFIRM '1 0 0'
COLOR_DIALOG_HUD '1 1 1'
COLOR_DIALOG_SERVERINFO '1 1 1'
COLOR_DIALOG_SCREENSHOTVIEWER '1 1 1'
+COLOR_DIALOG_FIRSTRUN '1 1 1'
+COLOR_DIALOG_WELCOME '1 1 1'
COLOR_DIALOG_CVARS '1 0 0'
COLOR_DIALOG_HUDCONFIRM '1 0 0'
COLOR_DIALOG_HUD '1 1 1'
COLOR_DIALOG_SERVERINFO '1 1 1'
COLOR_DIALOG_SCREENSHOTVIEWER '1 1 1'
+COLOR_DIALOG_FIRSTRUN '1 1 1'
+COLOR_DIALOG_WELCOME '1 1 1'
COLOR_DIALOG_CVARS '1 0 0'
COLOR_DIALOG_HUDCONFIRM '1 0 0'
}
}
+string welcomedialog_args;
+NET_HANDLE(TE_CSQC_SERVERINFO, bool isNew)
+{
+ if(welcomedialog_args)
+ strunzone(welcomedialog_args);
+ welcomedialog_args = strcat("name \"", ReadString(), "\"");
+ welcomedialog_args = strcat(welcomedialog_args, " ip \"", ReadString(), "\"");
+ welcomedialog_args = strcat(welcomedialog_args, " motd \"", MakeConsoleSafe(strreplace("\n", "\\n", ReadString())), "\"");
+ string pic = ReadString();
+ if(pic == "" || PreviewExists(pic) )
+ {
+ if(pic != "")
+ welcomedialog_args = strcat(welcomedialog_args, " pic \"", pic, "\"");
+ localcmd("\nmenu_cmd directmenu Welcome ", welcomedialog_args, "\n");
+ welcomedialog_args = string_null;
+ } else {
+ welcomedialog_args = strzone(welcomedialog_args);
+ print(_("Requesting server banner...\n"));
+ localcmd("\ncmd getserverpic\n");
+ }
+ return true;
+}
+
+NET_HANDLE(TE_CSQC_SERVERINFO_PIC, bool isNew)
+{
+ if(welcomedialog_args)
+ {
+ localcmd("\nmenu_cmd directmenu Welcome ", strcat(welcomedialog_args, " pic \"", ReadPicture(), "\""), "\n");
+ strunzone(welcomedialog_args);
+ welcomedialog_args = string_null;
+ }
+ else // just get the image, we don't want to display the welcome dialog
+ ReadPicture();
+ return true;
+}
+
string _getcommandkey(string cmd_name, string command, bool forcename)
{
string keys;
REGISTER_NET_TEMP(TE_CSQC_TEAMNAGGER)
REGISTER_NET_TEMP(TE_CSQC_PINGPLREPORT)
REGISTER_NET_TEMP(TE_CSQC_WEAPONCOMPLAIN)
+REGISTER_NET_TEMP(TE_CSQC_SERVERINFO)
+REGISTER_NET_TEMP(TE_CSQC_SERVERINFO_PIC)
REGISTER_NET_TEMP(TE_CSQC_VEHICLESETUP)
const int RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder
return v;
}
#endif
+
+bool ImageExists(string img)
+{
+ if(fexists(strcat(img, ".tga"))) return true;
+ if(fexists(strcat(img, ".png"))) return true;
+ if(fexists(strcat(img, ".jpg"))) return true;
+ if(fexists(strcat(img, ".pcx"))) return true;
+
+ return false;
+}
string wordwrap_buffer;
string ftos_mindecimals(float number);
bool fexists(string f);
+bool ImageExists(string img);
// unzone the string, and return it as tempstring. Safe to be called on string_null
string fstrunzone(string s);
#include <common/command/_mod.qh>
+.void(entity me, float argsbuf) readInputArgs;
.entity firstChild, nextSibling;
string _dumptree_space;
m_play_click_sound(MENU_SOUND_OPEN);
m_goto(strcat(filter, argv(1))); // switch to a menu item
}
+ else if(argc > 2 && !isdemo())
+ {
+ entity e = NULL;
+ float argsbuf = 0;
+ string s = strzone(argv(1)); // dialog name
+ for(int i = 0; (e = nextent(e)); )
+ if(e.classname != "vtbl" && e.name == strcat(filter, s))
+ {
+ argsbuf = buf_create();
+ if(argsbuf >= 0)
+ if(e.readInputArgs)
+ {
+ for(i = 2; i < argc; ++i)
+ bufstr_add(argsbuf, argv(i), 1);
+ e.readInputArgs(e, argsbuf);
+ m_goto(strcat(filter, s));
+ }
+ if(argsbuf >= 0)
+ buf_del(argsbuf);
+ }
+ }
return;
}
SKINVECTOR(COLOR_DIALOG_CROSSHAIR, '1 0.7 0.7');
SKINVECTOR(COLOR_DIALOG_HUD, '1 0.7 0.7');
SKINVECTOR(COLOR_DIALOG_SERVERINFO, '0.7 0.7 1');
+ SKINVECTOR(COLOR_DIALOG_WELCOME, '1 0.7 0.7');
SKINVECTOR(COLOR_DIALOG_CVARS, '1 0 0');
SKINVECTOR(COLOR_DIALOG_SCREENSHOTVIEWER, '0.7 0.7 1');
SKINVECTOR(COLOR_DIALOG_HUDCONFIRM, '1 0 0');
#include <menu/xonotic/dialog_singleplayer_winner.qc>
#include <menu/xonotic/dialog_teamselect.qc>
#include <menu/xonotic/dialog_uid2name.qc>
+#include <menu/xonotic/dialog_welcome.qc>
#include <menu/xonotic/gametypelist.qc>
#include <menu/xonotic/hudskinlist.qc>
#include <menu/xonotic/image.qc>
#include <menu/xonotic/statslist.qc>
#include <menu/xonotic/tab.qc>
#include <menu/xonotic/tabcontroller.qc>
+#include <menu/xonotic/textbox.qc>
#include <menu/xonotic/textlabel.qc>
#include <menu/xonotic/textslider.qc>
#include <menu/xonotic/util.qc>
#include <menu/xonotic/dialog_singleplayer_winner.qh>
#include <menu/xonotic/dialog_teamselect.qh>
#include <menu/xonotic/dialog_uid2name.qh>
+#include <menu/xonotic/dialog_welcome.qh>
#include <menu/xonotic/gametypelist.qh>
#include <menu/xonotic/hudskinlist.qh>
#include <menu/xonotic/image.qh>
--- /dev/null
+#include "dialog_welcome.qh"
+
+#include "image.qh"
+#include "textlabel.qh"
+#include "textbox.qh"
+#include "radiobutton.qh"
+#include "commandbutton.qh"
+#include "slider.qh"
+
+void welcomeDialog_resetStrings(entity me)
+{
+ if(me.serverinfo_name)
+ strunzone(me.serverinfo_name);
+ me.serverinfo_name = strzone(_("<NO NAME>"));
+
+ if(me.serverinfo_ip)
+ strunzone(me.serverinfo_ip);
+ me.serverinfo_ip = strzone(_("<NO IP>"));
+
+ if(me.serverinfo_MOTD)
+ strunzone(me.serverinfo_MOTD);
+ me.serverinfo_MOTD = strzone(_("<NO MOTD>"));
+
+ if(me.serverinfo_pic)
+ strunzone(me.serverinfo_pic);
+ me.serverinfo_pic = strzone("no_banner");
+}
+void XonoticWelcomeDialog_configureDialog(entity me)
+{
+ welcomeDialog_resetStrings(me);
+ SUPER(XonoticWelcomeDialog).configureDialog(me);
+}
+void XonoticWelcomeDialog_readInputArgs(entity me, float argsbuf)
+{
+ int i = 0;
+ string s;
+ welcomeDialog_resetStrings(me);
+ if(argsbuf >= 0)
+ while((s = bufstr_get(argsbuf, i)) != "")
+ {
+ if(s == "name")
+ {
+ if(me.serverinfo_name)
+ strunzone(me.serverinfo_name);
+ me.serverinfo_name = strzone(bufstr_get(argsbuf, i + 1));
+ ++i;
+ }
+ else if(s == "ip")
+ {
+ if(me.serverinfo_ip)
+ strunzone(me.serverinfo_ip);
+ me.serverinfo_ip = strzone(bufstr_get(argsbuf, i + 1));
+ ++i;
+ }
+ else if(s == "motd")
+ {
+ if(me.serverinfo_MOTD)
+ strunzone(me.serverinfo_MOTD);
+ me.serverinfo_MOTD = strzone(bufstr_get(argsbuf, i + 1));
+ ++i;
+ }
+ else if(s == "pic")
+ {
+ if(me.serverinfo_pic)
+ strunzone(me.serverinfo_pic);
+ me.serverinfo_pic = strzone(strcat("/", bufstr_get(argsbuf, i + 1)));
+ ++i;
+ }
+ ++i;
+ }
+ me.serverinfo_name_ent.setText(me.serverinfo_name_ent, me.serverinfo_name);
+ me.serverinfo_ip_ent.setText(me.serverinfo_ip_ent, me.serverinfo_ip);
+ me.serverinfo_MOTD_ent.setText(me.serverinfo_MOTD_ent, me.serverinfo_MOTD);
+
+ if(me.serverinfo_pic_ent.src)
+ strunzone(me.serverinfo_pic_ent.src);
+ me.serverinfo_pic_ent.src = strzone(me.serverinfo_pic);
+}
+
+void XonoticWelcomeDialog_fill(entity me)
+{
+ entity e;
+
+ me.TR(me);
+ me.TD(me, 4, me.columns / 2, me.serverinfo_pic_ent = makeXonoticImage(string_null, 4.0/3.0));
+
+ me.gotoRC(me, 1, me.columns / 2);
+ me.TD(me, 1, me.columns / 2, me.serverinfo_name_ent = makeXonoticTextLabel(0, ""));
+ me.gotoRC(me, 2, me.columns / 2);
+ me.TD(me, 1, me.columns / 2, me.serverinfo_ip_ent = makeXonoticTextLabel(0, ""));
+
+ me.gotoRC(me, 4, 0);
+ me.TD(me, me.rows - 4 - 1, me.columns, me.serverinfo_MOTD_ent = makeXonoticTextBox());
+ me.serverinfo_MOTD_ent.allowColors = 1;
+ me.gotoRC(me, me.rows - 1, 0);
+ me.TD(me, 1, me.columns / 2, e = makeXonoticCommandButton(_("Disconnect"), '0 0 0', "disconnect", COMMANDBUTTON_CLOSE));
+ me.TD(me, 1, me.columns / 2, e = makeXonoticButton(_("OK"), '0 0 0'));
+ e.onClick = Dialog_Close;
+ e.onClickEntity = me;
+ e.preferredFocusPriority = 1;
+}
--- /dev/null
+#pragma once
+
+#include "rootdialog.qh"
+CLASS(XonoticWelcomeDialog, XonoticRootDialog)
+ METHOD(XonoticWelcomeDialog, fill, void(entity));
+ ATTRIB(XonoticWelcomeDialog, title, string, _("Welcome"));
+ ATTRIB(XonoticWelcomeDialog, color, vector, SKINCOLOR_DIALOG_WELCOME);
+ ATTRIB(XonoticWelcomeDialog, intendedWidth, float, 0.7);
+ ATTRIB(XonoticWelcomeDialog, rows, float, 14);
+ ATTRIB(XonoticWelcomeDialog, columns, float, 4);
+ ATTRIB(XonoticWelcomeDialog, name, string, "Welcome");
+
+ METHOD(XonoticWelcomeDialog, configureDialog, void(entity));
+ METHOD(XonoticWelcomeDialog, readInputArgs, void(entity, float));
+ ATTRIB(XonoticWelcomeDialog, serverinfo_name, string, string_null);
+ ATTRIB(XonoticWelcomeDialog, serverinfo_name_ent, entity, world);
+ ATTRIB(XonoticWelcomeDialog, serverinfo_ip, string, string_null);
+ ATTRIB(XonoticWelcomeDialog, serverinfo_ip_ent, entity, world);
+ ATTRIB(XonoticWelcomeDialog, serverinfo_MOTD, string, string_null);
+ ATTRIB(XonoticWelcomeDialog, serverinfo_MOTD_ent, entity, world);
+ ATTRIB(XonoticWelcomeDialog, serverinfo_pic, string, string_null);
+ ATTRIB(XonoticWelcomeDialog, serverinfo_pic_ent, entity, NULL);
+ENDCLASS(XonoticWelcomeDialog)
#include "dialog_multiplayer_create_mutators.qh"
#include "dialog_sandboxtools.qh"
#include "dialog_monstertools.qh"
+#include "dialog_welcome.qh"
#include "dialog_teamselect.qh"
#include "dialog_uid2name.qh"
#include "dialog_singleplayer.qh"
i.configureDialog(i);
me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z * SKINALPHA_DIALOG_SANDBOXTOOLS);
+ i = NEW(XonoticWelcomeDialog);
+ i.configureDialog(i);
+ me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
+
// main dialogs/windows
me.mainNexposee = n = NEW(XonoticNexposee);
--- /dev/null
+#include "textbox.qh"
+
+entity makeXonoticTextBox()
+{
+ entity me;
+ me = NEW(XonoticTextBox);
+ me.configureXonoticTextBox(me);
+ return me;
+}
+void XonoticTextBox_configureXonoticTextBox(entity me)
+{
+ me.configureListBox(me, me.scrollbarWidth, 1); // item height gets set up later
+}
+void XonoticTextBox_setSelected(entity me, float i)
+{
+ // nothing
+}
+void XonoticTextBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ me.itemHeight = me.rowsPerItem * me.fontSize / absSize_y;
+ SUPER(XonoticTextBox).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
+
+ me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
+ me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
+ me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
+}
+void XonoticTextBox_setText(entity me, string theText)
+{
+ int i, k;
+ string ts;
+ if(me.textbuf >= 0)
+ buf_del(me.textbuf);
+ me.textbuf = buf_create();
+ string s = strzone(theText);
+ me.nItems = 0;
+ k = tokenizebyseparator(s, "\\n");
+ for(i = 0; i < k; ++i)
+ {
+ getWrappedLine_remaining = argv(i);
+ if(!getWrappedLine_remaining)
+ {
+ bufstr_add(me.textbuf, "", 1);
+ ++me.nItems;
+ }
+ else while(getWrappedLine_remaining)
+ {
+ ts = getWrappedLine(1 - me.controlWidth, me.realFontSize, draw_TextWidth_WithColors);
+ if (ts != "")
+ {
+ bufstr_add(me.textbuf, ts, 1);
+ ++me.nItems;
+ }
+ }
+ }
+ strunzone(s);
+ me.textbufSize = buf_getsize(me.textbuf);
+}
+void XonoticTextBox_destroy(entity me)
+{
+ if(me.textbuf >= 0)
+ buf_del(me.textbuf);
+}
+void XonoticTextBox_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused)
+{
+ if(me.textbufSize > 0)
+ draw_CenterText(me.realUpperMargin * eY + 0.5 * eX, bufstr_get(me.textbuf, i), me.realFontSize, '1 1 1', 1, me.allowColors);
+}
--- /dev/null
+#pragma once
+
+#include "../item/listbox.qh"
+CLASS(XonoticTextBox, ListBox)
+ METHOD(XonoticTextBox, configureXonoticTextBox, void(entity));
+ ATTRIB(XonoticTextBox, fontSize, float, SKINFONTSIZE_NORMAL);
+ ATTRIB(XonoticTextBox, scrollbarWidth, float, SKINWIDTH_SCROLLBAR);
+ ATTRIB(XonoticTextBox, src, string, SKINGFX_SCROLLBAR);
+ ATTRIB(XonoticTextBox, tolerance, vector, SKINTOLERANCE_SLIDER);
+ ATTRIB(XonoticTextBox, rowsPerItem, float, 1);
+ METHOD(XonoticTextBox, resizeNotify, void(entity, vector, vector, vector, vector));
+ ATTRIB(XonoticTextBox, color, vector, SKINCOLOR_SCROLLBAR_N);
+ ATTRIB(XonoticTextBox, colorF, vector, SKINCOLOR_SCROLLBAR_F);
+ ATTRIB(XonoticTextBox, color2, vector, SKINCOLOR_SCROLLBAR_S);
+ ATTRIB(XonoticTextBox, colorC, vector, SKINCOLOR_SCROLLBAR_C);
+ ATTRIB(XonoticTextBox, colorBG, vector, SKINCOLOR_LISTBOX_BACKGROUND);
+ ATTRIB(XonoticTextBox, alphaBG, float, SKINALPHA_LISTBOX_BACKGROUND);
+
+ ATTRIB(XonoticTextBox, realFontSize, vector, '0 0 0');
+ ATTRIB(XonoticTextBox, realUpperMargin, float, 0);
+
+ METHOD(XonoticTextBox, setSelected, void(entity, float));
+ METHOD(XonoticTextBox, destroy, void(entity));
+ ATTRIB(XonoticTextBox, textbuf, int, -1);
+ ATTRIB(XonoticTextBox, textbufSize, int, 0);
+ ATTRIB(XonoticTextBox, allowColors, bool, 0);
+ METHOD(XonoticTextBox, setText, void(entity, string));
+ METHOD(XonoticTextBox, drawListBoxItem, void(entity, int, vector, bool, bool)); // item number, width/height, isSelected, isFocused
+ENDCLASS(XonoticTextBox)
+entity makeXonoticTextBox();
MUTATOR_CALLHOOK(DecodeLevelParms);
}
+
+void serverinfo_welcomemessage_send(entity this)
+{
+ msg_entity = this;
+ WriteHeader(MSG_ONE, TE_CSQC_SERVERINFO);
+ WriteString(MSG_ONE, "128.03.192.999"); // FIXME: send the real server ip
+ WriteString(MSG_ONE, autocvar_hostname);
+ WriteString(MSG_ONE, getwelcomemessage(this));
+ string pic = strcat("128.03.192.999", "/banner");
+ if(!ImageExists(pic))
+ {
+ print("Warning: image %s doesn't exist!\n", pic);
+ pic = "";
+ }
+ WriteString(MSG_ONE, pic);
+}
+
+void serverinfo_pic_send(entity this)
+{
+ string pic = strcat("128.03.192.999", "/banner");
+ if(!ImageExists(pic))
+ {
+ print("Cannot send %s, file doesn't exist!\n", pic);
+ return;
+ }
+ msg_entity = this;
+ WriteHeader(MSG_ONE, TE_CSQC_SERVERINFO_PIC);
+ WritePicture(MSG_ONE, pic, 3072);
+}
/*
=============
this.model_randomizer = random();
if (IS_REAL_CLIENT(this))
+ {
+ serverinfo_welcomemessage_send(this);
sv_notice_join(this);
+ }
// update physics stats (players can spawn before physics runs)
Physics_UpdateStats(this, PHYS_HIGHSPEED(this));
}
}
}
+
+void ClientCommand_getserverpic(entity caller, float request) // internal command, used only by code
+{
+ switch(request)
+ {
+ case CMD_REQUEST_COMMAND:
+ {
+ serverinfo_pic_send(caller);
+ return;
+ }
+
+ default:
+ case CMD_REQUEST_USAGE:
+ {
+ sprint(caller, "\nUsage:^3 cmd getserverpic\n");
+ sprint(caller, " No arguments required.\n");
+ return;
+ }
+ }
+}
void ClientCommand_mv_getpicture(entity caller, float request, float argc) // internal command, used only by code
{
CLIENT_COMMAND("autoswitch", ClientCommand_autoswitch(ent, request, arguments), "Whether or not to switch automatically when getting a better weapon") \
CLIENT_COMMAND("clientversion", ClientCommand_clientversion(ent, request, arguments), "Release version of the game") \
CLIENT_COMMAND("mv_getpicture", ClientCommand_mv_getpicture(ent, request, arguments), "Retrieve mapshot picture from the server") \
+ CLIENT_COMMAND("getserverpic", ClientCommand_getserverpic(ent, request), "Retrieve server banner from the server") \
CLIENT_COMMAND("join", ClientCommand_join(ent, request), "Become a player in the game") \
CLIENT_COMMAND("physics", ClientCommand_physics(ent, request, arguments), "Change physics set") \
CLIENT_COMMAND("ready", ClientCommand_ready(ent, request), "Qualify as ready to end warmup stage (or restart server if allowed)") \
// exempt commands which are not subject to floodcheck
case "begin": break; // handled by engine in host_cmd.c
case "download": break; // handled by engine in cl_parse.c
+ case "getserverpic": break; // handled by server in this file
case "mv_getpicture": break; // handled by server in this file
case "pause": break; // handled by engine in host_cmd.c
case "prespawn": break; // handled by engine in host_cmd.c
string MapVote_Suggest(entity this, string m);
+void serverinfo_welcomemessage_send(entity this);
+void serverinfo_pic_send(entity this);
+
// used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file
void ClientCommand_macro_write_aliases(float fh);