Forum

Tutorial: How to do DAT menu.DAT

Post tutorials on how to do certain tasks within game or engine code here.

Moderator: InsideQC Admins

Tutorial: How to do DAT menu.DAT

Postby behind_you » Wed Jun 08, 2011 9:13 am

[speech]Modding for quake is great because you can do just about anything you want to. However, it's extremely difficult since about 99% everything you can do with quake these days is unknown. There are hardly any tutorials that point people on the right path and newbies are left to fend for themselves. Being a noob myself, I know all about these stuggles. That's why I am going to try to educate everyone about what little I know, just to make things easier. Of course, my techniques are nowhere near perfect, but if they were, what would there be to learn?[/speech]

So, I managed to get my hands on a menu.dat source (thank you giffe and others) and started tinkering around with it. Creating and manipulating your own menus can be hard unless you know what you are doing. That's why I'm dedicating this tutorial to uncovering the secrets of menu.dat and how to make your own menu through nothing but QuakeC.

The Pros:
- Easily design your own menus
- Only needs some qc knowledge

The Cons:
- Menu is slow during gameplay
- Menu sometimes crashes when entering game, must be engine bug

What you need:
Download this file. It's the menuqc scratch I got and modified together along with the fteqcc compiler you will need.

OK let's get started. Extract the zip file to a separate folder. You will find the compiler 'fteqccgui.exe' along the the .qc and .qh files and the progs.dat source.

Open menu.qc first. The first thing you will see is the m_init(); function. This function is called when the menu is loaded (when you start the game). It contains stuff like returning the screensize for use and other things of the sort. I will explain the other codes found here as I go along.

Right below you will find the m_keyup(); and m_keydown(); functions. These functions detect mouse and keyboard input. So if you want to recieve what the user is typing, this is where to do it. Notice how I already put some code that will detect when the mouse button is clicked or released.

After this, you will find a float function (I think that's what it's called) called check_mouse. This is an extremely important function. It will return TRUE or FALSE depending on if your mouse is on a selectable menu item.

Head down to m_draw();. This is the code that draws what you will see. AKA the equivilant of csqc's CSQC_UpdateView(); Here we will do things like draw the mouse cursor and draw the menus.

The bottom has other menu functions. You do not need to worry about these things. m_shutdown(); is executed when you exit, so that may be of use to anyone(ie like writing cvars with FRIK_FILE).

OK. The files msys.qc and mbuiltin.qh contains a bunch of definitions for use. I've added my own at the bottom of these files.

Before beginning, I would like to mention that there are a lot of ways to do this. Some are better than my way. Some are worse. I'll illustrate the technique I use to get my menus to show up.

Now let's begin. First off, we must define our menus for use. So open msys.qh and scroll down to the bottom. Find the following to the bottom of msys.qh:

Code: Select all
float menumode;                  //which menu to show. menu options below


This is a simple float that determines which menu you want to see. Add this after:

Code: Select all
float NONE = -1;
float OFFLINEMENU = 0;
   float SELECTDIFFICULTY = 0.1;
   float LOADGAME = 0.2;
float INGAMEMENU = 1;
   float SAVEGAME = 1.1;


These constant floats represent your menus. Notice how I tabbed some of them out to look like submenus. This simply makes life easier for you when you go to code when each menu will appear and when you will inevitably build on this. You could use their number values instead and get a migraine if you want.

Now open mbuiltin.qh and near the bottom you will find a function I wrote for you called selecttext. You will be using this function to draw selectable text that will lead to another menu.

Alright now that you have some sort of picture on how your menu will look like. Create a new file and name it "mdraw.qc" (without quotes). Add this function:

Code: Select all
void() offlinemenu =
{
   drawstring(centerscreen - '0 30 0', "Welcome to your new menu!", '20 20 0', '0 0 1', 1, 0);
   
   selecttext (centerscreen, "New Game", SELECTDIFFICULTY, '8 8 0');

   selecttext (centerscreen, "Load Game", LOADGAME, '8 8 0');

   selecttext (centerscreen + '0 15 0', "Options", OPTIONS, '8 8 0');

   cmdtext (centerscreen + '0 30 0', "Quit", "quit", '8 8 0');
};


Using the selecttext function, three selectable text items have been made in your start menu. To explain how it works:

'centerscreen' is the first parm of selecttext. It's the position the text will be written. notice how the other options add 15 y units to it, just to prevent overlapping.

The second parm is a string that represents how the text will look like.

