Inside3D!
     

Tutorial: CSQC GUI Menus
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming
View previous topic :: View next topic  
Author Message
avirox



Joined: 16 Aug 2006
Posts: 109

PostPosted: Mon May 11, 2009 2:22 am    Post subject: Tutorial: CSQC GUI Menus Reply with quote

Hey guys! I'm here with a tutorial to implement basic GUI menus into your mod via CSQC. The best part about this tutorial is that the work is practically all done for you, so you should be able to get this up and running quickly and easily by just adding a few .qc files and tweaking a couple functions. Now isn't that fun?


Background info: While working on Fortress Live I decided to try and update the interfaces for quake Team Fortress to make them appear more "modern" to attract new players. My goal was to "mimic" some of the appearance and functionality of Half-Life's VGUI. The result, however, is a pretty dynamic and highly modable GUI system.

Half-Life's VGUI:


My GUI:



The Code:
Before we begin, I assume you have a working/compiling CSQC mod. If you are new to this or have never heard of CSQC, I suggest you take a look at these tutorials:
http://qexpo.tastyspleen.net/booth.php?id=165&page=308
http://qexpo.tastyspleen.net/booth.php?id=165&page=317
Please note that this tutorial was designed for FTE's CSQC. The CSQC implementation in Darkplaces differs in some areas, but those areas should be easily tweakable.
Also please note that I will not be giving lengthy details about every bit of code we're adding here because the code is heavily commented to supply you with the specifics.

To start off, we need to make sure all the defs are in place. Go in your defs.qc (or system.qc) all the way at the bottom and add these lines:

Code:

// CSQC GUI
float in_menu, menu_selected, menu_impulse, mouse_branch;
float global_bt_no, global_menu_flags;
vector mousepos, testsize_one;
.string bt_text, bt_cmd, bt_img;
.float bt_flags, bt_no;
float vid_realwidth, vid_realheight; // the *real* width and height of the 2d rendered scene

float () MouseInBox;
void (string menu_name, float menu_flags) InitMenu;
void (string button_text, string button_cmd, vector button_spawnpos, string button_image, vector button_size, float button_flags) CreateButton;

#define MENU_TEST1         666

// global menu flags
#define MFG_NOCURSOR         1

// menu flags
#define MENUFLAG_DISABLED      1
#define MENUFLAG_BRANCH         2


