mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-01-17 22:50:51 +00:00
1. general menu code cleanup
2. added lots of comments to my menu code 3. wrote a string hash api (key -> value assignment list) Hm, nothing else afaik. ;)
This commit is contained in:
parent
51805ebd92
commit
757556022b
10 changed files with 806 additions and 108 deletions
|
@ -11,7 +11,7 @@ QCPPFLAGS=-I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
|
|||
|
||||
pkgdata_DATA= menu.dat
|
||||
|
||||
menu_src= menu.qc servlist.qc options.qc cbuf_def.qc cvar_def.qc draw_def.qc file_def.qc game_def.qc inputline_def.qc key_defs.qc menu_def.qc options_util.qc string_def.qc controls_o.qc
|
||||
menu_src= menu.qc servlist.qc options.qc cbuf_def.qc cvar_def.qc draw_def.qc file_def.qc game_def.qc inputline_def.qc key_defs.qc menu_def.qc options_util.qc string_def.qc controls_o.qc stringh_def.qc
|
||||
|
||||
menu.dat: menu.src $(menu_src)
|
||||
$(QFCC) $(QCFLAGS) $(QCPPFLAGS) -P $<
|
||||
|
|
|
@ -1,3 +1,34 @@
|
|||
/*
|
||||
options.qc
|
||||
|
||||
Options menu
|
||||
|
||||
Copyright (C) 2002 Robin Redeker <elmex@x-paste.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
This is a array of the commands, which
|
||||
can be binded by menu.
|
||||
*/
|
||||
#define NUM_BINDED_KEYS 34
|
||||
string [NUM_BINDED_KEYS] key_bindings =
|
||||
{
|
||||
|
@ -25,7 +56,6 @@ string [NUM_BINDED_KEYS] key_bindings =
|
|||
"messagemode",
|
||||
"screenshot",
|
||||
#define NUM_MISC_KEYS NUM_BASIC_KEYS + 4
|
||||
"impulse 0",
|
||||
"impulse 1",
|
||||
"impulse 2",
|
||||
"impulse 3",
|
||||
|
@ -36,11 +66,107 @@ string [NUM_BINDED_KEYS] key_bindings =
|
|||
"impulse 8"
|
||||
#define NUM_WEAPON_KEYS NUM_MISC_KEYS + 9
|
||||
};
|
||||
// this array holds readable information about the binded keys
|
||||
string [NUM_BINDED_KEYS] key_bindings_desc;
|
||||
|
||||
integer set_key_flag;
|
||||
integer set_key_flag; // holds flag for the key-setting
|
||||
|
||||
string (integer key, integer bindnum) make_key_desc =
|
||||
// three global hashes for the main binding groups
|
||||
integer basic_binding_hash;
|
||||
integer misc_binding_hash;
|
||||
integer weapon_binding_hash;
|
||||
|
||||
/*
|
||||
init_binding_hash
|
||||
|
||||
this function initializes the hashes for the binding menus
|
||||
*/
|
||||
void ()
|
||||
init_binding_hash =
|
||||
{
|
||||
/*
|
||||
DESIGN NOTES for the Menu:
|
||||
|
||||
binding config is loaded into hashes.
|
||||
the key of the hash is the string, which will
|
||||
be displayed as binding description.
|
||||
The first value of the key is the command, which
|
||||
is binded.
|
||||
The second valu (loaded later) of the hash
|
||||
will be the keyname. (see get_hash_keys())
|
||||
*/
|
||||
|
||||
// Basic keys
|
||||
basic_binding_hash = StringHash_Create();
|
||||
StringHash_Set(basic_binding_hash, "Attack", "+attack", 0);
|
||||
StringHash_Set(basic_binding_hash, "Next weapon", "impulse 10", 0);
|
||||
StringHash_Set(basic_binding_hash, "Jump/Swin up", "+jump", 0);
|
||||
StringHash_Set(basic_binding_hash, "Walk forward", "+forward", 0);
|
||||
StringHash_Set(basic_binding_hash, "Backpedal", "+back", 0);
|
||||
StringHash_Set(basic_binding_hash, "Turn left", "+left", 0);
|
||||
StringHash_Set(basic_binding_hash, "Turn right", "+right", 0);
|
||||
StringHash_Set(basic_binding_hash, "Run", "+speed", 0);
|
||||
StringHash_Set(basic_binding_hash, "Step left", "+moveleft", 0);
|
||||
StringHash_Set(basic_binding_hash, "Step right", "+moveright", 0);
|
||||
StringHash_Set(basic_binding_hash, "Sidestep", "+strafe", 0);
|
||||
StringHash_Set(basic_binding_hash, "Look up", "+lookup", 0);
|
||||
StringHash_Set(basic_binding_hash, "Look down", "+lookdown", 0);
|
||||
StringHash_Set(basic_binding_hash, "Center view", "centerview", 0);
|
||||
StringHash_Set(basic_binding_hash, "Mouse look", "+mlook", 0);
|
||||
StringHash_Set(basic_binding_hash, "Keyboard look", "+klook", 0);
|
||||
StringHash_Set(basic_binding_hash, "Swim up", "+moveup", 0);
|
||||
StringHash_Set(basic_binding_hash, "Swim down", "+movedown", 0);
|
||||
|
||||
// Misc keys
|
||||
misc_binding_hash = StringHash_Create();
|
||||
StringHash_Set(misc_binding_hash, "Pause game", "pause", 0);
|
||||
StringHash_Set(misc_binding_hash, "Tog. m.-grab", "toggle in_grab", 0);
|
||||
StringHash_Set(misc_binding_hash, "Messagemode", "messagemode", 0);
|
||||
StringHash_Set(misc_binding_hash, "Screenshot", "screenshot", 0);
|
||||
|
||||
// Weapon keys
|
||||
weapon_binding_hash = StringHash_Create();
|
||||
StringHash_Set(weapon_binding_hash, "Axe", "impulse 1", 0);
|
||||
StringHash_Set(weapon_binding_hash, "Shotgun", "impulse 2", 0);
|
||||
StringHash_Set(weapon_binding_hash, "Super Shotgun", "impulse 3", 0);
|
||||
StringHash_Set(weapon_binding_hash, "Nailgun", "impulse 4", 0);
|
||||
StringHash_Set(weapon_binding_hash, "Super Nailgun", "impulse 5", 0);
|
||||
StringHash_Set(weapon_binding_hash, "Rocket L. ", "impulse 6", 0);
|
||||
StringHash_Set(weapon_binding_hash, "Grenade L. ", "impulse 7", 0);
|
||||
StringHash_Set(weapon_binding_hash, "Thunderbolt", "impulse 8", 0);
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
get_keyname
|
||||
|
||||
Gets the string of the key, which is binded
|
||||
to a special binding.
|
||||
bindnum is the number of the binding.
|
||||
As a command/binding can be binded to many keys,
|
||||
you can get the second, third, etc. key by giving
|
||||
the bindnum.
|
||||
*/
|
||||
string (string binding, integer bindnum)
|
||||
get_keyname =
|
||||
{
|
||||
local integer keynum;
|
||||
local string keyname;
|
||||
|
||||
keynum = Key_LookupBinding(IMT_0, bindnum, binding);
|
||||
if(keynum == -1) {
|
||||
keyname = "";
|
||||
} else {
|
||||
keyname = Key_KeynumToString(keynum);
|
||||
// cut away the "K_", thats maybe enough as description for now
|
||||
keyname = String_Cut(0, 2, keyname);
|
||||
}
|
||||
return keyname;
|
||||
};
|
||||
|
||||
//XXX: make_key_desc will be removed soon
|
||||
string (integer key, integer bindnum)
|
||||
make_key_desc =
|
||||
{
|
||||
local integer keynum;
|
||||
local string keyname;
|
||||
|
@ -56,35 +182,79 @@ string (integer key, integer bindnum) make_key_desc =
|
|||
return keyname;
|
||||
};
|
||||
|
||||
void () load_keybindings =
|
||||
{
|
||||
local integer i;
|
||||
local string nddesc = "";
|
||||
/*
|
||||
get_hash_keys
|
||||
|
||||
for(i = 0;i < NUM_BINDED_KEYS; i++) {
|
||||
key_bindings_desc[i] = make_key_desc(i, 1);
|
||||
nddesc = make_key_desc(i, 2);
|
||||
if(nddesc != "") {
|
||||
key_bindings_desc[i] += ", " + nddesc;
|
||||
gets the keys for a keybinding-hash
|
||||
*/
|
||||
void (integer hash_id)
|
||||
get_hash_keys =
|
||||
{
|
||||
local integer i,hlen;
|
||||
local string binding, desc1 = "", desc2 = "";
|
||||
|
||||
hlen = StringHash_Length(hash_id);
|
||||
for(i = 0;i < hlen; i++) {
|
||||
binding = StringHash_GetIdx(hash_id, i, 0);
|
||||
desc1 = get_keyname(binding, 1); // first key binded to
|
||||
desc2 = get_keyname(binding, 2); // second key binded to
|
||||
|
||||
if(desc2 != "") {
|
||||
desc1 += ", " + desc2;
|
||||
}
|
||||
StringHash_SetIdx(hash_id, i, desc1, 1);
|
||||
}
|
||||
};
|
||||
|
||||
//**** BINDINGS OPTIONS
|
||||
/*
|
||||
load_keybindings
|
||||
|
||||
integer (string text, integer key) control_bind_f =
|
||||
Loads the kername for into the hashes
|
||||
*/
|
||||
void ()
|
||||
load_keybindings =
|
||||
{
|
||||
local string binding;
|
||||
local integer retval = 0, bindcnt = 0;
|
||||
get_hash_keys(basic_binding_hash);
|
||||
get_hash_keys(misc_binding_hash);
|
||||
get_hash_keys(weapon_binding_hash);
|
||||
};
|
||||
|
||||
binding = key_bindings[stoi(text)];
|
||||
|
||||
/*******************
|
||||
* BINDINGS OPTIONS
|
||||
* Binding settings
|
||||
*******************/
|
||||
|
||||
/*
|
||||
DESIGN NOTE (by elmex):
|
||||
|
||||
Every sub-menu for control bindings
|
||||
has its own hash for holding the keybindings.
|
||||
Thats why there are three different functions, which
|
||||
shadow the CB_MAIN_control_binding() function.
|
||||
They get the binding from the correct hash.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
CB_MAIN_control_binding
|
||||
|
||||
The core function of all control_binding function.
|
||||
Its taking the binding as argument.
|
||||
This function is called by the real callbacks.
|
||||
*/
|
||||
integer (string binding, integer key)
|
||||
CB_MAIN_control_binding =
|
||||
{
|
||||
local integer retval = 0, bindcnt = 0;
|
||||
|
||||
if(set_key_flag) {
|
||||
bindcnt = Key_CountBinding(IMT_0, binding);
|
||||
/* we are not binding keys for more than one command
|
||||
by the menu (maybe extended later) */
|
||||
if(bindcnt < 2) {
|
||||
Key_SetBinding (IMT_0, key, binding);
|
||||
}
|
||||
// else: not bind
|
||||
} // else: not bind the key
|
||||
|
||||
set_key_flag = 0;
|
||||
retval = 1;
|
||||
|
@ -104,133 +274,215 @@ integer (string text, integer key) control_bind_f =
|
|||
return retval;
|
||||
};
|
||||
|
||||
integer () basic_control_bind_draw =
|
||||
/*
|
||||
CB_basic_control_binding
|
||||
|
||||
Callback for the basic control bindings menu
|
||||
*/
|
||||
integer (string text, integer key)
|
||||
CB_basic_control_binding =
|
||||
{
|
||||
local string binding = StringHash_GetIdx(basic_binding_hash, stoi(text), 0);
|
||||
return CB_MAIN_control_binding(binding, key);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
DRAW_basic_control_binding
|
||||
|
||||
Draws the menu for the basic control bindins
|
||||
*/
|
||||
integer ()
|
||||
DRAW_basic_control_binding =
|
||||
{
|
||||
local integer cursor_pad = 40, bind_desc_pad;
|
||||
local integer i, hl;
|
||||
|
||||
bind_desc_pad = 120;
|
||||
|
||||
Draw_String (20, 10, "Backspace/Delete: Del binding");
|
||||
Draw_String (20, 20, "Enter: New binding");
|
||||
|
||||
draw_val_item (20, 40, bind_desc_pad, "Attack", key_bindings_desc[0]);
|
||||
draw_val_item (20, 50, bind_desc_pad, "Next weapon", key_bindings_desc[1]);
|
||||
draw_val_item (20, 60, bind_desc_pad, "Jump/Swin up", key_bindings_desc[2]);
|
||||
draw_val_item (20, 70, bind_desc_pad, "Walf forward", key_bindings_desc[3]);
|
||||
draw_val_item (20, 80, bind_desc_pad, "Backpedal", key_bindings_desc[4]);
|
||||
draw_val_item (20, 90, bind_desc_pad, "Turn left", key_bindings_desc[5]);
|
||||
draw_val_item (20, 100, bind_desc_pad, "Turn right", key_bindings_desc[6]);
|
||||
draw_val_item (20, 110, bind_desc_pad, "Run", key_bindings_desc[7]);
|
||||
draw_val_item (20, 120, bind_desc_pad, "Step left", key_bindings_desc[8]);
|
||||
draw_val_item (20, 130, bind_desc_pad, "Step right", key_bindings_desc[9]);
|
||||
draw_val_item (20, 140, bind_desc_pad, "Sidestep", key_bindings_desc[10]);
|
||||
draw_val_item (20, 150, bind_desc_pad, "Look up", key_bindings_desc[11]);
|
||||
draw_val_item (20, 160, bind_desc_pad, "Look down", key_bindings_desc[12]);
|
||||
draw_val_item (20, 170, bind_desc_pad, "Center view", key_bindings_desc[13]);
|
||||
draw_val_item (20, 180, bind_desc_pad, "Mouse look", key_bindings_desc[14]);
|
||||
draw_val_item (20, 190, bind_desc_pad, "Keyboard look", key_bindings_desc[15]);
|
||||
draw_val_item (20, 200, bind_desc_pad, "Swim up", key_bindings_desc[16]);
|
||||
draw_val_item (20, 210, bind_desc_pad, "Swim down", key_bindings_desc[17]);
|
||||
|
||||
hl = StringHash_Length(basic_binding_hash);
|
||||
for(i=0;i < hl; i++) {
|
||||
draw_val_item (20, 40+(i*10), bind_desc_pad,
|
||||
StringHash_GetIdx(basic_binding_hash, i, -1),
|
||||
StringHash_GetIdx(basic_binding_hash, i, 1));
|
||||
}
|
||||
|
||||
opt_cursor (12, (Menu_GetIndex() * 10) + cursor_pad);
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
integer () misc_control_bind_draw =
|
||||
/*
|
||||
MENU_basic_control_binding
|
||||
|
||||
Menu making function for the control bindings
|
||||
*/
|
||||
void ()
|
||||
MENU_basic_control_binding =
|
||||
{
|
||||
local integer cursor_pad = 40, bind_desc_pad;
|
||||
|
||||
bind_desc_pad = 120;
|
||||
|
||||
Draw_String (20, 10, "Backspace/Delete: Del binding");
|
||||
Draw_String (20, 20, "Enter: New binding");
|
||||
|
||||
draw_val_item (20, 40, bind_desc_pad, "Pause game", key_bindings_desc[NUM_BASIC_KEYS]);
|
||||
draw_val_item (20, 50, bind_desc_pad, "Tog. m.-grab", key_bindings_desc[NUM_BASIC_KEYS + 1]);
|
||||
draw_val_item (20, 60, bind_desc_pad, "Messagemode", key_bindings_desc[NUM_BASIC_KEYS + 2]);
|
||||
draw_val_item (20, 70, bind_desc_pad, "Screenshot", key_bindings_desc[NUM_BASIC_KEYS + 3]);
|
||||
|
||||
opt_cursor (12, (Menu_GetIndex() * 10) + cursor_pad);
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
integer () weapon_control_bind_draw =
|
||||
{
|
||||
local integer cursor_pad = 40, bind_desc_pad;
|
||||
|
||||
bind_desc_pad = 120;
|
||||
|
||||
Draw_String (20, 10, "Backspace/Delete: Del binding");
|
||||
Draw_String (20, 20, "Enter: New binding");
|
||||
|
||||
draw_val_item (20, 40, bind_desc_pad, "Axe", key_bindings_desc[NUM_MISC_KEYS]);
|
||||
draw_val_item (20, 50, bind_desc_pad, "Shotgun", key_bindings_desc[NUM_MISC_KEYS + 1]);
|
||||
draw_val_item (20, 60, bind_desc_pad, "Super Shotgun", key_bindings_desc[NUM_MISC_KEYS + 2]);
|
||||
draw_val_item (20, 70, bind_desc_pad, "Nailgun", key_bindings_desc[NUM_MISC_KEYS + 3]);
|
||||
draw_val_item (20, 80, bind_desc_pad, "Super Nailgun", key_bindings_desc[NUM_MISC_KEYS + 4]);
|
||||
draw_val_item (20, 90, bind_desc_pad, "Rocket L. ", key_bindings_desc[NUM_MISC_KEYS + 5]);
|
||||
draw_val_item (20, 100, bind_desc_pad, "Grenade L. ", key_bindings_desc[NUM_MISC_KEYS + 6]);
|
||||
draw_val_item (20, 110, bind_desc_pad, "Thunderbolt", key_bindings_desc[NUM_MISC_KEYS + 7]);
|
||||
|
||||
opt_cursor (12, (Menu_GetIndex() * 10) + cursor_pad);
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
|
||||
void () basic_control_bind_menu =
|
||||
{
|
||||
local integer i;
|
||||
local integer i,hl;
|
||||
|
||||
Menu_Begin (54, 40, "Basic bindings");
|
||||
Menu_FadeScreen (1);
|
||||
Menu_Draw (basic_control_bind_draw);
|
||||
Menu_Draw (DRAW_basic_control_binding);
|
||||
|
||||
for (i = 0; i < NUM_BASIC_KEYS; i++) {
|
||||
Menu_Item (20, 40 + i*10, itos(i), control_bind_f, 1);
|
||||
hl = StringHash_Length(basic_binding_hash);
|
||||
for (i = 0; i < hl; i++) {
|
||||
Menu_Item (20, 40 + i*10, itos(i), CB_basic_control_binding, 1);
|
||||
}
|
||||
Menu_End ();
|
||||
};
|
||||
|
||||
void () misc_control_bind_menu =
|
||||
|
||||
/*
|
||||
CB_misc_control_binding
|
||||
|
||||
Callback for misc control bindings.
|
||||
*/
|
||||
integer (string text, integer key)
|
||||
CB_misc_control_binding =
|
||||
{
|
||||
local integer i;
|
||||
local string binding = StringHash_GetIdx(misc_binding_hash, stoi(text), 0);
|
||||
return CB_MAIN_control_binding(binding, key);
|
||||
};
|
||||
|
||||
/*
|
||||
DRAW_misc_control_binding
|
||||
|
||||
Draw the bindings for the misc controls
|
||||
*/
|
||||
integer ()
|
||||
DRAW_misc_control_binding =
|
||||
{
|
||||
local integer cursor_pad = 40, bind_desc_pad;
|
||||
local integer i, hl;
|
||||
|
||||
bind_desc_pad = 120;
|
||||
|
||||
Draw_String (20, 10, "Backspace/Delete: Del binding");
|
||||
Draw_String (20, 20, "Enter: New binding");
|
||||
|
||||
hl = StringHash_Length(misc_binding_hash);
|
||||
for(i=0;i < hl; i++) {
|
||||
draw_val_item (20, 40+(i*10), bind_desc_pad,
|
||||
StringHash_GetIdx(misc_binding_hash, i, -1),
|
||||
StringHash_GetIdx(misc_binding_hash, i, 1));
|
||||
}
|
||||
|
||||
opt_cursor (12, (Menu_GetIndex() * 10) + cursor_pad);
|
||||
return 1;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
MENU_misc_control_binding
|
||||
|
||||
Menu maker function for the misc control binding
|
||||
*/
|
||||
void ()
|
||||
MENU_misc_control_binding =
|
||||
{
|
||||
local integer i,hl;
|
||||
|
||||
Menu_Begin (54, 50, "Misc bindings");
|
||||
Menu_FadeScreen (1);
|
||||
Menu_Draw (misc_control_bind_draw);
|
||||
Menu_Draw (DRAW_misc_control_binding);
|
||||
|
||||
for (i = NUM_BASIC_KEYS; i < NUM_MISC_KEYS; i++) {
|
||||
Menu_Item (20, 40 + i*10, itos(i), control_bind_f, 1);
|
||||
hl = StringHash_Length(basic_binding_hash);
|
||||
for (i = 0; i < hl; i++) {
|
||||
Menu_Item (20, 40 + i*10, itos(i), CB_misc_control_binding, 1);
|
||||
}
|
||||
Menu_End ();
|
||||
};
|
||||
|
||||
void () weapon_control_bind_menu =
|
||||
/*
|
||||
CB_weapon_control_binding
|
||||
|
||||
Callback function for the weapons control bindings
|
||||
*/
|
||||
integer (string text, integer key)
|
||||
CB_weapon_control_binding =
|
||||
{
|
||||
local integer i;
|
||||
local string binding = StringHash_GetIdx(weapon_binding_hash, stoi(text),0);
|
||||
return CB_MAIN_control_binding(binding, key);
|
||||
};
|
||||
|
||||
/*
|
||||
DRAW_weapon_control_binding
|
||||
|
||||
Draw the weapon binding menu
|
||||
*/
|
||||
integer ()
|
||||
DRAW_weapon_control_binding =
|
||||
{
|
||||
local integer cursor_pad = 40, bind_desc_pad;
|
||||
local integer i,hl;
|
||||
|
||||
bind_desc_pad = 120;
|
||||
|
||||
Draw_String (20, 10, "Backspace/Delete: Del binding");
|
||||
Draw_String (20, 20, "Enter: New binding");
|
||||
|
||||
hl = StringHash_Length(weapon_binding_hash);
|
||||
for(i=0;i < hl; i++) {
|
||||
draw_val_item (20, 40+(i*10), bind_desc_pad,
|
||||
StringHash_GetIdx(weapon_binding_hash, i, -1),
|
||||
StringHash_GetIdx(weapon_binding_hash, i, 1));
|
||||
}
|
||||
|
||||
opt_cursor (12, (Menu_GetIndex() * 10) + cursor_pad);
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
MENU_weapon_control_binding
|
||||
|
||||
Menu maker for the weapons menu
|
||||
*/
|
||||
void ()
|
||||
MENU_weapon_control_binding =
|
||||
{
|
||||
local integer i,hl;
|
||||
|
||||
Menu_Begin (54, 60, "Weapon bindings");
|
||||
Menu_FadeScreen (1);
|
||||
Menu_Draw (weapon_control_bind_draw);
|
||||
Menu_Draw (DRAW_weapon_control_binding);
|
||||
|
||||
for (i = NUM_MISC_KEYS; i < NUM_WEAPON_KEYS; i++) {
|
||||
Menu_Item (20, 40 + i*10, itos(i), control_bind_f, 1);
|
||||
hl = StringHash_Length(basic_binding_hash);
|
||||
for (i = 0; i < hl; i++) {
|
||||
Menu_Item (20, 40 + i*10, itos(i), CB_weapon_control_binding, 1);
|
||||
}
|
||||
Menu_End ();
|
||||
};
|
||||
|
||||
void () control_bind_menu =
|
||||
/*
|
||||
MENU_control_binding
|
||||
|
||||
Main controls menu, for selecting the sub control menus
|
||||
*/
|
||||
void ()
|
||||
MENU_control_binding =
|
||||
{
|
||||
init_binding_hash (); // init the keybinding hashes
|
||||
load_keybindings (); // load the keybindings into hashes
|
||||
|
||||
Menu_Begin (54, 60, "Bindings");
|
||||
Menu_Pic (16, 4, "gfx/qplaque.lmp");
|
||||
Menu_CenterPic (160, 4, "gfx/p_option.lmp");
|
||||
|
||||
Menu_FadeScreen (1);
|
||||
basic_control_bind_menu ();
|
||||
misc_control_bind_menu ();
|
||||
weapon_control_bind_menu ();
|
||||
|
||||
MENU_basic_control_binding ();
|
||||
MENU_misc_control_binding ();
|
||||
MENU_weapon_control_binding();
|
||||
|
||||
Menu_End ();
|
||||
};
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ menu.dat
|
|||
@srcdir@/key_defs.qc
|
||||
@srcdir@/menu_def.qc
|
||||
@srcdir@/string_def.qc
|
||||
@srcdir@/stringh_def.qc
|
||||
|
||||
@srcdir@/options_util.qc
|
||||
|
||||
|
|
|
@ -180,7 +180,8 @@ CB_audio_options =
|
|||
local float volume;
|
||||
|
||||
volume = cvar("volume");
|
||||
volume = min_max_cnt(MIN_VOLUME, MAX_VOLUME, VOLUME_STEP, volume, (key == QFK_RIGHT) && (key != QFK_LEFT));
|
||||
volume = min_max_cnt(MIN_VOLUME, MAX_VOLUME, VOLUME_STEP, volume,
|
||||
(key == QFK_RIGHT) && (key != QFK_LEFT));
|
||||
cvar_set("volume", ftos(volume));
|
||||
|
||||
return 0;
|
||||
|
@ -247,8 +248,6 @@ CB_control_options =
|
|||
{
|
||||
local float val;
|
||||
|
||||
load_keybindings();
|
||||
|
||||
switch (text) {
|
||||
case "in_grab":
|
||||
Cbuf_AddText ("toggle in_grab\n");
|
||||
|
@ -344,8 +343,11 @@ MENU_control_options =
|
|||
Menu_Begin (54, 40, "Controls");
|
||||
Menu_FadeScreen (1);
|
||||
Menu_CenterPic (160, 4, "gfx/p_option.lmp");
|
||||
|
||||
Menu_Draw (DRAW_control_options);
|
||||
control_bind_menu ();
|
||||
|
||||
MENU_control_binding ();
|
||||
|
||||
Menu_Item (54, 70, "in_grab", CB_control_options, 0);
|
||||
Menu_Item (54, 80, "autorun", CB_control_options, 0);
|
||||
Menu_Item (54, 90, "m_pitch", CB_control_options, 0);
|
||||
|
@ -353,6 +355,7 @@ MENU_control_options =
|
|||
Menu_Item (54, 110, "freelook", CB_control_options, 0);
|
||||
Menu_Item (54, 120, "lookspring", CB_control_options, 0);
|
||||
Menu_Item (54, 130, "lookstrafe", CB_control_options, 0);
|
||||
|
||||
Menu_End ();
|
||||
};
|
||||
|
||||
|
@ -435,7 +438,6 @@ integer (integer key, integer unicode, integer down)
|
|||
CB_options =
|
||||
{
|
||||
// pre-loading of the bindings and set_key_flag == 0
|
||||
load_keybindings();
|
||||
set_key_flag = 0;
|
||||
|
||||
return 0;
|
||||
|
@ -457,6 +459,7 @@ MENU_options =
|
|||
Menu_Pic (16, 4, "gfx/qplaque.lmp");
|
||||
Menu_CenterPic (160, 4, "gfx/p_option.lmp");
|
||||
Menu_KeyEvent (CB_options);
|
||||
|
||||
MENU_control_options ();
|
||||
MENU_video_options ();
|
||||
MENU_audio_options ();
|
||||
|
|
|
@ -12,7 +12,6 @@ CB_ Input callback
|
|||
MENU_ Menu making function
|
||||
DRAW_ Draw
|
||||
|
||||
|
||||
Comment functions like this:
|
||||
/*
|
||||
[PREFIX_]FunctionName
|
||||
|
|
|
@ -40,6 +40,7 @@ void Cbuf_Progs_Init (struct progs_s *pr);
|
|||
void File_Progs_Init (struct progs_s *pr);
|
||||
void InputLine_Progs_Init (struct progs_s *pr);
|
||||
void String_Progs_Init (struct progs_s *pr);
|
||||
void StringHash_Progs_Init (struct progs_s *pr);
|
||||
void Cvar_Progs_Init (struct progs_s *pr);
|
||||
void Key_Progs_Init (struct progs_s *pr);
|
||||
|
||||
|
|
|
@ -361,6 +361,7 @@ Menu_Init (void)
|
|||
File_Progs_Init (&menu_pr_state);
|
||||
InputLine_Progs_Init (&menu_pr_state);
|
||||
String_Progs_Init (&menu_pr_state);
|
||||
StringHash_Progs_Init (&menu_pr_state);
|
||||
Cvar_Progs_Init (&menu_pr_state);
|
||||
Key_Progs_Init (&menu_pr_state);
|
||||
PR_Cmds_Init (&menu_pr_state);
|
||||
|
|
|
@ -8,4 +8,4 @@ libQFgamecode_builtins_la_LDFLAGS= -version-info 1:0:0
|
|||
libQFgamecode_builtins_la_SOURCES= pr_cmds.c
|
||||
|
||||
libQFcsqc_la_LDFLAGS= -version-info 1:0:0
|
||||
libQFcsqc_la_SOURCES= bi_init.c bi_cbuf.c bi_file.c bi_inputline.c bi_string.c bi_cvar.c bi_keys.c
|
||||
libQFcsqc_la_SOURCES= bi_init.c bi_cbuf.c bi_file.c bi_inputline.c bi_string.c bi_cvar.c bi_keys.c bi_strhash.c
|
||||
|
|
|
@ -37,6 +37,7 @@ static void (*const cbuf_progs_init)(struct progs_s *) = Cbuf_Progs_Init;
|
|||
static void (*const file_progs_init)(struct progs_s *) = File_Progs_Init;
|
||||
static void (*const inputline_progs_init)(struct progs_s *) = InputLine_Progs_Init;
|
||||
static void (*const string_progs_init)(struct progs_s *) = String_Progs_Init;
|
||||
static void (*const stringhashe_progs_init)(struct progs_s *) = StringHash_Progs_Init;
|
||||
static void (*const cvar_progs_init)(struct progs_s *) = Cvar_Progs_Init;
|
||||
static void (*const key_progs_init)(struct progs_s *) = Key_Progs_Init;
|
||||
|
||||
|
|
440
libs/gamecode/builtins/bi_strhash.c
Normal file
440
libs/gamecode/builtins/bi_strhash.c
Normal file
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
bi_inputline.c
|
||||
|
||||
CSQC string hashes builtins
|
||||
|
||||
Copyright (C) 2002 Robin Redeker <elmex@x-paste.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
static const char rcsid[] =
|
||||
"$Id$";
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include "QF/console.h"
|
||||
#include "QF/draw.h"
|
||||
#include "QF/progs.h"
|
||||
#include "QF/zone.h"
|
||||
|
||||
#define MAX_SH_VALUES 16
|
||||
|
||||
/*
|
||||
DESIGN NOTE (by elmex):
|
||||
|
||||
This file contains code for QuakeC hashes.
|
||||
The hases are designed to have around 10 sub-values for
|
||||
one key.
|
||||
Hashes are stored as hash-ids in the QC-environment.
|
||||
|
||||
The key->value pairs in the hashes are stored in the
|
||||
order they are set. So its easy to make array-hashes.
|
||||
The function which gets the Lenght of a hash is specialy
|
||||
made for array purposes.
|
||||
|
||||
TODO: Check the FIXME's below in the code.
|
||||
(please taniwha ;)
|
||||
(about memory leaks)
|
||||
*/
|
||||
|
||||
// a hash element
|
||||
typedef struct {
|
||||
const char * key;
|
||||
const char * values[MAX_SH_VALUES];
|
||||
} str_hash_elem;
|
||||
|
||||
// a structure of a hash
|
||||
typedef struct {
|
||||
str_hash_elem **elements;
|
||||
int cnt_elements;
|
||||
} str_hash;
|
||||
|
||||
// a list structure of hashes
|
||||
typedef struct {
|
||||
str_hash **hashes;
|
||||
int cnt_hashes;
|
||||
} strh_resources_t;
|
||||
|
||||
/*
|
||||
bi_StringHash_Create
|
||||
|
||||
Creates a hash structure and gives back the hash-id to the
|
||||
QC-environment
|
||||
*/
|
||||
static void
|
||||
bi_StringHash_Create (progs_t *pr)
|
||||
{
|
||||
strh_resources_t *res = PR_Resources_Find (pr, "StringHash");
|
||||
int i;
|
||||
int hash_id=-1;
|
||||
|
||||
// check if there is a empty hash in the array
|
||||
for(i = 0; i < res->cnt_hashes; i++) {
|
||||
if(res->hashes[i]->cnt_elements == 0) {
|
||||
// we found a empty already allocated hash
|
||||
hash_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(hash_id == -1) {
|
||||
/* allocate a new hash struct, if
|
||||
there is no old one */
|
||||
if(res->hashes == NULL) { // allocate a new list of hashes
|
||||
res->hashes = (str_hash **) malloc(
|
||||
sizeof(str_hash*) * (res->cnt_hashes + 1));
|
||||
} else { // reallocate the list of hashes
|
||||
res->hashes = (str_hash **) realloc(res->hashes,
|
||||
sizeof(str_hash*) * (res->cnt_hashes + 1));
|
||||
}
|
||||
hash_id = res->cnt_hashes;
|
||||
|
||||
res->hashes[hash_id] =
|
||||
(str_hash*) malloc(sizeof(str_hash));
|
||||
// dont forge to clean the hash
|
||||
memset(res->hashes[hash_id],0,sizeof(str_hash));
|
||||
|
||||
res->cnt_hashes++; // increase cnt of allocated hashes
|
||||
}
|
||||
G_INT (pr, OFS_RETURN) = hash_id;
|
||||
}
|
||||
|
||||
/*
|
||||
bi_StringHash_Destroy
|
||||
|
||||
Destroys a hash
|
||||
*/
|
||||
static void
|
||||
bi_StringHash_Destroy (progs_t *pr)
|
||||
{
|
||||
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
|
||||
int hash_id = G_INT (pr, OFS_PARM0);
|
||||
str_hash *sh = NULL;
|
||||
int i;
|
||||
|
||||
if(hash_id >= res->cnt_hashes || hash_id < 0) {
|
||||
G_INT (pr, OFS_RETURN) = 0;
|
||||
return;
|
||||
}
|
||||
sh = res->hashes[hash_id];
|
||||
|
||||
/* we dont really destroy the allocated hash,
|
||||
but we free the elements of the hash
|
||||
and mark it for reuseing */
|
||||
|
||||
for(i = 0; i < sh->cnt_elements; i++) {
|
||||
if(sh->elements[i] != NULL) {
|
||||
/* they should never be NULL,
|
||||
buy, who knows? */
|
||||
PR_Error(pr, "NULL hash-element found -> not supposed!");
|
||||
} else {
|
||||
free(sh->elements[i]); // free str_hash_elem structs
|
||||
}
|
||||
/*
|
||||
FIXME: HELP: taniwha??? do i have to FREE
|
||||
the strings ??
|
||||
*/
|
||||
}
|
||||
free(sh->elements); // free the list pointer
|
||||
sh->elements = NULL;
|
||||
sh->cnt_elements = 0;
|
||||
G_INT (pr, OFS_RETURN) = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
bi_StringHash_Set
|
||||
|
||||
Sets a the key-value (with a special id) in a hash
|
||||
to a value.
|
||||
If a non existing key is given a element if generated for it.
|
||||
FIXME: look if this functions does mem-leak
|
||||
*/
|
||||
static void
|
||||
bi_StringHash_Set (progs_t *pr)
|
||||
{
|
||||
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
|
||||
int hash_id = G_INT (pr, OFS_PARM0);
|
||||
const char *key = G_STRING (pr, OFS_PARM1);
|
||||
const char *val = G_STRING (pr, OFS_PARM2);
|
||||
int val_id = G_INT (pr, OFS_PARM3);
|
||||
str_hash *sh = NULL;
|
||||
int i,found_fl=0;
|
||||
|
||||
// validate the hash ID
|
||||
if(res->hashes == NULL ||
|
||||
(hash_id >= res->cnt_hashes || hash_id < 0) ||
|
||||
(val_id < 0 || val_id >= MAX_SH_VALUES))
|
||||
{
|
||||
G_INT (pr, OFS_RETURN) = 0;
|
||||
return;
|
||||
}
|
||||
sh = res->hashes[hash_id];
|
||||
|
||||
// first search for existing key
|
||||
for(i = 0; i < sh->cnt_elements; i++) {
|
||||
if(strcmp(sh->elements[i]->key, key) == 0) {
|
||||
// found already a element with that key
|
||||
if(sh->elements[i]->values[val_id] == NULL) { // empty val
|
||||
sh->elements[i]->values[val_id] = val;
|
||||
} else {
|
||||
/* FIXME: taniwha, do i have to free it?
|
||||
* this is a potential memory leak, maybe, bu
|
||||
* i dont know how Hunk_Alloc* stuff works
|
||||
* ITS NOT DOCUMENTED!
|
||||
*/
|
||||
sh->elements[i]->values[val_id] = val;
|
||||
}
|
||||
found_fl = 1;
|
||||
}
|
||||
}
|
||||
if(!found_fl) { // add a new element
|
||||
if(sh->elements == NULL) { // alloc new elements list pointer
|
||||
sh->elements = (str_hash_elem**) malloc(sizeof(str_hash_elem*));
|
||||
sh->cnt_elements = 0; // 0 because usage as index here
|
||||
} else {
|
||||
sh->elements = (str_hash_elem**) realloc(sh->elements,
|
||||
sizeof(str_hash_elem*) * (sh->cnt_elements+1));
|
||||
}
|
||||
sh->elements[sh->cnt_elements] = malloc(sizeof(str_hash_elem));
|
||||
memset(sh->elements[sh->cnt_elements],0,sizeof(str_hash_elem));
|
||||
|
||||
sh->elements[sh->cnt_elements]->key = key;
|
||||
sh->elements[sh->cnt_elements]->values[val_id] = val;
|
||||
|
||||
sh->cnt_elements++;
|
||||
}
|
||||
G_INT (pr, OFS_RETURN) = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
bi_StringHash_SetIdx
|
||||
|
||||
Sets a the key-value (with a special id) in a hash
|
||||
to a value. This function works by index of the element.
|
||||
A element in hash is NOT generated automatically.
|
||||
FIXME: look if this functions does mem-leak
|
||||
*/
|
||||
static void
|
||||
bi_StringHash_SetIdx (progs_t *pr)
|
||||
{
|
||||
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
|
||||
int hash_id = G_INT (pr, OFS_PARM0);
|
||||
int idx = G_INT (pr, OFS_PARM1);
|
||||
const char *val = G_STRING (pr, OFS_PARM2);
|
||||
int val_id = G_INT (pr, OFS_PARM3);
|
||||
str_hash *sh = NULL;
|
||||
|
||||
// validate the hash ID
|
||||
if(res->hashes == NULL ||
|
||||
(hash_id >= res->cnt_hashes || hash_id < 0) ||
|
||||
(val_id < 0 || val_id >= MAX_SH_VALUES))
|
||||
{
|
||||
G_INT (pr, OFS_RETURN) = 0;
|
||||
return;
|
||||
}
|
||||
sh = res->hashes[hash_id];
|
||||
|
||||
if(idx < 0 || idx >= sh->cnt_elements || sh->elements[idx] == NULL) {
|
||||
if(sh->elements[idx] == NULL)
|
||||
PR_Error(pr, "NULL hash-element found -> not supposed!");
|
||||
|
||||
G_INT (pr, OFS_RETURN) = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
sh->elements[idx]->values[val_id] = val;
|
||||
|
||||
G_INT (pr, OFS_RETURN) = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
bi_StringHash_Get
|
||||
|
||||
Gets the value of a key and its id in a hash
|
||||
*/
|
||||
static void
|
||||
bi_StringHash_Get (progs_t *pr)
|
||||
{
|
||||
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
|
||||
int hash_id = G_INT (pr, OFS_PARM0);
|
||||
const char *key = G_STRING (pr, OFS_PARM1);
|
||||
int val_id = G_INT (pr, OFS_PARM2);
|
||||
str_hash *sh = NULL;
|
||||
int i,found_fl=0;
|
||||
const char *retstr = NULL;
|
||||
|
||||
// validate the hash ID
|
||||
if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0 ||
|
||||
val_id >= MAX_SH_VALUES)
|
||||
{
|
||||
retstr = "";
|
||||
RETURN_STRING(pr, retstr);
|
||||
return;
|
||||
}
|
||||
sh = res->hashes[hash_id];
|
||||
|
||||
// first search for existing key
|
||||
for(i = 0; i < sh->cnt_elements; i++) {
|
||||
if(strcmp(sh->elements[i]->key, key) == 0) {
|
||||
if(sh->elements[i]->values[val_id] != NULL) {
|
||||
retstr = sh->elements[i]->values[val_id];
|
||||
} else {
|
||||
retstr = "";
|
||||
}
|
||||
found_fl = 1;
|
||||
}
|
||||
}
|
||||
if(!found_fl) {
|
||||
retstr = "";
|
||||
}
|
||||
RETURN_STRING(pr, retstr);
|
||||
}
|
||||
|
||||
/*
|
||||
bi_StringHash_Length
|
||||
|
||||
Gets the count of the elements in a hash
|
||||
*/
|
||||
static void
|
||||
bi_StringHash_Length (progs_t *pr)
|
||||
{
|
||||
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
|
||||
int hash_id = G_INT (pr, OFS_PARM0);
|
||||
str_hash *sh = NULL;
|
||||
|
||||
// validate the hash ID
|
||||
if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0) {
|
||||
G_INT (pr, OFS_RETURN) = 0;
|
||||
return;
|
||||
}
|
||||
sh = res->hashes[hash_id];
|
||||
|
||||
G_INT (pr, OFS_RETURN) = sh->cnt_elements;
|
||||
}
|
||||
|
||||
/*
|
||||
bi_StringHash_GetIdx
|
||||
|
||||
Gets a hash elment by its index
|
||||
special: if the val_id is -1 the key of the element will
|
||||
be returned
|
||||
*/
|
||||
static void
|
||||
bi_StringHash_GetIdx (progs_t *pr)
|
||||
{
|
||||
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
|
||||
int hash_id = G_INT (pr, OFS_PARM0);
|
||||
int idx = G_INT (pr, OFS_PARM1);
|
||||
int val_id = G_INT (pr, OFS_PARM2);
|
||||
str_hash *sh = NULL;
|
||||
const char *retstr = NULL;
|
||||
|
||||
// validate the hash ID
|
||||
if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0) {
|
||||
retstr = NULL;
|
||||
}
|
||||
sh = res->hashes[hash_id];
|
||||
|
||||
if(idx < 0 || idx >= sh->cnt_elements ||
|
||||
(val_id < -1 || val_id >= MAX_SH_VALUES))
|
||||
{
|
||||
retstr = NULL;
|
||||
} else {
|
||||
if(val_id == -1) {
|
||||
retstr = sh->elements[idx]->key;
|
||||
} else {
|
||||
retstr = sh->elements[idx]->values[val_id];
|
||||
}
|
||||
}
|
||||
if(retstr == NULL) { retstr = ""; }
|
||||
|
||||
RETURN_STRING(pr, retstr);
|
||||
}
|
||||
|
||||
/*
|
||||
bi_strh_clear
|
||||
|
||||
Free the dynamic allocated memory
|
||||
XXX: taniwha: i dont know what to free
|
||||
exactly, could you validate this code?
|
||||
*/
|
||||
static void
|
||||
bi_strh_clear (progs_t *pr, void *data)
|
||||
{
|
||||
strh_resources_t *res = (strh_resources_t *)data;
|
||||
int i,d;
|
||||
|
||||
for (i = 0; i < res->cnt_hashes; i++) {
|
||||
if (res->hashes[i]) {
|
||||
for(d = 0; d < res->hashes[i]->cnt_elements; d++) {
|
||||
free(res->hashes[i]->elements[d]);
|
||||
}
|
||||
free(res->hashes[i]->elements);
|
||||
free(res->hashes[i]);
|
||||
res->hashes[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
StringHash_Progs_Init
|
||||
|
||||
Inits the Progs-system with StringHash resources and
|
||||
functions.
|
||||
*/
|
||||
void
|
||||
StringHash_Progs_Init (progs_t *pr)
|
||||
{
|
||||
strh_resources_t *res = malloc (sizeof (strh_resources_t));
|
||||
res->cnt_hashes = 0;
|
||||
res->hashes = NULL;
|
||||
|
||||
PR_Resources_Register (pr, "StringHash", res, bi_strh_clear);
|
||||
PR_AddBuiltin (pr, "StringHash_Create", bi_StringHash_Create, -1);
|
||||
PR_AddBuiltin (pr, "StringHash_Destroy", bi_StringHash_Destroy, -1);
|
||||
PR_AddBuiltin (pr, "StringHash_Set", bi_StringHash_Set, -1);
|
||||
PR_AddBuiltin (pr, "StringHash_Get", bi_StringHash_Get, -1);
|
||||
PR_AddBuiltin (pr, "StringHash_Length", bi_StringHash_Length, -1);
|
||||
PR_AddBuiltin (pr, "StringHash_SetIdx", bi_StringHash_SetIdx, -1);
|
||||
PR_AddBuiltin (pr, "StringHash_GetIdx", bi_StringHash_GetIdx, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
XXX NOTE by elmex:
|
||||
A file, decripted like this is what
|
||||
i want to see everywhere in qf-cvs =)
|
||||
No excuse for undocumented code and design without
|
||||
a reason for it.
|
||||
We/I want to know why something was designed how it is.
|
||||
*/
|
Loading…
Reference in a new issue