The third parm is what menu you will be taken to when you click on the text. Notice how I used the constant floats I defined in the beginning. Told you it makes life easier :D.

Forth parm is the text size. Feel free to change BUT NEVER ASSIGN ANY Z VARIABLE! It's pointless. Text is 2D. Z variables don't affect your text and you will simply be spammed with warnings.

For an explanation of the selecttext function itself:

void(vector pos, string txt, float gomenu, vector textsize) selecttext =
{
local float charnum, talpha;
local vector txtsize;

charnum = strlen(txt); //gets how many characters (including spaces) are in the text
txtsize_x = textsize_x * charnum; //multiplies the size of each character by how many characters, effectively getting how big the whole string is
txtsize_y = textsize_y; //y stays the same

if (check_mouse(pos, txtsize)) //if your mouse is positioned on the text
{
talpha = 1; //brighten the text
if (soundonce != txt) //play the sound of your choice once
{
localsound("sound/menu/menu3.wav");
soundonce = txt;
}
if (mhit == 0) //if you click on the text while positioned on the text
{
menumode = gomenu; //head over to whatever menu specified
}
mhit = 1;
}
else
{
talpha = 0.75; //otherwise, make text less bright and do nothing
}

drawstring(pos, txt, textsize, '1 1 1', talpha, 0); //draw the text
};

Now our first menu is complete! Let's make another! This time we will make a menu that allows you to select the difficulty level you want to play. This beats the tacky Quake technique of choosing which teleport to go through to choose difficulty :P.

Add this to the end of mdraw.qc:
Code: Select all
void() selectdifficulty =
{
   drawstring(centerscreen - '0 30 0', "Select Difficulty!", '20 20 0', '1 0 0', 1, 0);//Title
   
   exectext (centerscreen, "Easy", "skill", "0", "map e1m1\n", '8 8 0');

   exectext (centerscreen + '0 15 0', "Normal", "skill", "1", "map e1m1\n", '8 8 0');

   exectext (centerscreen + '0 30 0', "Hard", "skill", "2", "map e1m1\n", '8 8 0');

   exectext (centerscreen + '0 45 0', "Nightmare", "skill", "3", "map e1m1\n", '8 8 0');
   
   selecttext (centerscreen + '0 60 0', "Return", OFFLINE, '8 8 0');

};


As you can see, these options use a new function I made called exectext. After selecting difficulty level, this will start the game with whatever difficulty level you chose. I also included a return option that returns you to the previous menu.

Now an explanation:

The first parm of exectext is the position. I change the y variable of some options just to keep from overlapping

Second parm is the text you will see.

Third parm is what cvar to change, in this case the "skill" cvar.

Forth parm is what to change the cvar's value to.

Fifth parm is what to type in the console after. In this case it's "map e1m1" which starts the map you want.

Last parm is text size. NO Z VALUE!!!!

The exectext function is pretty much the same as selecttext besides setting cvar and starting the map.

HAHA! We got the jist of it! Now we made a menu and made it select difficulty level and start the game! Nice!

One thing that pissed me off the most about Quake is how the menu was always the same no matter if you were starting the game, in the middle of the game, or even in a multiplayer game! Let's fix that!

Add this ingamemenu and savegame function at the bottom of mdraw.qc:

Code: Select all
void() ingamemenu =
{
   drawstring(centerscreen - '0 30 0', "In the Game!", '20 20 0', '1 0 0', 1, 0);//Title

   selecttext (centerscreen, "Save Game", SAVEGAME, '8 8 0');

   selecttext (centerscreen + '0 15 0', "Load Game", LOADGAME, '8 8 0');

   cmdtext (centerscreen + '0 30 0', "Quit", "quit", '8 8 0');
};

void() savemenu =
{
   drawstring(centerscreen - '0 30 0', "Save it!", '20 20 0', '1 0 0', 1, 0);//Title

   cmdtext (centerscreen, "Save Slot 1", "save s01", '8 8 0');

   cmdtext (centerscreen + '0 15 0', "Save Slot 2", "save s02", '8 8 0');

   cmdtext (centerscreen + '0 30 0', "Save Slot 3", "save s03", '8 8 0');
   
   selecttext (centerscreen + '0 45 0', "Return", NONE, '8 8 0');
};