Now that those are added, make sure your engine supports and has defined the following extensions (these may be different than DP's):
Code:

void(...) localcmd = #46;
float(string s) tokenize = #441;
string(float argnum) argv = #442;
float(string s) cvar = #45;                  // return cvar.value
float(string s) stof   = #81;
string(float f) ftos            = #26;
float(string st) strlen = #114;
entity() spawn                  = #14;
string(string s) cvar_string = #448;
string (vector v) vtos = #27;
string(string s, float start, float length) substring = #116;
entity (entity start, .string fld, string match) find = #18;
void (entity e) remove = #15;

Note that the above don't need to be added to your defs/system.qc if they are already defined somewhere. Just add the ones that may not be there already.

Now that the definitions are in, create the following 2 files and add them in this order after defs.qc in your progs.src:
gui_intercept.qc
gui_draw.qc

Now open gui_intercept.qc and lets get biz-ay. Copy and paste the following into that one QC file:
Code:

// Handle GUI events in menu-specific manners

// A button is clicked..
void ( entity t_button ) CSQC_GUI_ButtonClick =
{
   switch(in_menu) {
      default:
         if (!menu_impulse) {         // if the menu didn't already send the reply to the server regarding the menu..
            menu_impulse = TRUE;
            if (t_button.bt_flags & MENUFLAG_BRANCH)
               mouse_branch = TRUE;
            if (t_button.bt_cmd == "default")
               localcmd("impulse ", ftos(t_button.bt_no), "\n");
            else
               localcmd(t_button.bt_cmd, "\n");
         }
   }
};

// When a mouse hovers over a button, apply this effect..
void (string button_image, vector button_position, vector button_size, string button_text) CSQC_GUI_MouseOverButton =
{
   local float selected_button, stl;
   selected_button = MouseInBox();
   
   switch(in_menu) {
      default:
         drawpic(button_position, button_image, button_size, '1 1 1', .4);
         drawstring(button_position + '10 12 0', button_text, '8 8 0', '1 1 1', 1);
         break;
   }
};

// Called before the menu buttons are rendered
void () CSQC_GUI_MenuBackground =
{
   switch(in_menu) {
      default:         // No default background image
         break;
   }
};

// Called after the menu buttons are rendered
void () CSQC_GUI_MenuForeground =
{
   switch(in_menu) {
      default:         // No default foreground image
         break;
   }
};


Save and close. You're done there. Editing that file will be handled perhaps in a later tutorial for advanced modding Smile

Now open up gui_draw.qc. This is where we tell quake to draw your images/texts. As before, copy and paste in the following:
Code:

// CSQC GUI Stuff
// draws the selection box with text inside it (only draws one box style for now)
void (vector boxpos, vector boxsize, string boxtext, vector boxrgb, float boxalpha) spawnbox =
{
   drawpic(boxpos, "progs/csqc/csqcguiback.tga", boxsize, boxrgb, boxalpha);
   drawstring(boxpos + '10 12 0', boxtext, '8 8 0', boxrgb, 1);
};

void () CSQCGUI_ClearMenu =
{
   local entity button;
   
   // Remove all buttons
   button = find(world, classname, "gui_button");
   while (button)
   {
      remove(button);
      button = find(button, classname, "gui_button");
   }

   global_bt_no = 0;      // clear globally assigned button #s
   
   menu_selected = 0;
   in_menu = 0;
   menu_impulse = 0;
   
   // reset mouse pos
   if (!mouse_branch) {
      mousepos_x = vid_realwidth*.5;
      mousepos_y = vid_realheight*.5;
   }
   else
      mouse_branch = FALSE;
};

void () CSQCGUI_Render =
{
   local float args, boxno, i;
   entity button;

   if (!in_menu)
      return;

   cprint("\n");         // clear the currently printed menu
   
   CSQC_GUI_MenuBackground ();
   
   button = find(world, classname, "gui_button");
   while (button)
   {
      boxno = button.bt_no;
      if (MouseInBox() == boxno) {
         CSQC_GUI_MouseOverButton( button.bt_img, button.origin, button.size, button.bt_text );
         if (menu_selected) {
            CSQC_GUI_ButtonClick( button );
         }
      }
      else {
         if (button.bt_flags & MENUFLAG_DISABLED)
            spawnbox(button.origin, button.size, button.bt_text, '.4 .4 .4', .2);
         else
            spawnbox(button.origin, button.size, button.bt_text, '1 1 1', .2);
      }
         
      button = find(button, classname, "gui_button");
   }

   CSQC_GUI_MenuForeground ();
   
   if (!(global_menu_flags & MFG_NOCURSOR))
      drawstring(mousepos, "X", '8 8 0', '1 0.91 0.51', .6);      // Draw the "cursor"
};

void (string menu_name, float menu_flags) InitMenu =
{
   localcmd("csqcgui_menu ", menu_name, " ", ftos(menu_flags), "\n");
   global_menu_flags = menu_flags;
};

void (string button_text, string button_cmd, vector button_spawnpos, string button_image, vector button_size, float button_flags) CreateButton =
{
   local entity newbutton;
   
   global_bt_no++;
   
   newbutton = spawn();
   newbutton.classname = "gui_button";
   newbutton.origin = button_spawnpos;
   newbutton.size = button_size;
   newbutton.bt_img = button_image;
   newbutton.bt_text = button_text;
   newbutton.bt_cmd = button_cmd;
   newbutton.bt_flags = button_flags;
   newbutton.bt_no   = global_bt_no;
};

// Which box the mouse is hovering over currently
float () MouseInBox =
{
   float in_box, boxval;
   entity button;
   
   button = find(world, classname, "gui_button");
   while (button)
   {
      if (mousepos_y >= button.origin_y && mousepos_y <= button.origin_y+button.size_y)
         if (mousepos_x >= button.origin_x && mousepos_x <= button.origin_x+button.size_x) {
            if (!button.bt_flags & MENUFLAG_DISABLED)
               boxval = button.bt_no;
            break;
         }
      button = find(button, classname, "gui_button");
   }
   
   return boxval;
};

Save. Close. The easy part is over Smile

Now there are two key functions to any decent CSQC mod. The first, and most important, is CSQC_UpdateView(). CSQC_UpdateView, in a nutshell, runs each frame and renders pretty much everything you see. You need to add 3 lines of code to this function, right after renderscene(); is called:
Code:

vid_realwidth = width;
vid_realheight = height;
CSQCGUI_Render();

This pretty much just makes the CSQC look for CSQCGUI items to render.

Now in this same function, we also want to make sure that the crosshair is not drawn when we're in a menu. If you have a line like:
Code:

setviewprop(VF_DRAWCROSSHAIR, 1);

then you must whip it- er i mean change it to:
Code:

   if (!in_menu || global_menu_flags & MFG_NOCURSOR)
      setviewprop(VF_DRAWCROSSHAIR, 1);


I assume that you already have CSQC_UpdateView in your mod. If not, here's what mine looks like:
Code:

void(float width, float height, float menushown) CSQC_UpdateView =
{
   clearscene();       //wipe the scene, and apply the default rendering values.

   
   setviewprop(VF_MIN_X, 0);   //set the left of the view
   setviewprop(VF_MIN_Y, 0);   //set the top of the view
   setviewprop(VF_SIZE_X, width);   //set how wide the view is (full width)
   setviewprop(VF_SIZE_Y, height); //set how high the view is (full height)
   
   setviewprop(VF_DRAWENGINESBAR, 1);
   if (!in_menu || global_menu_flags & MFG_NOCURSOR)
      setviewprop(VF_DRAWCROSSHAIR, 1);
   
//        setviewprop(VF_FOV, cvar("fov"));   //this is entirely optional   FIXME: fov is x,y
   //if you use prediction, you'll need the following four lines
//   setviewprop(VF_ORIGIN, myvieworg);   //change where the view is drawn
//   setviewprop(VF_ANGLES, myviewang);   //change the angle the view is drawn at
//   makevectors(myviewang);         //calc the listener directions
//   setlistener(myvieworg, v_forward, v_right, v_up);   //change where sounds come from

   addentities(4|1|2);

   renderscene();
   
   // Set the console size vars
   vid_realwidth = width;
   vid_realheight = height;
   CSQCGUI_Render();
};


Now for the final function we need to edit: CSQC_InputEvent.
CSQC_InputEvent tracks key presses to mouse movements and everything in between. You can do a ton of stuff with this function. For purposes of my GUI mod, it hijacks the mouse so we can select things in menus.

I'm just going to supply the whole function the way I have it, but if you have your own then just add the parts in the brackets from if ( in_menu && !(global_menu_flags & MFG_NOCURSOR) ) { to somewhere accessible in your code.
Code:

// Catch inputs from the client like pressing a key or using the mouse (used for menus)
float(float eventtype, float param1, float param2) CSQC_InputEvent =
{
   if ( in_menu && !(global_menu_flags & MFG_NOCURSOR) ) {
      if (eventtype == 0)   {//keypress down inputs
         if (param1 == 512) {      // Left Click
            if (MouseInBox())
            {
               menu_selected = MouseInBox();         // Actual menu selection is handled in CSQCGUI_Render() for modability purposes
               //print("You clicked box number ", ftos(MouseInBox()), "\n");
            }
            return TRUE;
         }
         else if (param1 == 513) {         // Right click
            print(vtos(mousepos),"\n");
         }
         else if (param1 == 514) {         // Middle click
            if (testsize_one == '0 0 0')
               testsize_one = mousepos;
            else {
               print(vtos(mousepos-testsize_one),"\n");
               testsize_one = '0 0 0';
            }
         }
      }
      if (eventtype == 2/* && in_menu*/)   //mouse
      {

         mousepos_x += param1;
         mousepos_y += param2;

         if (mousepos_x < 0)
            mousepos_x = 0;
         if (mousepos_y < 0)
            mousepos_y = 0;

         if (mousepos_x > vid_realwidth)
            mousepos_x = vid_realwidth;
         if (mousepos_y > vid_realheight)
            mousepos_y = vid_realheight;            

         return TRUE;
      }
   }
   return FALSE;
};


Congratulations! If that compiled successfully (I'm sure I will need to update this tutorial many times) then you now have my GUI system installed in your mod with minimal intrusion. In my next post I will describe getting your first menus loaded..


Last edited by avirox on Tue May 12, 2009 1:51 am; edited 4 times in total
Back to top
View user's profile Send private message
avirox



Joined: 16 Aug 2006
Posts: 109

PostPosted: Mon May 11, 2009 2:24 am    Post subject: Reply with quote

Part II: Adding your own menus with the above system

Alright, now it's time to put this code to use. In this part of the tutorial I'm going to explain a very simple and basic style of menu. At this point I'd like to mention that the menu system I created was designed to interact with SSQC menus which use 1,2,3,etc. button presses. However, the menus can also be used stand-alone (note that this offers somewhat limited modability since it's only interacting with client commands).

The menu we're going to create today will look like this:


Surprisingly or not, I was able to cook that menu up in under a minute's worth of code using my GUI system.

Lets first get some necessary files and add some commands.

Download the button background image here.
Once you've downloaded that, take the file and put it in your mod's progs/csqc/ folder (or create that and put it there).

Next up we're going to precache that file and setup some more functions. Go in your defs.qc/system.qc and add the following to the bottom:
Code:

void () CSQCGUI_ClearMenu;  // clears the screen of the current menu and the mouse position
void () Menu_TestMenu; // Test button menu


In order to precache the file, we want to find the CSQC function that's called the earliest. This function is CSQC_Init(), called when you first load up your csprogs.dat. If you don't have a CSQC_Init(), then make one now - it could be placed anywhere after defs.qc, but I'll leave the specifics up to you. In that function, add the following:

Code:

precache_pic("progs/csqc/csqcguiback.tga"); // GUI Button Background
registercommand("testmenu"); // call up our test menu

Save and close. For purposes of this tutorial, we're done there.

Now what we're going to do is add a new function which sets up the menu and its buttons. I suggest adding specific menus into their own .qc file(s) located after the GUI qc files. For purposes of making sure we're all on the same page, create a gui_menu_test.qc file, and make sure it's added after the previous 2 gui files in your progs.src.

Open gui_menu_test.qc and add the following function:
Code:

void () Menu_TestMenu =
{
   local vector men;                  // position of the first menu button
   local vector but_size;               // size of all the buttons
   local string buttonback;            // Button image
   
   but_size = '160 35 0';
   men_x = (vid_realheight*0.5) - (but_size_x*0.5);;      // first button will be displayed in the dead center of the horizontal area
   men_y = vid_realheight*0.2;                     // first button will be displayed 20% of the vertical value from the top of the screen
   buttonback = "progs/csqc/csqcguiback.tga";
   
   
   InitMenu( "menu_testme", 0 );
   CreateButton( "Fire!",             "+attack\n;testmenu",                             men, buttonback, but_size, 0); men_y+=but_size_y;
   CreateButton( "Jump!",             "+jump\n;testmenu",                               men, buttonback, but_size, 0); men_y+=but_size_y;
   CreateButton( "Say Cheese!",       "say cheese\n;testmenu",                          men, buttonback, but_size, 0); men_y+=but_size_y;
   CreateButton( "Go Apes**t!",       "cl_yawspeed 2000\n;+right\n;testmenu",             men, buttonback, but_size, 0); men_y+=but_size_y;
   CreateButton( "Back to normal!!",    "cl_yawspeed 5\n;-right\n;-jump\n;-attack\n;testmenu",    men, buttonback, but_size, 0); men_y+=but_size_y;
}


Let me briefly explain the way InitMenu and CreateButton work.

InitMenu is mainly used for debugging purposes. If something goes wrong somewhere, it allows me to know which menu I'm looking at using the cvar "csqcgui_menu". The second field of InitMenu allows you to specify whether this is a mouse-driven menu or not. If I called InitMenu using, say:
InitMenu( "menu_testme", MFG_NOCURSOR );
Then the menu would be displayed, but your mouse would still control the player's view. This is only useful when you have SSQC running in the background to interpret "impulse X" or other client commands. If you wanted to replace an SSQC text menu but didn't need or want mouse support with it, then this is what you would use.

CreateButton is fairly straight forward, and is easy to use as well as highly tweakable:

Field #1 is the button's text. If this is left blank then only the button itself will be displayed.

Field #2 is the command that gets executed when the button is clicked. You will note that in the above code, "testmenu" is the last thing being called in this field. The reason for this is that with the current GUI system, once you click a button, the menu will send a single function and then cease to take any more clicks. Again, this is because the menu is meant to interact with SSQC and be updated by the server on what to display next.

Field #3 specifies the X,Y(,Z) position where your button will spawn on the screen. For debugging purposes, I've left it so that when you right-click it will tell you the X,Y position that your cursor currently occupies.

Field #4 is the button image. Always make sure this image is precache'd first when used, else other connecting clients won't be able to download it, and it will generally work slower.

Field #5 is the button size. No big brainer here, but before you set a button size make sure you're scaling it properly to the 2D resolution your menus are aiming for. If you want to make buttons the same size regardless of 2d resolution, set sizes based on con_realheight/con_realwidth.

Finally, field #6 allows you to specify certain flag properties. Currently there are 2:
#define MENUFLAG_DISABLED 1
#define MENUFLAG_BRANCH 2
MENUFLAG_DISABLED means that the button is disabled and therefore unable to be used/clicked.
MENUFLAG_BRANCH just means that this selection leads to another selection. For example:


So now we have the menus, the commands, and the images. The next part is how to get all this to display in-game.

There are many ways to parse server/client information in CSQC, and how you do it in your mod is really up to you, but to keep this tutorial simple I'm just going to use CSQC_ConsoleCommand. This function picks up any console/alias command from the client, IE: +showscores, +attack, etc. In this function, add the following lines of code:
Code:

   local float args;
   args = tokenize(strMessage);
   
   if (argv(0) == "testmenu") {
      if (in_menu) {
         CSQCGUI_ClearMenu();
         return TRUE;
      }
      CSQCGUI_ClearMenu();      // Clear the current menu
      in_menu = MENU_TEST1;
      Menu_TestMenu();
      
   }


Now load up your mod and go in the console. There, type (or bind to a key) "testmenu". If all goes well, you should see a menu which resembles the first image in this post, and you should be able to use your mouse to click and use the menu items.


EDIT: Small bonus tutorial based on the above!
Let me just go over a quick and easy way to make the menu look less bland.

Adding a background image can make your menus pop out more and look much cooler. Go in the CSQC_GUI_MenuBackground function in gui_parse.qc and change it to the following:
Code:

// Called before the menu buttons are rendered
void () CSQC_GUI_MenuBackground =
{
   local vector menback_pos, menback_size;
   
   switch(in_menu) {
      case MENU_TEST1:
         // individual button size: '160 35 0'
         menback_pos_x = ((vid_realwidth*0.5) - (160*0.5)) - 20;
         menback_pos_y = (vid_realheight*0.2) - 20;
         menback_size_x = 200;            // Since we're overlapping 20 on each side, add 40 to 160.
         menback_size_y = (35*5)+40;         // There are 5 buttons in a row vertically, so compensate for that and add 40
         drawpic(menback_pos, "progs/csqc/csqcguiback.tga", menback_size, '1 1 .5', .2);         // Large text box (class info)
         break;
      default:         // No default background image
         break;
   }
};

Basically what I did here was draw the same box we used for the button images, and enlarged it. I also changed the RGB value to give it a different color. After adding this code, your menu should now look like this:



This same code can be applied for CSQC_GUI_MenuForeground, but in that case the new box will cover the menu items rather than be behind them. Tinker around with this and create different background images - it can really help the over-all look and feel!


Last edited by avirox on Tue May 12, 2009 1:20 pm; edited 9 times in total
Back to top
View user's profile Send private message
Error
Inside3D Staff


Joined: 05 Nov 2004
Posts: 558
Location: VA, USA

PostPosted: Mon May 11, 2009 7:11 am    Post subject: Reply with quote

oh my god I love you
_________________
Inside3D : Knowledge Is Power
Darkplaces Documentation Wiki
Back to top
View user's profile Send private message Send e-mail Visit poster's website AIM Address Yahoo Messenger MSN Messenger
leileilol



Joined: 15 Oct 2004
Posts: 1321

PostPosted: Mon May 11, 2009 9:42 am    Post subject: Reply with quote

i like this, together with dp's loadfont you can make a very convincing ripoff
_________________
Back to top
View user's profile Send private message
avirox



Joined: 16 Aug 2006
Posts: 109

PostPosted: Mon May 11, 2009 4:45 pm    Post subject: Reply with quote

It's not meant to be a ripoff, just a way to mimic the functionality of more modern in-game GUI's. You can go well above and beyond the simple box/branch system, but for simplicity I've kept it to that for the first 2 parts of this tutorial. Maybe later I'll write a third part on SSQC=>CSQC GUI interaction, because that's where the sweet spot of these menus really lies.
Back to top
View user's profile Send private message
r00k



Joined: 13 Nov 2004
Posts: 483

PostPosted: Mon May 11, 2009 8:15 pm    Post subject: Reply with quote

Nice! Is that using FTE??
Back to top
View user's profile Send private message
GiffE



Joined: 08 Oct 2006
Posts: 141
Location: USA, CT

PostPosted: Mon May 11, 2009 8:20 pm    Post subject: Reply with quote

Very nice work!
Something like this will certainly save me some work!
_________________
http://www.giffe-bin.net/
Back to top
View user's profile Send private message Visit poster's website
avirox



Joined: 16 Aug 2006
Posts: 109

PostPosted: Tue May 12, 2009 1:52 am    Post subject: Reply with quote

Thanks guys! Anyways, I noticed that in the first post the code for the MENUFLAG_DISABLED flag was not added. This has been remedied. The two functions I changed were:

MouseInBox()
CSQCGUI_Render()

I'm also cooking up a new quick tutorial for drop-down menus.
Back to top
View user's profile Send private message
Chip



Joined: 21 Jan 2009
Posts: 314
Location: Romania

PostPosted: Tue May 12, 2009 7:03 am    Post subject: What about Darkplaces? Reply with quote

So, if I want to use this approach using Darkplaces, what should the changes be? It could really change the appearance of my upcoming Quantum Engine.
_________________
My Projects: Quake 1 Mods | OpenQuartz 2 | ChipQuake
Back to top
View user's profile Send private message Visit poster's website
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Tue May 12, 2009 7:35 am    Post subject: Reply with quote

I see nothing mentioned in that tut that will fail with DP.

Although you will need FTEQCC to compile it (thanks to 'switch' and '#define'), but you could recode those parts if you want to use FrikQCC.
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
CocoT



Joined: 14 Dec 2004
Posts: 599
Location: Belly-Gum

PostPosted: Wed May 13, 2009 2:29 pm    Post subject: Reply with quote

Wonderful tutorial, Avirox! I can't wait to try it out Smile
_________________
http://www.planetcocot.net/
Back to top
View user's profile Send private message Send e-mail Visit poster's website
avirox



Joined: 16 Aug 2006
Posts: 109

PostPosted: Wed May 13, 2009 2:52 pm    Post subject: Reply with quote

Part III: Basic Menus II

So I thought I might dedicate today's GUI tutorial to a basic drop-down/branch menu. This tutorial requires that you've done the steps in Part I and II, as this code borrows some of the same resources from the first menu described above.

The result of this menu will look something like this:


Lets open up our definitions qc file and add a new def for our drop-down menu:
Code:

#define MENU_TEST_DROPDOWN      1337


That's all we need there. Next we gotta make sure the menu can be called up. For purposes of keeping things simple, lets add a new client command which can be called at will to summon up the menu. In CSQC_Init() add this somewhere:
Code:

registercommand("testmenu2");


Now that that's defined, the command can be intercepted by the CSQC_ConsoleCommand function. Thus, like in the last tutorial, that's where we're going to spawn our new menu from.

Add the following lines to CSQC_ConsoleCommand:
Code:

if (argv(0) == "testmenu2") {
      CSQCGUI_ClearMenu();      // Clear the current menu
      in_menu = MENU_TEST_DROPDOWN;
      //print(argv(1)," eyyy\n");
      Menu_DropdownMenu(stof(argv(1)));
      
   }


Now wait! We didn't add our menu yet! Preferably we should add this menu function somewhere before CSQC_ConsoleCommand, but if that's not the case in your code then just make sure you have void (float mn_flags) Menu_DropdownMenu; defined somewhere in your definitions file.

Here's the code for our simple drop-down menu:
Code:

void (float mn_flags) Menu_DropdownMenu =
{
   local vector men;                  // position of the first menu button
   local vector but_size;               // size of all the buttons
   local string buttonback;            // Button image
   local float button_flag;
   
   // if mn_flags is 1, we will display the drop-down menu
   if (mn_flags > 0)
      button_flag = MENUFLAG_DISABLED;      // disable the other buttons when the drop-down menu is active

   but_size = '145 35 0';
   men_x = vid_realwidth*0.1;
   men_y = vid_realheight*0.2;                     // first button will be displayed 20% of the vertical value from the top of the screen
   buttonback = "progs/csqc/csqcguiback.tga";
   
   InitMenu( "menu_dropdown", 0 );
   CreateButton( "Say This!",          "say this!\n;clearmenu",men, buttonback, but_size,             button_flag);       men_x+=but_size_x;
   CreateButton( "Say That!",          "say that!\n;clearmenu",men, buttonback, but_size,             button_flag);       men_x+=but_size_x;
   CreateButton( "More Options",       "nothing",             men, buttonback, but_size - '25 0 0',    MENUFLAG_DISABLED); men_x+=but_size_x - 25;
   if (mn_flags > 0)
      CreateButton( "-",         "testmenu2",         men, buttonback, '25 35 0',            MENUFLAG_BRANCH);
   else
      CreateButton( "+",         "testmenu2 1",         men, buttonback, '25 35 0',            MENUFLAG_BRANCH);   

   if (mn_flags > 0) {
      men_x-=(but_size_x - 25);   //
      men_y+=but_size_y;         // draw new menu items below the "More Options" button
   }
   else
      men = -2*but_size;      // spawn menus off the screen
      
   CreateButton( "Fire!",    "+attack\n;testmenu",     men, buttonback, but_size, 0); men_y+=but_size_y;
   CreateButton( "Jump!",    "+jump\n;testmenu",      men, buttonback, but_size, 0); men_y+=but_size_y;
}


What this menu will basically do, when loaded up without any value for "mn_flags", is display 3 buttons:
Say This!
Say That!
More Options!
and a fourth button which will act as our little list expander ("+").

The "More Options!" Button is permanently flagged with MENUFLAG_DISABLED, which was discussed in Part II. It cannot be clicked or inter-acted with - it's just there to act as a title per-se to the drop-down list.

When the "+" button is clicked, the menu sends the client a command to parse, adding a value to "mn_flags". This will disable the other 2 buttons, and display the 2 new menus under the third button. If this button is clicked again (now displayed as a "-"), it will move the 2 new buttons away and re-enable the first 2.

Also note that the "+"/"-" buttons use the MENUFLAG_BRANCH flag. That just means that when you click this button your mouse position will not reset to the center of the screen, and just stay where it is.

I highly suggest reading through the code comments, as they provide more specifics on what's going on.

And that's it! Once you're in the game just call "testmenu2" from the console and your shiny new menu should pop up!

Next up (when I get the time) I'll explain the GUI intercept events and give yall some cool things to do with them!
Back to top
View user's profile Send private message
avirox



Joined: 16 Aug 2006
Posts: 109

PostPosted: Tue May 19, 2009 3:18 pm    Post subject: Reply with quote

Good news for lazy people! I've compiled a little "clean" version of the CSQC GUI which acts as a standalone CSQC mod. You can download it here. This is a CSQC mod with the GUI already implemented + the first menu tutorial above. The above tutorials are for implementing the menus into your existing CSQC mod, but this source is only for those starting a new one. Needs FTEQCC/GUI to compile.

Last edited by avirox on Thu May 21, 2009 1:58 pm; edited 1 time in total
Back to top
View user's profile Send private message
qbism



Joined: 04 Nov 2004
Posts: 82

PostPosted: Thu May 21, 2009 1:47 am    Post subject: Reply with quote

You mean http://avirox.amnesiagames.com/FTE/csqc_gui_10.zip ?
Back to top
View user's profile Send private message Visit poster's website
leileilol



Joined: 15 Oct 2004
Posts: 1321

PostPosted: Thu May 21, 2009 5:29 am    Post subject: Reply with quote

qbism wrote:
*


you!
_________________
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming All times are GMT
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2004 phpBB Group