void() loadmenu =
{
   drawstring(centerscreen - '0 30 0', "Load it!", '20 20 0', '1 0 0', 1, 0);//Title

   cmdtext (centerscreen, "Load Slot 1", "load s01", '8 8 0');

   cmdtext (centerscreen + '0 15 0', "Load Slot 2", "load s02", '8 8 0');

   cmdtext (centerscreen + '0 30 0', "Load Slot 3", "load s03", '8 8 0');

   selecttext (centerscreen + '0 45 0', "Return", NONE, '8 8 0');
};


This uses another function called cmdtext. It's exactly the same as exectext except it doesn't mess with any cvars. This ingame menu uses very basic saving and loading techniques. You can expand it as much as you want.

You should get the jist of things now. Basic menus complete! Now to actually implement them!

Open menu.qc. Find the m_draw(); function. Add this before time = gettime():

Code: Select all
   if (menumode == NONE)
      {
      if (clientstate() == CS_CONNECTED)
         {
         menumode = INGAME;
         }
      else
         {
         menumode = OFFLINE;
         }
      }
   else if (menumode == INGAMEMENU)
      ingamemenu();      
   else if (menumode == SELECTDIFFICULTY)
      skillselect();
   else if (menumode == SAVEGAME)
      savemenu();
   else if (menumode == LOADGAME)
      loadmenu();
   else
      offlinemenu();


Now the use of the constant floats is truly appreciated! This checks what menumode it is and draws the appropriate menu.

There you have it! Your own basic menu! Compile and place the menu.dat file inside your game directory! I plan on adding to this tutorial so look out!
Last edited by behind_you on Mon Jun 13, 2011 6:35 am, edited 1 time in total.
User avatar
behind_you
 
Posts: 237
Joined: Sat Feb 05, 2011 6:57 am
Location: Tripoli, Libya

Postby Baker » Wed Jun 08, 2011 3:16 pm

This is nice, I've never seen any documentation for menu.dat creation before.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Postby Feared » Wed Jun 08, 2011 8:13 pm

Nice indeed.

I'm not a fan of these free file hosting sites that make you wait 30 seconds before downloading your file that might be 20kb. These sites also usually delete files after not being downloaded for 30 days or so.

Here's a mirror,
http://bfeared.com/library/quake/dump/makemenuqc.rar
User avatar
Feared
 
Posts: 95
Joined: Fri Jun 11, 2010 11:58 pm
Location: Wylie, TX

Postby qbism » Thu Jun 09, 2011 1:03 am

Now we need a programming tutorial to put menu.dat support in our engines! :)
EDIT: can start by (re) reading Baker's CSQC: Global Implementation Project
User avatar
qbism
 
Posts: 1236
Joined: Thu Nov 04, 2004 5:51 am

Postby behind_you » Mon Jun 13, 2011 6:22 am

Thanks a lot! And thanks for re uploading it, feared.

People really need to share their ideas and post tutorials to make life easier for the rest of us!

EDIT: Using your link for the tutorial Feared. Thanks again!
User avatar
behind_you
 
Posts: 237
Joined: Sat Feb 05, 2011 6:57 am
Location: Tripoli, Libya

Re: Tutorial: How to do DAT menu.DAT

Postby Blackstar1000 » Fri Dec 16, 2011 1:48 am

can someone make a youtube video of this thing in action?
Knives out. Catch the mouse. Squash his head. Put him in your mouth.
Blackstar1000
 
Posts: 52
Joined: Mon Sep 13, 2010 5:16 pm

Re: Tutorial: How to do DAT menu.DAT

Postby toneddu2000 » Fri Dec 16, 2011 10:08 pm

actually compiler gave me some error compiling it (some const names not consistent) and even after fixing errors menu doesn't work correctly(menu always shows on screen,even after pressed esc key). So, maybe, first to do a video, someone should correct the code!
toneddu2000
 
Posts: 1329
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Tutorial: How to do DAT menu.DAT

Postby toneddu2000 » Sun Feb 19, 2012 4:38 pm

Ok I fixed it. Next week I'll post it. Now I need just one help. When I select a sub menu, the menu items of the preceding menu are still visible.
I made an example. If I create a hierarchy like this:
MAIN MENU
---- NEW GAME
-------- EASY
-------- MEDIUM
-------- HARD
---- OPTIONS
If i click on MAIN MENU it shows me NEW GAME string but under it, it also shows the NEW GAME string! So it becomes a mess!
I tried to use the clearscene function from CSQC but it doesn't work with menuqc (the compiler didn't return any errors but it gave me quakec error at runtime)
This is the (ugky I know) code I used to detected the menu clicked (I used behind_you code with some makeups)
Code: Select all
void() menuchoice
{

if (menumode == RETURNTOMAIN)
      {
     drawstring(centerscreen - '0 30 0', "", '20 20 0', '0 0 1', 1, 0);
   
      drawstring(centerscreen - '0 50 0', "", '20 20 0', '0 0 1', 1, 0);

      drawstring(centerscreen - '0 70 0', "", '20 20 0', '0 0 1', 1, 0);

      drawstring(centerscreen - '0 90 0', "", '20 20 0', '0 0 1', 1, 0);
    
     drawstring(centerscreen - '0 110 0', "", '20 20 0', '0 0 1', 1, 0);
    
     drawstring(centerscreen - '0 130 0', "", '20 20 0', '0 0 1', 1, 0);
   
     mainmenu();
     }
    
if (menumode == SELECTDIFFICULTY)
      {
     drawstring(centerscreen - '0 30 0', "", '20 20 0', '0 0 1', 1, 0);
   
      drawstring(centerscreen - '0 50 0', "", '20 20 0', '0 0 1', 1, 0);

      drawstring(centerscreen - '0 70 0', "", '20 20 0', '0 0 1', 1, 0);

      drawstring(centerscreen - '0 90 0', "", '20 20 0', '0 0 1', 1, 0);
     skillselect();
     }
    
if (menumode == SAVEGAME)
      {
     drawstring(centerscreen - '0 30 0', "", '20 20 0', '0 0 1', 1, 0);
   
      drawstring(centerscreen - '0 50 0', "", '20 20 0', '0 0 1', 1, 0);

      drawstring(centerscreen - '0 70 0', "", '20 20 0', '0 0 1', 1, 0);

      drawstring(centerscreen - '0 90 0', "", '20 20 0', '0 0 1', 1, 0);
     skillselect();
     }
    
}


As you can see I even tried to fill blank strings in the same position of every item BEFORE the new menu it shows but they don't replace the old ones.
I just need a "sweeping function" to clear the screen
Thanks in advance

PS: just a side note: do you know how to use fonts (ttf files)
Putting these lines in default.cfg
Code: Select all
loadfont console gfx/fonts/arial.ttf
loadfont menu gfx/fonts/arial.ttf
con_textsize 10

it uses the Arial font (don't worry I know it's copyrighted - it was just a test! :) ) for the console but NOT for the menu
toneddu2000
 
Posts: 1329
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Tutorial: How to do DAT menu.DAT

Postby toneddu2000 » Wed Feb 29, 2012 2:00 pm

Any possible hint guys? I still can't display new items because old items are still present in the menu tree
toneddu2000
 
Posts: 1329
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Tutorial: How to do DAT menu.DAT

Postby Ghost_Fang » Wed Mar 07, 2012 9:12 pm

Use "else if" when calling your menu types in m_draw,

Code: Select all
if (menumode == INGAMEMENU)
      ingamemenu();
  else if (menumode == SINGLEPLAYER)
      singleplayer();     
  else if (menumode == SELECTDIFFICULTY)
      selectdifficulty();
  else
     mainmenu();


That fixed it for me
Ghost_Fang
 
Posts: 336
Joined: Thu Nov 12, 2009 4:37 am

Re: Tutorial: How to do DAT menu.DAT

Postby toneddu2000 » Thu Mar 08, 2012 8:31 am

Thanks a lot Ghost_Fang! I'll try as soon as I can!
toneddu2000
 
Posts: 1329
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Tutorial: How to do DAT menu.DAT

Postby JasonX » Sun Mar 25, 2012 2:21 am

Any updates on the TTF font thing?
JasonX
 
Posts: 409
Joined: Tue Apr 21, 2009 2:08 pm

Re: Tutorial: How to do DAT menu.DAT

Postby toneddu2000 » Sun Mar 25, 2012 4:56 pm

JasonX wrote:Any updates on the TTF font thing?

Unfortunately still not. Fonts are displayed only on the console menu but not in the game menu.
behind_new never replied so,without his permission, I can't upload the menu source code :(
my intent was to release it under MIT license, so everyone could use it even without releasing code changes
toneddu2000
 
Posts: 1329
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Tutorial: How to do DAT menu.DAT

Postby Mexicouger » Tue Sep 11, 2012 12:16 am

How can you call the menu via an impulse in-game?
User avatar
Mexicouger
 
Posts: 514
Joined: Sat May 01, 2010 10:12 pm


Return to Programming Tutorials

Who is online

Users browsing this forum: No registered users and 1 guest