// player defaults
_cl_color "112.211" // same effect as 112, but menuqc can detect this as the default and not intentionally set
_cl_name ""
+ seta _cl_gender 0 "storage cvar for current player gender (0 = undisclosed, 1 = male, 2 = female)"
_cl_playermodel models/player/erebus.iqm
_cl_playerskin 0
// use default physics
set sv_friction_on_land 0
+set sv_friction_slick 0.5
set sv_player_viewoffset "0 0 35" "view offset of the player model"
set sv_player_mins "-16 -16 -24" "playermodel mins"
r_labelsprites_scale 0.40625 // labels sprites get displayed at 0.5x from 640x480 to 1280x1024, and at 1x from 1600x1200 onwards
- exec binds-default.cfg
+ exec binds-xonotic.cfg
// we must change its default from 1.0 to 1 to be consistent with menuqc
set slowmo 1
- seta menu_skin "luminos"
+ seta menu_skin "luma"
set menu_slowmo 1
seta menu_sounds 0 "enables menu sound effects. 1 enables click sounds, 2 also enables hover sounds"
seta menu_tooltips 1 "menu tooltips: 0 disabled, 1 enabled, 2 also shows cvar or console command (when available) changed or executed by the item"
exec _hud_descriptions.cfg
// exec the default skin config
// please add any new cvars into the hud_save script in qcsrc/client/hud_config.qc for consistency
- exec hud_luminos.cfg
+ exec hud_luma.cfg
// ... and now that everything is configured/aliased, we can do some things:
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+ #include "../dpdefs/progsdefs.qh"
+ #include "../dpdefs/dpextensions.qh"
+ #include "constants.qh"
+ #include "util.qh"
+ #include "urllib.qh"
+ #include "weapons/weapons.qh"
+ #include "../server/weapons/accuracy.qh"
+ #include "../server/defs.qh"
+ #include "playerstats.qh"
+ #include "../server/scores.qh"
+#endif
+
#ifdef SVQC
void PlayerStats_Prematch(void)
{
void PlayerStats_GameReport_Accuracy(entity p)
{
- entity w;
- float i;
+ int i;
for(i = WEP_FIRST; i <= WEP_LAST; ++i)
{
- w = get_weaponinfo(i);
+ entity w = get_weaponinfo(i);
#define ACCMAC(suffix,field) \
PS_GR_P_ADDVAL(p, sprintf("acc-%s-%s", w.netname, suffix), p.accuracy.(field[i-1]));
if(autocvar_g_playerstats_gamereport_uri != "")
{
- PlayerStats_GameReport_DelayMapVote = TRUE;
+ PlayerStats_GameReport_DelayMapVote = true;
url_multi_fopen(
autocvar_g_playerstats_gamereport_uri,
FILE_APPEND,
}
else
{
- PlayerStats_GameReport_DelayMapVote = FALSE;
+ PlayerStats_GameReport_DelayMapVote = false;
db_close(PS_GR_OUT_DB);
PS_GR_OUT_DB = -1;
}
if(PS_GR_OUT_DB >= 0)
{
- PlayerStats_GameReport_DelayMapVote = TRUE;
+ PlayerStats_GameReport_DelayMapVote = true;
serverflags |= SERVERFLAG_PLAYERSTATS;
PlayerStats_GameReport_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD);
PlayerStats_GameReport_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM);
}
- else { PlayerStats_GameReport_DelayMapVote = FALSE; }
+ else { PlayerStats_GameReport_DelayMapVote = false; }
}
void PlayerStats_GameReport_Handler(entity fh, entity pass, float status)
{
// url_fclose has finished
dprint("Player stats written\n");
- PlayerStats_GameReport_DelayMapVote = FALSE;
+ PlayerStats_GameReport_DelayMapVote = false;
if(PS_GR_OUT_DB >= 0)
{
db_close(PS_GR_OUT_DB);
default:
{
print("Player stats writing failed: ", ftos(status), "\n");
- PlayerStats_GameReport_DelayMapVote = FALSE;
+ PlayerStats_GameReport_DelayMapVote = false;
if(PS_GR_OUT_DB >= 0)
{
db_close(PS_GR_OUT_DB);
#endif
url_fputs(fh, sprintf("l %s\n", cvar_string("_menu_prvm_language"))); // language
url_fputs(fh, sprintf("c %s\n", cvar_string("_menu_prvm_country"))); // country
- url_fputs(fh, sprintf("g %s\n", cvar_string("_menu_prvm_gender"))); // gender
+ url_fputs(fh, sprintf("g %s\n", cvar_string("_cl_gender"))); // gender
url_fputs(fh, sprintf("n %s\n", cvar_string("_cl_name"))); // name
url_fputs(fh, sprintf("m %s %s\n", cvar_string("_cl_playermodel"), cvar_string("_cl_playerskin"))); // model/skin
*/url_fputs(fh, "\n");
#ifdef MENUQC
url_fputs(fh, sprintf("l %s\n", cvar_string("_menu_prvm_language"))); // language
url_fputs(fh, sprintf("c %s\n", cvar_string("_menu_prvm_country"))); // country
- url_fputs(fh, sprintf("g %s\n", cvar_string("_menu_prvm_gender"))); // gender
+ url_fputs(fh, sprintf("g %s\n", cvar_string("_cl_gender"))); // gender
url_fputs(fh, sprintf("n %s\n", cvar_string("_cl_name"))); // name
url_fputs(fh, sprintf("m %s %s\n", cvar_string("_cl_playermodel"), cvar_string("_cl_playerskin"))); // model/skin
#endif
#ifdef CSQC
/*
- * FIXME - crypto_* builtin functions missing in CSQC (csprogsdefs.qc:885)
+ * FIXME - crypto_* builtin functions missing in CSQC (csprogsdefs.qh:885)
void PlayerInfo_Details()
{
print("-- Getting detailed PlayerInfo for local player (CSQC)\n");
+#ifndef WEAPONS_C
+#define WEAPONS_C
+
+#if defined(CSQC)
+ #include "../../dpdefs/csprogsdefs.qh"
+ #include "../../client/defs.qh"
+ #include "../constants.qh"
+ #include "../stats.qh"
+ #include "../../warpzonelib/anglestransform.qh"
+ #include "../../warpzonelib/mathlib.qh"
+ #include "../../warpzonelib/common.qh"
+ #include "../../warpzonelib/client.qh"
+ #include "../util.qh"
+ #include "../buffs.qh"
+ #include "weapons.qh"
+ #include "../../client/autocvars.qh"
+ #include "../deathtypes.qh"
+ #include "../../csqcmodellib/interpolate.qh"
+ #include "../../client/movetypes.qh"
+ #include "../../client/main.qh"
+ #include "../../csqcmodellib/cl_model.qh"
+#elif defined(MENUQC)
+#elif defined(SVQC)
+ #include "../../dpdefs/progsdefs.qh"
+ #include "../../dpdefs/dpextensions.qh"
+ #include "../../warpzonelib/anglestransform.qh"
+ #include "../../warpzonelib/mathlib.qh"
+ #include "../../warpzonelib/common.qh"
+ #include "../../warpzonelib/util_server.qh"
+ #include "../../warpzonelib/server.qh"
+ #include "../constants.qh"
+ #include "../stats.qh"
+ #include "../teams.qh"
+ #include "../util.qh"
+ #include "../buffs.qh"
+ #include "../monsters/monsters.qh"
+ #include "config.qh"
+ #include "weapons.qh"
+ #include "../../server/weapons/csqcprojectile.qh"
+ #include "../../server/weapons/tracing.qh"
+ #include "../../server/t_items.qh"
+ #include "../../server/autocvars.qh"
+ #include "../../server/constants.qh"
+ #include "../../server/defs.qh"
+ #include "../notifications.qh"
+ #include "../deathtypes.qh"
+ #include "../../server/mutators/mutators_include.qh"
+ #include "../mapinfo.qh"
+ #include "../../server/command/common.qh"
+ #include "../../csqcmodellib/sv_model.qh"
+ #include "../../server/portals.qh"
+ #include "../../server/g_hook.qh"
+#endif
#ifndef MENUQC
#include "calculations.qc"
#endif
#elif WEP_MAXCOUNT > 24
WriteInt48_t(dst, w);
#else
- WriteInt24_t(dst, w_x);
+ WriteInt24_t(dst, w.x);
#endif
}
#endif
WepSet WepSet_GetFromStat()
{
WepSet w = '0 0 0';
- w_x = getstati(STAT_WEAPONS);
+ w.x = getstati(STAT_WEAPONS);
#if WEP_MAXCOUNT > 24
- w_y = getstati(STAT_WEAPONS2);
+ w.y = getstati(STAT_WEAPONS2);
#if WEP_MAXCOUNT > 48
- w_z = getstati(STAT_WEAPONS3);
+ w.z = getstati(STAT_WEAPONS3);
#endif
#endif
return w;
#endif
void register_weapon(
- float id,
+ int id,
WepSet bit,
float(float) func,
.float ammotype,
e.netname = refname;
e.message = wepname;
- #ifndef MENUQC
+ #ifdef CSQC
func(WR_INIT);
#endif
}
dummy_weapon_info.w_crosshair_size = 1;
dummy_weapon_info.model2 = "";
- float i;
+ int i;
weaponorder_byid = "";
for(i = WEP_MAXCOUNT; i >= 1; --i)
if(weapon_info[i-1])
weaponorder_byid = strcat(weaponorder_byid, " ", ftos(i));
weaponorder_byid = strzone(substring(weaponorder_byid, 1, strlen(weaponorder_byid) - 1));
}
-entity get_weaponinfo(float id)
+entity get_weaponinfo(int id)
{
entity w;
if(id < WEP_FIRST || id > WEP_LAST)
switch ( s )
{
case "nex" : return "vortex";
- case "rocketlauncher" : return "devastator";
+ case "rocketlauncher" : return "devastator";
case "laser" : return "blaster";
case "minstanex" : return "vaporizer";
case "grenadelauncher": return "mortar";
float W_FixWeaponOrder_BuildImpulseList_buf[WEP_MAXCOUNT];
string W_FixWeaponOrder_BuildImpulseList_order;
-void W_FixWeaponOrder_BuildImpulseList_swap(float i, float j, entity pass)
+void W_FixWeaponOrder_BuildImpulseList_swap(int i, int j, entity pass)
{
float h;
h = W_FixWeaponOrder_BuildImpulseList_buf[i];
W_FixWeaponOrder_BuildImpulseList_buf[i] = W_FixWeaponOrder_BuildImpulseList_buf[j];
W_FixWeaponOrder_BuildImpulseList_buf[j] = h;
}
-float W_FixWeaponOrder_BuildImpulseList_cmp(float i, float j, entity pass)
+float W_FixWeaponOrder_BuildImpulseList_cmp(int i, int j, entity pass)
{
entity e1, e2;
float d;
}
string W_FixWeaponOrder_BuildImpulseList(string o)
{
- float i;
+ int i;
W_FixWeaponOrder_BuildImpulseList_order = o;
for(i = WEP_FIRST; i <= WEP_LAST; ++i)
W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST] = i;
}
#ifdef CSQC
-.float GetAmmoFieldFromNum(float i)
+.float GetAmmoFieldFromNum(int i)
{
switch(i)
{
}
}
-float GetAmmoStat(.float ammotype)
+int GetAmmoStat(.float ammotype)
{
switch(ammotype)
{
}
}
#endif
+#endif
+#ifndef WEAPONS_H
+#define WEAPONS_H
+
#ifndef MENUQC
#include "calculations.qh"
#endif
-const float MAX_SHOT_DISTANCE = 32768;
+const int MAX_SHOT_DISTANCE = 32768;
// weapon pickup ratings for bot logic
-const float BOT_PICKUP_RATING_LOW = 2500;
-const float BOT_PICKUP_RATING_MID = 5000;
-const float BOT_PICKUP_RATING_HIGH = 10000;
+const int BOT_PICKUP_RATING_LOW = 2500;
+const int BOT_PICKUP_RATING_MID = 5000;
+const int BOT_PICKUP_RATING_HIGH = 10000;
// weapon flags
-const float WEP_TYPE_OTHER = 0x00; // not for damaging people
-const float WEP_TYPE_SPLASH = 0x01; // splash damage
-const float WEP_TYPE_HITSCAN = 0x02; // hitscan
-const float WEP_TYPEMASK = 0x0F;
-const float WEP_FLAG_CANCLIMB = 0x10; // can be used for movement
-const float WEP_FLAG_NORMAL = 0x20; // in "most weapons" set
-const float WEP_FLAG_HIDDEN = 0x40; // hides from menu
-const float WEP_FLAG_RELOADABLE = 0x80; // can has reload
-const float WEP_FLAG_SUPERWEAPON = 0x100; // powerup timer
-const float WEP_FLAG_MUTATORBLOCKED = 0x200; // hides from impulse 99 etc. (mutators are allowed to clear this flag)
+const int WEP_TYPE_OTHER = 0x00; // not for damaging people
+const int WEP_TYPE_SPLASH = 0x01; // splash damage
+const int WEP_TYPE_HITSCAN = 0x02; // hitscan
+const int WEP_TYPEMASK = 0x0F;
+const int WEP_FLAG_CANCLIMB = 0x10; // can be used for movement
+const int WEP_FLAG_NORMAL = 0x20; // in "most weapons" set
+const int WEP_FLAG_HIDDEN = 0x40; // hides from menu
+const int WEP_FLAG_RELOADABLE = 0x80; // can has reload
+const int WEP_FLAG_SUPERWEAPON = 0x100; // powerup timer
+const int WEP_FLAG_MUTATORBLOCKED = 0x200; // hides from impulse 99 etc. (mutators are allowed to clear this flag)
// weapon requests
-const float WR_SETUP = 1; // (SERVER) setup weapon data
-const float WR_THINK = 2; // (SERVER) logic to run every frame
-const float WR_CHECKAMMO1 = 3; // (SERVER) checks ammo for weapon primary
-const float WR_CHECKAMMO2 = 4; // (SERVER) checks ammo for weapon second
-const float WR_AIM = 5; // (SERVER) runs bot aiming code for this weapon
-const float WR_INIT = 6; // (BOTH) precaches models/sounds used by this weapon, also sets up weapon properties
-const float WR_SUICIDEMESSAGE = 7; // (SERVER) notification number for suicide message (may inspect w_deathtype for details)
-const float WR_KILLMESSAGE = 8; // (SERVER) notification number for kill message (may inspect w_deathtype for details)
-const float WR_RELOAD = 9; // (SERVER) handles reloading for weapon
-const float WR_RESETPLAYER = 10; // (SERVER) clears fields that the weapon may use
-const float WR_IMPACTEFFECT = 11; // (CLIENT) impact effect for weapon explosion
-const float WR_PLAYERDEATH = 12; // (SERVER) called whenever a player dies
-const float WR_GONETHINK = 13; // (SERVER) logic to run when weapon is lost
-const float WR_CONFIG = 14; // (ALL) dump weapon cvars to config in data directory (see: sv_cmd dumpweapons)
-const float WR_ZOOMRETICLE = 15; // (CLIENT) weapon specific zoom reticle
-const float WR_DROP = 16; // (SERVER) the weapon is dropped
-const float WR_PICKUP = 17; // (SERVER) a weapon is picked up
+const int WR_SETUP = 1; // (SERVER) setup weapon data
+const int WR_THINK = 2; // (SERVER) logic to run every frame
+const int WR_CHECKAMMO1 = 3; // (SERVER) checks ammo for weapon primary
+const int WR_CHECKAMMO2 = 4; // (SERVER) checks ammo for weapon second
+const int WR_AIM = 5; // (SERVER) runs bot aiming code for this weapon
+const int WR_INIT = 6; // (BOTH) precaches models/sounds used by this weapon, also sets up weapon properties
+const int WR_SUICIDEMESSAGE = 7; // (SERVER) notification number for suicide message (may inspect w_deathtype for details)
+const int WR_KILLMESSAGE = 8; // (SERVER) notification number for kill message (may inspect w_deathtype for details)
+const int WR_RELOAD = 9; // (SERVER) handles reloading for weapon
+const int WR_RESETPLAYER = 10; // (SERVER) clears fields that the weapon may use
+const int WR_IMPACTEFFECT = 11; // (CLIENT) impact effect for weapon explosion
+const int WR_PLAYERDEATH = 12; // (SERVER) called whenever a player dies
+const int WR_GONETHINK = 13; // (SERVER) logic to run when weapon is lost
+const int WR_CONFIG = 14; // (ALL) dump weapon cvars to config in data directory (see: sv_cmd dumpweapons)
+const int WR_ZOOMRETICLE = 15; // (CLIENT) weapon specific zoom reticle
+const int WR_DROP = 16; // (SERVER) the weapon is dropped
+const int WR_PICKUP = 17; // (SERVER) a weapon is picked up
// variables:
string weaponorder_byid;
// weapon name macros
#define WEP_FIRST 1
#define WEP_MAXCOUNT 24 // Increase as needed. Can be up to three times as much.
-float WEP_COUNT;
-float WEP_LAST;
+int WEP_COUNT;
+int WEP_LAST;
WepSet WEPSET_ALL;
WepSet WEPSET_SUPERWEAPONS;
// functions:
entity get_weaponinfo(float id);
string W_FixWeaponOrder(string order, float complete);
++string W_UndeprecateName(string s);
string W_NameWeaponOrder(string order);
string W_NumberWeaponOrder(string order);
string W_FixWeaponOrder_BuildImpulseList(string o);
string GetAmmoPicture(.float ammotype);
#ifdef CSQC
-.float GetAmmoFieldFromNum(float i);
-float GetAmmoStat(.float ammotype);
+.float GetAmmoFieldFromNum(int i);
+int GetAmmoStat(.float ammotype);
#endif
// ammo types
// entity properties of weaponinfo:
// fields which are explicitly/manually set are marked with "M", fields set automatically are marked with "A"
-.float weapon; // M: WEP_id // WEP_...
+.int weapon; // M: WEP_id // WEP_...
.WepSet weapons; // A: WEPSET_id // WEPSET_...
.float(float) weapon_func; // M: function // w_...
..float ammo_field; // M: ammotype // main ammo field
-.float impulse; // M: impulse // weapon impulse
-.float spawnflags; // M: flags // WEPSPAWNFLAG_... combined
+.int impulse; // M: impulse // weapon impulse
+.int spawnflags; // M: flags // WEPSPAWNFLAG_... combined
.float bot_pickupbasevalue; // M: rating // bot weapon priority
.vector wpcolor; // M: color // waypointsprite color
.string wpmodel; // A: wpn-id // wpn- sprite name
// note: the fabs call is just there to hide "if result is constant" warning
#define REGISTER_WEAPON_2(id,bit,function,ammotype,impulse,flags,rating,color,modelname,simplemdl,crosshair,wepimg,refname,wepname) \
- float id; \
+ int id; \
WepSet bit; \
float function(float); \
void RegisterWeapons_##id() \
#undef REGISTER_WEAPON
ACCUMULATE_FUNCTION(RegisterWeapons, register_weapons_done);
+#endif
--- /dev/null
+#ifndef PROGSDEFS_H
+#define PROGSDEFS_H
+
+/*
+==============================================================================
+
+ SOURCE FOR GLOBALVARS_T C STRUCTURE
+ MUST NOT BE MODIFIED, OR CRC ERRORS WILL APPEAR
+
+==============================================================================
+*/
+
+//
+// system globals
+//
+entity self;
+entity other;
+entity world;
+float time;
+float frametime;
+
+float force_retouch; // force all entities to touch triggers
+ // next frame. this is needed because
+ // non-moving things don't normally scan
+ // for triggers, and when a trigger is
+ // created (like a teleport trigger), it
+ // needs to catch everything.
+ // decremented each frame, so set to 2
+ // to guarantee everything is touched
+string mapname;
+
+float deathmatch;
+float coop;
+float teamplay;
+
+int serverflags; // propagated from level to level, used to
+ // keep track of completed episodes
+
+float total_secrets;
+float total_monsters;
+
+float found_secrets; // number of secrets found
+float killed_monsters; // number of monsters killed
+
+
+// spawnparms are used to encode information about clients across server
+// level changes
+float parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16;
+
+//
+// global variables set by built in functions
+//
+vector v_forward, v_up, v_right; // set by makevectors()
+
+// set by traceline / tracebox
+float trace_allsolid;
+float trace_startsolid;
+float trace_fraction;
+vector trace_endpos;
+vector trace_plane_normal;
+float trace_plane_dist;
+entity trace_ent;
+float trace_inopen;
+float trace_inwater;
+
+entity msg_entity; // destination of single entity writes
+
+//
+// required prog functions
+//
+void() main; // only for testing
+
+void() StartFrame;
+
+void() PlayerPreThink;
+void() PlayerPostThink;
+
+void() ClientKill;
++#ifdef DP_EXT_PRECONNECT
++void() ClientPreConnect;
++#endif
+void() ClientConnect;
+void() PutClientInServer; // call after setting the parm1... parms
+void() ClientDisconnect;
+
+void() SetNewParms; // called when a client first connects to
+ // a server. sets parms so they can be
+ // saved off for restarts
+
+void() SetChangeParms; // call to set parms for self so they can
+ // be saved for a level transition
+
+
+//================================================
+void end_sys_globals; // flag for structure dumping
+//================================================
+
+/*
+==============================================================================
+
+ SOURCE FOR ENTVARS_T C STRUCTURE
+ MUST NOT BE MODIFIED, OR CRC ERRORS WILL APPEAR
+
+==============================================================================
+*/
+
+//
+// system fields (*** = do not set in prog code, maintained by C code)
+//
+.int modelindex; // *** model index in the precached list
+.vector absmin, absmax; // *** origin + mins / maxs
+
+.float ltime; // local time for entity
+.float movetype;
+.float solid;
+
+.vector origin; // ***
+.vector oldorigin; // ***
+.vector velocity;
+.vector angles;
+.vector avelocity;
+
+.vector punchangle; // temp angle adjust from damage or recoil
+
+.string classname; // spawn function
+.string model;
+.int frame;
+.int skin;
+.int effects;
+
+.vector mins, maxs; // bounding box extents reletive to origin
+.vector size; // maxs - mins
+
+.void() touch;
+.void() use;
+.void() think;
+.void() blocked; // for doors or plats, called when can't push other
+
+.float nextthink;
+.entity groundentity;
+
+// stats
+.float health;
+.float frags;
+.int weapon; // one of the IT_SHOTGUN, etc flags
+.string weaponmodel;
+.float weaponframe;
+.float currentammo;
+.float ammo_shells, ammo_nails, ammo_rockets, ammo_cells;
+
+.int items; // bit flags
+
+.float takedamage;
+.entity chain;
+.float deadflag;
+
+.vector view_ofs; // add to origin to get eye point
+
+
+.float button0; // fire
+.float button1; // use
+.float button2; // jump
+
+.float impulse; // weapon changes
+
+.float fixangle;
+.vector v_angle; // view / targeting angle for players
+.float idealpitch; // calculated pitch angle for lookup up slopes
+
+
+.string netname;
+
+.entity enemy;
+
+.int flags;
+
+.int colormap;
+.float team;
+
+.float max_health; // players maximum health is stored here
+
+.float teleport_time; // don't back up
+
+.float armortype; // save this fraction of incoming damage
+.float armorvalue;
+
+.float waterlevel; // 0 = not in, 1 = feet, 2 = wast, 3 = eyes
+.float watertype; // a contents value
+
+.float ideal_yaw;
+.float yaw_speed;
+
+.entity aiment;
+
+.entity goalentity; // a movetarget or an enemy
+
+.int spawnflags;
+
+.string target;
+.string targetname;
+
+// damage is accumulated through a frame. and sent as one single
+// message, so the super shotgun doesn't generate huge messages
+.float dmg_take;
+.float dmg_save;
+.entity dmg_inflictor;
+
+.entity owner; // who launched a missile
+.vector movedir; // mostly for doors, but also used for waterjump
+
+.string message; // trigger messages
+
+.float sounds; // either a cd track number or sound number
+
+.string noise, noise1, noise2, noise3; // contains names of wavs to play
+
+//================================================
+void end_sys_fields; // flag for structure dumping
+//================================================
+
+/*
+==============================================================================
+
+ CONSTANT DEFINITIONS
+
+==============================================================================
+*/
+
+
+//
+// constants
+//
+
+// edict.flags
+const int FL_FLY = 1;
+const int FL_SWIM = 2;
+const int FL_CLIENT = 8; // set for all client edicts
+const int FL_INWATER = 16; // for enter / leave water splash
+const int FL_MONSTER = 32;
+const int FL_GODMODE = 64; // player cheat
+const int FL_NOTARGET = 128; // player cheat
+const int FL_ITEM = 256; // extra wide size for bonus items
+const int FL_ONGROUND = 512; // standing on something
+const int FL_PARTIALGROUND = 1024; // not all corners are valid
+const int FL_WATERJUMP = 2048; // player jumping out of water
+const int FL_JUMPRELEASED = 4096; // for jump debouncing
+
+// edict.movetype values
+const int MOVETYPE_NONE = 0; // never moves
+//const int MOVETYPE_ANGLENOCLIP= 1;
+//const int MOVETYPE_ANGLECLIP = 2;
+const int MOVETYPE_WALK = 3; // players only
+const int MOVETYPE_STEP = 4; // discrete, not real time unless fall
+const int MOVETYPE_FLY = 5;
+const int MOVETYPE_TOSS = 6; // gravity
+const int MOVETYPE_PUSH = 7; // no clip to world, push and crush
+const int MOVETYPE_NOCLIP = 8;
+const int MOVETYPE_FLYMISSILE = 9; // fly with extra size against monsters
+const int MOVETYPE_BOUNCE = 10;
+const int MOVETYPE_BOUNCEMISSILE= 11; // bounce with extra size
+
+// edict.solid values
+const int SOLID_NOT = 0; // no interaction with other objects
+const int SOLID_TRIGGER = 1; // touch on edge, but not blocking
+const int SOLID_BBOX = 2; // touch on edge, block
+const int SOLID_SLIDEBOX = 3; // touch on edge, but not an onground
+const int SOLID_BSP = 4; // bsp clip, touch on edge, block
+
+// range values
+const int RANGE_MELEE = 0;
+const int RANGE_NEAR = 1;
+const int RANGE_MID = 2;
+const int RANGE_FAR = 3;
+
+// deadflag values
+
+const int DEAD_NO = 0;
+const int DEAD_DYING = 1;
+const int DEAD_DEAD = 2;
+const int DEAD_RESPAWNABLE = 3;
+const int DEAD_RESPAWNING = 4; // dead, waiting for buttons to be released
+
+// takedamage values
+
+const int DAMAGE_NO = 0;
+const int DAMAGE_YES = 1;
+const int DAMAGE_AIM = 2;
+
+// items
+const int IT_AXE = 4096;
+const int IT_SHOTGUN = 1;
+const int IT_SUPER_SHOTGUN = 2;
+const int IT_NAILGUN = 4;
+const int IT_SUPER_NAILGUN = 8;
+const int IT_GRENADE_LAUNCHER = 16;
+const int IT_ROCKET_LAUNCHER = 32;
+const int IT_LIGHTNING = 64;
+const int IT_EXTRA_WEAPON = 128;
+
+//const int IT_SHELLS = 256;
+//const int IT_NAILS = 512;
+//const int IT_ROCKETS = 1024;
+//const int IT_CELLS = 2048;
+
+const int IT_ARMOR1 = 8192;
+const int IT_ARMOR2 = 16384;
+const int IT_ARMOR3 = 32768;
+const int IT_SUPERHEALTH = 65536;
+
+//const int IT_KEY1 = 131072;
+//const int IT_KEY2 = 262144;
+
+const int IT_INVISIBILITY = 524288;
+const int IT_INVULNERABILITY = 1048576;
+const int IT_SUIT = 2097152;
+const int IT_QUAD = 4194304;
+
+// point content values
+
+const int CONTENT_EMPTY = -1;
+const int CONTENT_SOLID = -2;
+const int CONTENT_WATER = -3;
+const int CONTENT_SLIME = -4;
+const int CONTENT_LAVA = -5;
+const int CONTENT_SKY = -6;
+
+const int STATE_TOP = 0;
+const int STATE_BOTTOM = 1;
+const int STATE_UP = 2;
+const int STATE_DOWN = 3;
+
+const vector VEC_ORIGIN = '0 0 0';
+const vector VEC_HULL_MIN = '-16 -16 -24';
+const vector VEC_HULL_MAX = '16 16 32';
+
+const vector VEC_HULL2_MIN = '-32 -32 -24';
+const vector VEC_HULL2_MAX = '32 32 64';
+
+// protocol bytes
+const int SVC_TEMPENTITY = 23;
+const int SVC_KILLEDMONSTER = 27;
+const int SVC_FOUNDSECRET = 28;
+const int SVC_INTERMISSION = 30;
+const int SVC_FINALE = 31;
+const int SVC_CDTRACK = 32;
+const int SVC_SELLSCREEN = 33;
+
+
+const int TE_SPIKE = 0;
+const int TE_SUPERSPIKE = 1;
+const int TE_GUNSHOT = 2;
+const int TE_EXPLOSION = 3;
+const int TE_TAREXPLOSION = 4;
+const int TE_LIGHTNING1 = 5;
+const int TE_LIGHTNING2 = 6;
+const int TE_WIZSPIKE = 7;
+const int TE_KNIGHTSPIKE = 8;
+const int TE_LIGHTNING3 = 9;
+const int TE_LAVASPLASH = 10;
+const int TE_TELEPORT = 11;
+
+// sound channels
+// channel 0 never willingly overrides
+// other channels (1-7) allways override a playing sound on that channel
+const int CHAN_AUTO = 0;
+const int CHAN_WEAPON = 1;
+const int CHAN_VOICE = 2;
+const int CHAN_ITEM = 3;
+const int CHAN_BODY = 4;
+
+const int ATTN_NONE = 0;
+const int ATTN_NORM = 1;
+const int ATTN_IDLE = 2;
+const int ATTN_STATIC = 3;
+
+// update types
+
+const int UPDATE_GENERAL = 0;
+const int UPDATE_STATIC = 1;
+const int UPDATE_BINARY = 2;
+const int UPDATE_TEMP = 3;
+
+// entity effects
+
+const int EF_BRIGHTFIELD = 1;
+const int EF_MUZZLEFLASH = 2;
+const int EF_BRIGHTLIGHT = 4;
+const int EF_DIMLIGHT = 8;
+
+
+// messages
+const int MSG_BROADCAST = 0; // unreliable to all
+const int MSG_ONE = 1; // reliable to one (msg_entity)
+const int MSG_ALL = 2; // reliable to all
+const int MSG_INIT = 3; // write to the init string
+
+//===========================================================================
+
+//
+// builtin functions
+//
+
+void(vector ang) makevectors = #1; // sets v_forward, etc globals
+void(entity e, vector o) setorigin = #2;
+void(entity e, string m) setmodel = #3; // set movetype and solid first
+void(entity e, vector min, vector max) setsize = #4;
+// #5 was removed
+void() break_to_debugger = #6;
+float() random = #7; // returns 0 - 1
+void(entity e, float chan, string samp, float vol, float atten) sound = #8;
+vector(vector v) normalize = #9;
+void(string e, ...) error = #10;
+void(string e, ...) objerror = #11;
+float(vector v) vlen = #12;
+float(vector v) vectoyaw = #13;
+entity() spawn = #14;
+void(entity e) remove = #15;
+
+// sets trace_* globals
+// nomonsters can be:
+// An entity will also be ignored for testing if forent == test,
+// forent->owner == test, or test->owner == forent
+// a forent of world is ignored
+void(vector v1, vector v2, float nomonsters, entity forent) traceline = #16;
+
+entity() checkclient = #17; // returns a client to look for
+entity(entity start, .string fld, string match) find = #18;
+string(string s) precache_sound = #19;
+string(string s) precache_model = #20;
+void(entity client, string s, ...)stuffcmd = #21;
+entity(vector org, float rad) findradius = #22;
+void(string s, ...) bprint = #23;
+void(entity client, string s, ...) sprint = #24;
+void(string s, ...) dprint = #25;
+string(float f) ftos = #26;
+string(vector v) vtos = #27;
+void() coredump = #28; // prints all edicts
+void() traceon = #29; // turns statment trace on
+void() traceoff = #30;
+void(entity e) eprint = #31; // prints an entire edict
+float(float yaw, float dist) walkmove = #32; // returns true or false
+// #33 was removed
+float() droptofloor= #34; // true if landed on floor
+void(float style, string value) lightstyle = #35;
+float(float v) rint = #36; // round to nearest int
+float(float v) floor = #37; // largest integer <= v
+float(float v) ceil = #38; // smallest integer >= v
+// #39 was removed
+float(entity e) checkbottom = #40; // true if self is on ground
+float(vector v) pointcontents = #41; // returns a CONTENT_*
+// #42 was removed
+float(float f) fabs = #43;
+vector(entity e, float speed) aim = #44; // returns the shooting vector
+float(string s) cvar = #45; // return cvar.value
+void(string s, ...) localcmd = #46; // put string into local que
+entity(entity e) nextent = #47; // for looping through all ents
+void(vector o, vector d, float color, float count) particle = #48;// start a particle effect
+void() ChangeYaw = #49; // turn towards self.ideal_yaw
+ // at self.yaw_speed
+// #50 was removed
+vector(vector v) vectoangles = #51;
+
+//
+// direct client message generation
+//
+void(float to, float f) WriteByte = #52;
+void(float to, float f) WriteChar = #53;
+void(float to, float f) WriteShort = #54;
+void(float to, float f) WriteLong = #55;
+void(float to, float f) WriteCoord = #56;
+void(float to, float f) WriteAngle = #57;
+void(float to, string s, ...) WriteString = #58;
+void(float to, entity s) WriteEntity = #59;
+
+//
+// broadcast client message generation
+//
+
+// void(float f) bWriteByte = #59;
+// void(float f) bWriteChar = #60;
+// void(float f) bWriteShort = #61;
+// void(float f) bWriteLong = #62;
+// void(float f) bWriteCoord = #63;
+// void(float f) bWriteAngle = #64;
+// void(string s) bWriteString = #65;
+// void(entity e) bWriteEntity = #66;
+
+void(float step) movetogoal = #67;
+
+string(string s) precache_file = #68; // no effect except for -copy
+void(entity e) makestatic = #69;
+void(string s) changelevel = #70;
+
+//#71 was removed
+
+void(string name, string value) cvar_set = #72; // sets cvar.value
+
+void(entity client, string s, ...) centerprint = #73; // sprint, but in middle
+
+void(vector pos, string samp, float vol, float atten) ambientsound = #74;
+
+string(string s) precache_model2 = #75; // registered version only
+string(string s) precache_sound2 = #76; // registered version only
+string(string s) precache_file2 = #77; // registered version only
+
+void(entity e) setspawnparms = #78; // set parm1... to the
+ // values at level start
+ // for coop respawn
+
+//============================================================================
+#endif
+#include "menu_cmd.qh"
+
+#include "../../common/command/generic.qh"
+
string _dumptree_space;
void _dumptree_open(entity pass, entity me)
{
}
}
else if(argc == 2 && !isdemo()) // don't allow this command in demos
+ {
+ m_play_click_sound(MENU_SOUND_OPEN);
m_goto(strcat(filter, argv(1))); // switch to a menu item
+ }
if(filter)
strunzone(filter);
return;
--- /dev/null
+#ifdef INTERFACE
+CLASS(Item) EXTENDS(Object)
+ METHOD(Item, draw, void(entity))
+ METHOD(Item, keyDown, float(entity, float, float, float))
+ METHOD(Item, keyUp, float(entity, float, float, float))
+ METHOD(Item, mouseMove, float(entity, vector))
+ METHOD(Item, mousePress, float(entity, vector))
+ METHOD(Item, mouseDrag, float(entity, vector))
+ METHOD(Item, mouseRelease, float(entity, vector))
+ METHOD(Item, focusEnter, void(entity))
+ METHOD(Item, focusLeave, void(entity))
+ METHOD(Item, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(Item, relinquishFocus, void(entity))
+ METHOD(Item, showNotify, void(entity))
+ METHOD(Item, hideNotify, void(entity))
+ METHOD(Item, toString, string(entity))
+ METHOD(Item, destroy, void(entity))
+ ATTRIB(Item, focused, float, 0)
+ ATTRIB(Item, focusable, float, 0)
++ ATTRIB(Item, allowFocusSound, float, 0)
+ ATTRIB(Item, parent, entity, NULL)
+ ATTRIB(Item, preferredFocusPriority, float, 0)
+ ATTRIB(Item, origin, vector, '0 0 0')
+ ATTRIB(Item, size, vector, '0 0 0')
+ ATTRIB(Item, tooltip, string, string_null)
+ENDCLASS(Item)
+#endif
+
+#ifdef IMPLEMENTATION
+void Item_destroy(entity me)
+{
+ // free memory associated with me
+}
+
+void Item_relinquishFocus(entity me)
+{
+ if(me.parent)
+ if(me.parent.instanceOfContainer)
+ me.parent.setFocus(me.parent, NULL);
+}
+
+void Item_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ me.origin = absOrigin;
+ me.size = absSize;
+}
+
+float autocvar_menu_showboxes;
+void Item_draw(entity me)
+{
+ if(autocvar_menu_showboxes)
+ {
+ vector rgb = '1 0 1';
+ float a = fabs(autocvar_menu_showboxes);
+
+ // don't draw containers and border images
+ if(me.instanceOfContainer || me.instanceOfBorderImage)
+ {
+ rgb = '0 0 0';
+ a = 0;
+ }
+
+#if 0
+ // hack to detect multi drawing
+ float r = random() * 3;
+ if(r >= 2)
+ rgb = '1 0 0';
+ else if(r >= 1)
+ rgb = '0 1 0';
+ else
+ rgb = '0 0 1';
+#endif
+ if(autocvar_menu_showboxes < 0)
+ {
+ draw_Fill('0 0 0', '0.5 0.5 0', rgb, a);
+ draw_Fill('0.5 0.5 0', '0.5 0.5 0', rgb, a);
+ }
+ if(autocvar_menu_showboxes > 0)
+ {
+ draw_Fill('0 0 0', '1 1 0', rgb, a);
+ }
+ }
+}
+
+void Item_showNotify(entity me)
+{
+}
+
+void Item_hideNotify(entity me)
+{
+}
+
+float Item_keyDown(entity me, float scan, float ascii, float shift)
+{
+ return 0; // unhandled
+}
+
+float Item_keyUp(entity me, float scan, float ascii, float shift)
+{
+ return 0; // unhandled
+}
+
+float Item_mouseMove(entity me, vector pos)
+{
+ return 0; // unhandled
+}
+
+float Item_mousePress(entity me, vector pos)
+{
+ return 0; // unhandled
+}
+
+float Item_mouseDrag(entity me, vector pos)
+{
+ return 0; // unhandled
+}
+
+float Item_mouseRelease(entity me, vector pos)
+{
+ return 0; // unhandled
+}
+
+void Item_focusEnter(entity me)
+{
++ if(me.allowFocusSound)
++ m_play_focus_sound();
+}
+
+void Item_focusLeave(entity me)
+{
+}
+
+string Item_toString(entity me)
+{
+ return string_null;
+}
+#endif
--- /dev/null
- METHOD(Button, focusEnter, void(entity))
+#ifdef INTERFACE
+CLASS(Button) EXTENDS(Label)
+ METHOD(Button, configureButton, void(entity, string, float, string))
+ METHOD(Button, draw, void(entity))
+ METHOD(Button, showNotify, void(entity))
+ METHOD(Button, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(Button, keyDown, float(entity, float, float, float))
+ METHOD(Button, mousePress, float(entity, vector))
+ METHOD(Button, mouseDrag, float(entity, vector))
+ METHOD(Button, mouseRelease, float(entity, vector))
- if(cvar("menu_sounds"))
- localsound("sound/misc/menu2.wav");
++ METHOD(Button, playClickSound, void(entity))
+ ATTRIB(Button, onClick, void(entity, entity), func_null)
+ ATTRIB(Button, onClickEntity, entity, NULL)
+ ATTRIB(Button, src, string, string_null)
+ ATTRIB(Button, srcSuffix, string, string_null)
+ ATTRIB(Button, src2, string, string_null) // is centered, same aspect, and stretched to label size
+ ATTRIB(Button, src2scale, float, 1)
+ ATTRIB(Button, srcMulti, float, 1) // 0: button square left, text right; 1: button stretched, text over it
+ ATTRIB(Button, buttonLeftOfText, float, 0)
+ ATTRIB(Button, focusable, float, 1)
++ ATTRIB(Button, allowFocusSound, float, 1)
+ ATTRIB(Button, pressed, float, 0)
+ ATTRIB(Button, clickTime, float, 0)
+ ATTRIB(Button, disabled, float, 0)
+ ATTRIB(Button, disabledAlpha, float, 0.3)
+ ATTRIB(Button, forcePressed, float, 0)
+ ATTRIB(Button, color, vector, '1 1 1')
+ ATTRIB(Button, colorC, vector, '1 1 1')
+ ATTRIB(Button, colorF, vector, '1 1 1')
+ ATTRIB(Button, colorD, vector, '1 1 1')
+ ATTRIB(Button, color2, vector, '1 1 1')
+ ATTRIB(Button, alpha2, float, 1)
+
+ ATTRIB(Button, origin, vector, '0 0 0')
+ ATTRIB(Button, size, vector, '0 0 0')
+ENDCLASS(Button)
+#endif
+
+#ifdef IMPLEMENTATION
+void Button_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ if(me.srcMulti)
+ me.keepspaceLeft = 0;
+ else
+ me.keepspaceLeft = min(0.8, absSize.y / absSize.x);
+ SUPER(Button).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
+}
+void Button_configureButton(entity me, string txt, float sz, string gfx)
+{
+ SUPER(Button).configureLabel(me, txt, sz, me.srcMulti ? 0.5 : 0);
+ me.src = gfx;
+}
+float Button_keyDown(entity me, float key, float ascii, float shift)
+{
+ if(key == K_ENTER || key == K_SPACE || key == K_KP_ENTER)
+ {
++ me.playClickSound(me);
+ me.clickTime = 0.1; // delayed for effect
+ return 1;
+ }
+ return 0;
+}
+float Button_mouseDrag(entity me, vector pos)
+{
+ me.pressed = 1;
+ if(pos.x < 0) me.pressed = 0;
+ if(pos.y < 0) me.pressed = 0;
+ if(pos.x >= 1) me.pressed = 0;
+ if(pos.y >= 1) me.pressed = 0;
+ return 1;
+}
+float Button_mousePress(entity me, vector pos)
+{
+ me.mouseDrag(me, pos); // verify coordinates
+ return 1;
+}
+float Button_mouseRelease(entity me, vector pos)
+{
+ me.mouseDrag(me, pos); // verify coordinates
+ if(me.pressed)
+ {
+ if (!me.disabled)
+ {
- void Button_focusEnter(entity me)
- {
- if(cvar("menu_sounds") > 1)
- localsound("sound/misc/menu1.wav");
- SUPER(Button).focusEnter(me);
- }
++ me.playClickSound(me);
+ if(me.onClick)
+ me.onClick(me, me.onClickEntity);
+ }
+ me.pressed = 0;
+ }
+ return 1;
+}
+void Button_showNotify(entity me)
+{
+ me.focusable = !me.disabled;
+}
+void Button_draw(entity me)
+{
+ vector bOrigin, bSize;
+ float save;
+
+ me.focusable = !me.disabled;
+
+ save = draw_alpha;
+ if(me.disabled)
+ draw_alpha *= me.disabledAlpha;
+
+ if(me.src)
+ {
+ if(me.srcMulti)
+ {
+ bOrigin = '0 0 0';
+ bSize = '1 1 0';
+ if(me.disabled)
+ draw_ButtonPicture(bOrigin, strcat(me.src, "_d", me.srcSuffix), bSize, me.colorD, 1);
+ else if(me.forcePressed || me.pressed || me.clickTime > 0)
+ draw_ButtonPicture(bOrigin, strcat(me.src, "_c", me.srcSuffix), bSize, me.colorC, 1);
+ else if(me.focused)
+ draw_ButtonPicture(bOrigin, strcat(me.src, "_f", me.srcSuffix), bSize, me.colorF, 1);
+ else
+ draw_ButtonPicture(bOrigin, strcat(me.src, "_n", me.srcSuffix), bSize, me.color, 1);
+ }
+ else
+ {
+ if(me.realFontSize_y == 0)
+ {
+ bOrigin = '0 0 0';
+ bSize = '1 1 0';
+ }
+ else
+ {
+ bOrigin = eY * (0.5 * (1 - me.realFontSize.y)) + eX * (0.5 * (me.keepspaceLeft - me.realFontSize.x));
+ bSize = me.realFontSize;
+ }
+ if(me.disabled)
+ draw_Picture(bOrigin, strcat(me.src, "_d", me.srcSuffix), bSize, me.colorD, 1);
+ else if(me.forcePressed || me.pressed || me.clickTime > 0)
+ draw_Picture(bOrigin, strcat(me.src, "_c", me.srcSuffix), bSize, me.colorC, 1);
+ else if(me.focused)
+ draw_Picture(bOrigin, strcat(me.src, "_f", me.srcSuffix), bSize, me.colorF, 1);
+ else
+ draw_Picture(bOrigin, strcat(me.src, "_n", me.srcSuffix), bSize, me.color, 1);
+ }
+ }
+ if(me.src2)
+ {
+ bOrigin = me.keepspaceLeft * eX;
+ bSize = eY + eX * (1 - me.keepspaceLeft);
+
+ bOrigin += bSize * (0.5 - 0.5 * me.src2scale);
+ bSize = bSize * me.src2scale;
+
+ draw_Picture(bOrigin, me.src2, bSize, me.color2, me.alpha2);
+ }
+
+ draw_alpha = save;
+
+ if(me.clickTime > 0 && me.clickTime <= frametime)
+ {
+ // keyboard click timer expired? Fire the event then.
+ if (!me.disabled)
+ if(me.onClick)
+ me.onClick(me, me.onClickEntity);
+ }
+ me.clickTime -= frametime;
+
+ SUPER(Button).draw(me);
+}
++void Dialog_Close(entity button, entity me);
++void Button_playClickSound(entity me)
++{
++ if(me.onClick == DialogOpenButton_Click)
++ m_play_click_sound(MENU_SOUND_OPEN);
++ else if(me.onClick == Dialog_Close)
++ m_play_click_sound(MENU_SOUND_CLOSE);
++ else
++ m_play_click_sound(MENU_SOUND_EXECUTE);
++}
+#endif
--- /dev/null
+#ifdef INTERFACE
+void CheckBox_Click(entity me, entity other);
+CLASS(CheckBox) EXTENDS(Button)
+ METHOD(CheckBox, configureCheckBox, void(entity, string, float, string))
+ METHOD(CheckBox, draw, void(entity))
++ METHOD(CheckBox, playClickSound, void(entity))
+ METHOD(CheckBox, toString, string(entity))
+ METHOD(CheckBox, setChecked, void(entity, float))
+ ATTRIB(CheckBox, useDownAsChecked, float, 0)
+ ATTRIB(CheckBox, checked, float, 0)
+ ATTRIB(CheckBox, onClick, void(entity, entity), CheckBox_Click)
+ ATTRIB(CheckBox, srcMulti, float, 0)
+ ATTRIB(CheckBox, disabled, float, 0)
+ENDCLASS(CheckBox)
+#endif
+
+#ifdef IMPLEMENTATION
+void CheckBox_setChecked(entity me, float val)
+{
+ me.checked = val;
+}
+void CheckBox_Click(entity me, entity other)
+{
+ me.setChecked(me, !me.checked);
+}
+string CheckBox_toString(entity me)
+{
+ return strcat(SUPER(CheckBox).toString(me), ", ", me.checked ? "checked" : "unchecked");
+}
+void CheckBox_configureCheckBox(entity me, string txt, float sz, string gfx)
+{
+ me.configureButton(me, txt, sz, gfx);
+ me.align = 0;
+}
+void CheckBox_draw(entity me)
+{
+ float s;
+ s = me.pressed;
+ if(me.useDownAsChecked)
+ {
+ me.srcSuffix = string_null;
+ me.forcePressed = me.checked;
+ }
+ else
+ me.srcSuffix = (me.checked ? "1" : "0");
+ me.pressed = s;
+ SUPER(CheckBox).draw(me);
+}
++void CheckBox_playClickSound(entity me)
++{
++ m_play_click_sound(MENU_SOUND_SELECT);
++}
+#endif
--- /dev/null
+// Note: this class is called Dialog, but it can also handle a tab under the following conditions:
+// - isTabRoot is 0
+// - backgroundImage is the tab's background
+// - closable is 0
+// - rootDialog is 0
+// - title is ""
+// - marginTop is
+// - intendedHeight ends up to be the tab's actual height, or at least close
+// - titleFontSize is 0
+// - marginTop cancels out as much of titleHeight as needed (that is, it should be actualMarginTop - titleHeight)
+// To ensure the latter, you best create all tabs FIRST and insert the tabbed
+// control to your dialog THEN - with the right height
+//
+// a subclass may help with using this as a tab
+
+#ifdef INTERFACE
+CLASS(Dialog) EXTENDS(InputContainer)
+ METHOD(Dialog, configureDialog, void(entity)) // no runtime configuration, all parameters are given in the code!
+ METHOD(Dialog, fill, void(entity)) // to be overridden by user to fill the dialog with controls
+ METHOD(Dialog, keyDown, float(entity, float, float, float))
+ METHOD(Dialog, close, void(entity))
+ METHOD(Dialog, addItemSimple, void(entity, float, float, float, float, entity, vector))
+
+ METHOD(Dialog, TD, void(entity, float, float, entity))
+ METHOD(Dialog, TDNoMargin, void(entity, float, float, entity, vector))
+ METHOD(Dialog, TDempty, void(entity, float))
+ METHOD(Dialog, setFirstColumn, void(entity, float))
+ METHOD(Dialog, TR, void(entity))
+ METHOD(Dialog, gotoRC, void(entity, float, float))
+
+ ATTRIB(Dialog, isTabRoot, float, 1)
+ ATTRIB(Dialog, closeButton, entity, NULL)
+ ATTRIB(Dialog, intendedHeight, float, 0)
+ ATTRIB(Dialog, itemOrigin, vector, '0 0 0')
+ ATTRIB(Dialog, itemSize, vector, '0 0 0')
+ ATTRIB(Dialog, itemSpacing, vector, '0 0 0')
+ ATTRIB(Dialog, currentRow, float, 0)
+ ATTRIB(Dialog, currentColumn, float, 0)
+ ATTRIB(Dialog, firstColumn, float, 0)
+
+ // to be customized
+ ATTRIB(Dialog, closable, float, 1)
+ ATTRIB(Dialog, title, string, "Form1") // ;)
+ ATTRIB(Dialog, color, vector, '1 0.5 1')
+ ATTRIB(Dialog, intendedWidth, float, 0)
+ ATTRIB(Dialog, rows, float, 3)
+ ATTRIB(Dialog, columns, float, 2)
+
+ ATTRIB(Dialog, marginTop, float, 0) // pixels
+ ATTRIB(Dialog, marginBottom, float, 0) // pixels
+ ATTRIB(Dialog, marginLeft, float, 0) // pixels
+ ATTRIB(Dialog, marginRight, float, 0) // pixels
+ ATTRIB(Dialog, columnSpacing, float, 0) // pixels
+ ATTRIB(Dialog, rowSpacing, float, 0) // pixels
+ ATTRIB(Dialog, rowHeight, float, 0) // pixels
+ ATTRIB(Dialog, titleHeight, float, 0) // pixels
+ ATTRIB(Dialog, titleFontSize, float, 0) // pixels; if 0, title causes no margin
+ ATTRIB(Dialog, zoomedOutTitleBarPosition, float, 0)
+ ATTRIB(Dialog, zoomedOutTitleBar, float, 0)
+
+ ATTRIB(Dialog, requiresConnection, float, 0) // set to true if the dialog requires a connection to be opened
+
+ ATTRIB(Dialog, backgroundImage, string, string_null)
+ ATTRIB(Dialog, borderLines, float, 1)
+ ATTRIB(Dialog, closeButtonImage, string, string_null)
+
+ ATTRIB(Dialog, frame, entity, NULL)
+ENDCLASS(Dialog)
+#endif
+
+#ifdef IMPLEMENTATION
+void Dialog_Close(entity button, entity me)
+{
+ me.close(me);
+}
+
+void Dialog_fill(entity me)
+{
+}
+
+void Dialog_addItemSimple(entity me, float row, float col, float rowspan, float colspan, entity e, vector v)
+{
+ vector o, s;
+ o = me.itemOrigin + eX * ( col * me.itemSpacing.x) + eY * ( row * me.itemSpacing.y);
+ s = me.itemSize + eX * ((colspan - 1) * me.itemSpacing.x) + eY * ((rowspan - 1) * me.itemSpacing.y);
+ o.x -= 0.5 * (me.itemSpacing.x - me.itemSize.x) * v.x;
+ s.x += (me.itemSpacing.x - me.itemSize.x) * v.x;
+ o.y -= 0.5 * (me.itemSpacing.y - me.itemSize.y) * v.y;
+ s.y += (me.itemSpacing.y - me.itemSize.y) * v.y;
+ me.addItem(me, e, o, s, 1);
+}
+
+void Dialog_gotoRC(entity me, float row, float col)
+{
+ me.currentRow = row;
+ me.currentColumn = col;
+}
+
+void Dialog_TR(entity me)
+{
+ me.currentRow += 1;
+ me.currentColumn = me.firstColumn;
+}
+
+void Dialog_TD(entity me, float rowspan, float colspan, entity e)
+{
+ me.addItemSimple(me, me.currentRow, me.currentColumn, rowspan, colspan, e, '0 0 0');
+ me.currentColumn += colspan;
+}
+
+void Dialog_TDNoMargin(entity me, float rowspan, float colspan, entity e, vector v)
+{
+ me.addItemSimple(me, me.currentRow, me.currentColumn, rowspan, colspan, e, v);
+ me.currentColumn += colspan;
+}
+
+void Dialog_setFirstColumn(entity me, float col)
+{
+ me.firstColumn = col;
+}
+
+void Dialog_TDempty(entity me, float colspan)
+{
+ me.currentColumn += colspan;
+}
+
+void Dialog_configureDialog(entity me)
+{
+ float absWidth, absHeight;
+
+ me.frame = spawnBorderImage();
+ me.frame.configureBorderImage(me.frame, me.title, me.titleFontSize, me.color, me.backgroundImage, me.borderLines * me.titleHeight);
+ me.frame.zoomedOutTitleBarPosition = me.zoomedOutTitleBarPosition;
+ me.frame.zoomedOutTitleBar = me.zoomedOutTitleBar;
+ me.frame.alpha = me.alpha;
+ me.addItem(me, me.frame, '0 0 0', '1 1 0', 1);
+
+ if (!me.titleFontSize)
+ me.titleHeight = 0; // no title bar
+
+ absWidth = me.intendedWidth * conwidth;
+ absHeight = me.borderLines * me.titleHeight + me.marginTop + me.rows * me.rowHeight + (me.rows - 1) * me.rowSpacing + me.marginBottom;
+ me.itemOrigin = eX * (me.marginLeft / absWidth)
+ + eY * ((me.borderLines * me.titleHeight + me.marginTop) / absHeight);
+ me.itemSize = eX * ((1 - (me.marginLeft + me.marginRight + me.columnSpacing * (me.columns - 1)) / absWidth) / me.columns)
+ + eY * (me.rowHeight / absHeight);
+ me.itemSpacing = me.itemSize
+ + eX * (me.columnSpacing / absWidth)
+ + eY * (me.rowSpacing / absHeight);
+ me.intendedHeight = absHeight / conheight;
+ me.currentRow = -1;
+ me.currentColumn = -1;
+
+ me.fill(me);
+
+ if(me.closable && me.borderLines > 0)
+ {
+ entity closebutton;
+ closebutton = me.closeButton = me.frame.closeButton = spawnButton();
+ closebutton.configureButton(closebutton, "", 0, me.closeButtonImage);
+ closebutton.onClick = Dialog_Close; closebutton.onClickEntity = me;
+ closebutton.srcMulti = 0;
+ me.addItem(me, closebutton, '0 0 0', '1 1 0', 1); // put it as LAST
+ }
+}
+
+void Dialog_close(entity me)
+{
+ if(me.parent.instanceOfNexposee)
+ {
+ ExposeeCloseButton_Click(me, me.parent);
+ }
+ else if(me.parent.instanceOfModalController)
+ {
+ DialogCloseButton_Click(me, me);
+ }
+}
+
+float Dialog_keyDown(entity me, float key, float ascii, float shift)
+{
+ if(me.closable)
+ {
+ if(key == K_ESCAPE)
+ {
++ m_play_click_sound(MENU_SOUND_CLOSE);
+ me.close(me);
+ return 1;
+ }
+ }
+ return SUPER(Dialog).keyDown(me, key, ascii, shift);
+}
+#endif
--- /dev/null
- void InputBox_Clear_Click(entity btn, entity me);
+#ifdef INTERFACE
+CLASS(InputBox) EXTENDS(Label)
+ METHOD(InputBox, configureInputBox, void(entity, string, float, float, string))
+ METHOD(InputBox, draw, void(entity))
+ METHOD(InputBox, setText, void(entity, string))
+ METHOD(InputBox, enterText, void(entity, string))
+ METHOD(InputBox, keyDown, float(entity, float, float, float))
+ METHOD(InputBox, mouseMove, float(entity, vector))
+ METHOD(InputBox, mouseRelease, float(entity, vector))
+ METHOD(InputBox, mousePress, float(entity, vector))
+ METHOD(InputBox, mouseDrag, float(entity, vector))
+ METHOD(InputBox, showNotify, void(entity))
+ METHOD(InputBox, resizeNotify, void(entity, vector, vector, vector, vector))
+
+ ATTRIB(InputBox, src, string, string_null)
+
+ ATTRIB(InputBox, cursorPos, float, 0) // characters
+ ATTRIB(InputBox, scrollPos, float, 0) // widths
+
+ ATTRIB(InputBox, focusable, float, 1)
++ ATTRIB(InputBox, allowFocusSound, float, 1)
+ ATTRIB(InputBox, disabled, float, 0)
+ ATTRIB(InputBox, lastChangeTime, float, 0)
+ ATTRIB(InputBox, dragScrollTimer, float, 0)
+ ATTRIB(InputBox, dragScrollPos, vector, '0 0 0')
+ ATTRIB(InputBox, pressed, float, 0)
+ ATTRIB(InputBox, editColorCodes, float, 1)
+ ATTRIB(InputBox, forbiddenCharacters, string, "")
+ ATTRIB(InputBox, color, vector, '1 1 1')
+ ATTRIB(InputBox, colorF, vector, '1 1 1')
+ ATTRIB(InputBox, maxLength, float, 255) // if negative, it counts bytes, not chars
+
+ ATTRIB(InputBox, enableClearButton, float, 1)
+ ATTRIB(InputBox, clearButton, entity, NULL)
+ ATTRIB(InputBox, cb_width, float, 0)
+ ATTRIB(InputBox, cb_pressed, float, 0)
+ ATTRIB(InputBox, cb_focused, float, 0)
+ ATTRIB(InputBox, cb_color, vector, '1 1 1')
+ ATTRIB(InputBox, cb_colorF, vector, '1 1 1')
+ ATTRIB(InputBox, cb_colorC, vector, '1 1 1')
+ENDCLASS(InputBox)
- void InputBox_Clear_Click(entity btn, entity me)
- {
- me.setText(me, "");
- }
-
+#endif
+
+#ifdef IMPLEMENTATION
+void InputBox_configureInputBox(entity me, string theText, float theCursorPos, float theFontSize, string gfx)
+{
+ SUPER(InputBox).configureLabel(me, theText, theFontSize, 0.0);
+ me.src = gfx;
+ me.cursorPos = theCursorPos;
+}
+void InputBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ SUPER(InputBox).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
+ if (me.enableClearButton)
+ {
+ me.cb_width = absSize.y / absSize.x;
+ me.cb_offset = bound(-1, me.cb_offset, 0) * me.cb_width; // bound to range -1, 0
+ me.keepspaceRight = me.keepspaceRight - me.cb_offset + me.cb_width;
+ }
+}
+
+void InputBox_setText(entity me, string txt)
+{
+ if(me.text)
+ strunzone(me.text);
+ SUPER(InputBox).setText(me, strzone(txt));
+}
+
- InputBox_Clear_Click(world, me);
+float over_ClearButton(entity me, vector pos)
+{
+ if (pos.x >= 1 + me.cb_offset - me.cb_width)
+ if (pos.x < 1 + me.cb_offset)
+ if (pos.y >= 0)
+ if (pos.y < 1)
+ return 1;
+ return 0;
+}
+
+float InputBox_mouseMove(entity me, vector pos)
+{
+ if (me.enableClearButton)
+ {
+ if (over_ClearButton(me, pos))
+ {
+ me.cb_focused = 1;
+ return 1;
+ }
+ me.cb_focused = 0;
+ }
+ return 1;
+}
+
+float InputBox_mouseDrag(entity me, vector pos)
+{
+ float p;
+ if(me.pressed)
+ {
+ me.dragScrollPos = pos;
+ p = me.scrollPos + pos.x - me.keepspaceLeft;
+ me.cursorPos = draw_TextLengthUpToWidth(me.text, p, 0, me.realFontSize);
+ me.lastChangeTime = time;
+ }
+ else if (me.enableClearButton)
+ {
+ if (over_ClearButton(me, pos))
+ {
+ me.cb_pressed = 1;
+ return 1;
+ }
+ }
+ me.cb_pressed = 0;
+ return 1;
+}
+
+float InputBox_mousePress(entity me, vector pos)
+{
+ if (me.enableClearButton)
+ if (over_ClearButton(me, pos))
+ {
+ me.cb_pressed = 1;
+ return 1;
+ }
+ me.dragScrollTimer = time;
+ me.pressed = 1;
+ return InputBox_mouseDrag(me, pos);
+}
+
+float InputBox_mouseRelease(entity me, vector pos)
+{
+ if(me.cb_pressed)
+ if (over_ClearButton(me, pos))
+ {
++ m_play_click_sound(MENU_SOUND_CLEAR);
++ me.setText(me, "");
+ me.cb_pressed = 0;
+ return 1;
+ }
+ float r = InputBox_mouseDrag(me, pos);
+ //reset cb_pressed after mouseDrag, mouseDrag could set cb_pressed in this case:
+ //mouse press out of the clear button, drag and then mouse release over the clear button
+ me.cb_pressed = 0;
+ me.pressed = 0;
+ return r;
+}
+
+void InputBox_enterText(entity me, string ch)
+{
+ float i;
+ for(i = 0; i < strlen(ch); ++i)
+ if(strstrofs(me.forbiddenCharacters, substring(ch, i, 1), 0) > -1)
+ return;
+ if(me.maxLength > 0)
+ {
+ if(strlen(ch) + strlen(me.text) > me.maxLength)
+ return;
+ }
+ else if(me.maxLength < 0)
+ {
+ if(u8_strsize(ch) + u8_strsize(me.text) > -me.maxLength)
+ return;
+ }
+ me.setText(me, strcat(substring(me.text, 0, me.cursorPos), ch, substring(me.text, me.cursorPos, strlen(me.text) - me.cursorPos)));
+ me.cursorPos += strlen(ch);
+}
+
+float InputBox_keyDown(entity me, float key, float ascii, float shift)
+{
+ me.lastChangeTime = time;
+ me.dragScrollTimer = time;
+ if(ascii >= 32 && ascii != 127)
+ {
+ me.enterText(me, chr(ascii));
+ return 1;
+ }
+ switch(key)
+ {
+ case K_KP_LEFTARROW:
+ case K_LEFTARROW:
+ me.cursorPos -= 1;
+ return 1;
+ case K_KP_RIGHTARROW:
+ case K_RIGHTARROW:
+ me.cursorPos += 1;
+ return 1;
+ case K_KP_HOME:
+ case K_HOME:
+ me.cursorPos = 0;
+ return 1;
+ case K_KP_END:
+ case K_END:
+ me.cursorPos = strlen(me.text);
+ return 1;
+ case K_BACKSPACE:
+ if(me.cursorPos > 0)
+ {
+ me.cursorPos -= 1;
+ me.setText(me, strcat(substring(me.text, 0, me.cursorPos), substring(me.text, me.cursorPos + 1, strlen(me.text) - me.cursorPos - 1)));
+ }
+ return 1;
+ case K_KP_DEL:
+ case K_DEL:
+ if(shift & S_CTRL)
++ {
++ m_play_click_sound(MENU_SOUND_CLEAR);
+ me.setText(me, "");
++ }
+ else
+ me.setText(me, strcat(substring(me.text, 0, me.cursorPos), substring(me.text, me.cursorPos + 1, strlen(me.text) - me.cursorPos - 1)));
+ return 1;
+ }
+ return 0;
+}
+
+void InputBox_draw(entity me)
+{
+ string CURSOR = "_";
+ float cursorPosInWidths, totalSizeInWidths;
+
+ if(me.pressed)
+ me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event
+
+ if(me.recalcPos)
+ me.recalcPositionWithText(me, me.text);
+
+ me.focusable = !me.disabled;
+ if(me.disabled)
+ draw_alpha *= me.disabledAlpha;
+
+ if(me.src)
+ {
+ if(me.focused && !me.disabled)
+ draw_ButtonPicture('0 0 0', strcat(me.src, "_f"), '1 1 0', me.colorF, 1);
+ else
+ draw_ButtonPicture('0 0 0', strcat(me.src, "_n"), '1 1 0', me.color, 1);
+ }
+
+ me.cursorPos = bound(0, me.cursorPos, strlen(me.text));
+ cursorPosInWidths = draw_TextWidth(substring(me.text, 0, me.cursorPos), 0, me.realFontSize);
+ totalSizeInWidths = draw_TextWidth(strcat(me.text, CURSOR), 0, me.realFontSize);
+
+ if(me.dragScrollTimer < time)
+ {
+ float save;
+ save = me.scrollPos;
+ me.scrollPos = bound(cursorPosInWidths - (0.875 - me.keepspaceLeft - me.keepspaceRight), me.scrollPos, cursorPosInWidths - 0.125);
+ if(me.scrollPos != save)
+ me.dragScrollTimer = time + 0.2;
+ }
+ me.scrollPos = min(me.scrollPos, totalSizeInWidths - (1 - me.keepspaceRight - me.keepspaceLeft));
+ me.scrollPos = max(0, me.scrollPos);
+
+ draw_SetClipRect(eX * me.keepspaceLeft, eX * (1 - me.keepspaceLeft - me.keepspaceRight) + eY);
+ if(me.editColorCodes)
+ {
+ string ch, ch2;
+ float i, n;
+ vector theColor;
+ float theAlpha; //float theVariableAlpha;
+ vector p;
+ vector theTempColor;
+ float component;
+
+ p = me.realOrigin - eX * me.scrollPos;
+ theColor = '1 1 1';
+ theAlpha = 1; //theVariableAlpha = 1; // changes when ^ax found
+
+ n = strlen(me.text);
+ for(i = 0; i < n; ++i)
+ {
+ ch = substring(me.text, i, 1);
+ if(ch == "^")
+ {
+ float w;
+ ch2 = substring(me.text, i+1, 1);
+ w = draw_TextWidth(strcat(ch, ch2), 0, me.realFontSize);
+ if(ch2 == "^")
+ {
+ draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5);
+ draw_Text(p + eX * 0.25 * w, "^", me.realFontSize, theColor, theAlpha, 0);
+ }
+ else if(ch2 == "0" || stof(ch2)) // digit?
+ {
+ switch(stof(ch2))
+ {
+ case 0: theColor = '0 0 0'; theAlpha = 1; break;
+ case 1: theColor = '1 0 0'; theAlpha = 1; break;
+ case 2: theColor = '0 1 0'; theAlpha = 1; break;
+ case 3: theColor = '1 1 0'; theAlpha = 1; break;
+ case 4: theColor = '0 0 1'; theAlpha = 1; break;
+ case 5: theColor = '0 1 1'; theAlpha = 1; break;
+ case 6: theColor = '1 0 1'; theAlpha = 1; break;
+ case 7: theColor = '1 1 1'; theAlpha = 1; break;
+ case 8: theColor = '1 1 1'; theAlpha = 0.5; break;
+ case 9: theColor = '0.5 0.5 0.5'; theAlpha = 1; break;
+ }
+ draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5);
+ draw_Text(p, strcat(ch, ch2), me.realFontSize, theColor, theAlpha, 0);
+ }
+ else if(ch2 == "x") // ^x found
+ {
+ theColor = '1 1 1';
+
+ component = HEXDIGIT_TO_DEC(substring(me.text, i+2, 1));
+ if (component >= 0) // ^xr found
+ {
+ theTempColor.x = component/15;
+
+ component = HEXDIGIT_TO_DEC(substring(me.text, i+3, 1));
+ if (component >= 0) // ^xrg found
+ {
+ theTempColor.y = component/15;
+
+ component = HEXDIGIT_TO_DEC(substring(me.text, i+4, 1));
+ if (component >= 0) // ^xrgb found
+ {
+ theTempColor.z = component/15;
+ theColor = theTempColor;
+ w = draw_TextWidth(substring(me.text, i, 5), 0, me.realFontSize);
+
+ draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5);
+ draw_Text(p, substring(me.text, i, 5), me.realFontSize, theColor, 1, 0); // theVariableAlpha instead of 1 using alpha tags ^ax
+ i += 3;
+ }
+ else
+ {
+ // blue missing
+ w = draw_TextWidth(substring(me.text, i, 4), 0, me.realFontSize);
+ draw_Fill(p, eX * w + eY * me.realFontSize.y, eZ, 0.5);
+ draw_Text(p, substring(me.text, i, 4), me.realFontSize, '1 1 1', theAlpha, 0);
+ i += 2;
+ }
+ }
+ else
+ {
+ // green missing
+ w = draw_TextWidth(substring(me.text, i, 3), 0, me.realFontSize);
+ draw_Fill(p, eX * w + eY * me.realFontSize.y, eY, 0.5);
+ draw_Text(p, substring(me.text, i, 3), me.realFontSize, '1 1 1', theAlpha, 0);
+ i += 1;
+ }
+ }
+ else
+ {
+ // red missing
+ //w = draw_TextWidth(substring(me.text, i, 2), 0) * me.realFontSize_x;
+ draw_Fill(p, eX * w + eY * me.realFontSize.y, eX, 0.5);
+ draw_Text(p, substring(me.text, i, 2), me.realFontSize, '1 1 1', theAlpha, 0);
+ }
+ }
+ else
+ {
+ draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5);
+ draw_Text(p, strcat(ch, ch2), me.realFontSize, theColor, theAlpha, 0);
+ }
+ p += w * eX;
+ ++i;
+ continue;
+ }
+ draw_Text(p, ch, me.realFontSize, theColor, theAlpha, 0); // TODO theVariableAlpha
+ p += eX * draw_TextWidth(ch, 0, me.realFontSize);
+ }
+ }
+ else
+ draw_Text(me.realOrigin - eX * me.scrollPos, me.text, me.realFontSize, '1 1 1', 1, 0);
+
+ if(!me.focused || (time - me.lastChangeTime) < floor(time - me.lastChangeTime) + 0.5)
+ draw_Text(me.realOrigin + eX * (cursorPosInWidths - me.scrollPos), CURSOR, me.realFontSize, '1 1 1', 1, 0);
+
+ draw_ClearClip();
+
+ if (me.enableClearButton)
+ if (me.text != "")
+ {
+ if(me.focused && me.cb_pressed)
+ draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_c"), eX * me.cb_width + eY, me.cb_colorC, 1);
+ else if(me.focused && me.cb_focused)
+ draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_f"), eX * me.cb_width + eY, me.cb_colorF, 1);
+ else
+ draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_n"), eX * me.cb_width + eY, me.cb_color, 1);
+ }
+
+ // skipping SUPER(InputBox).draw(me);
+ Item_draw(me);
+}
+
+void InputBox_showNotify(entity me)
+{
+ me.focusable = !me.disabled;
+}
+#endif
--- /dev/null
+#ifdef INTERFACE
+CLASS(ListBox) EXTENDS(Item)
+ METHOD(ListBox, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(ListBox, configureListBox, void(entity, float, float))
+ METHOD(ListBox, draw, void(entity))
+ METHOD(ListBox, keyDown, float(entity, float, float, float))
+ METHOD(ListBox, mousePress, float(entity, vector))
+ METHOD(ListBox, mouseDrag, float(entity, vector))
+ METHOD(ListBox, mouseRelease, float(entity, vector))
+ METHOD(ListBox, focusLeave, void(entity))
+ ATTRIB(ListBox, focusable, float, 1)
++ ATTRIB(ListBox, allowFocusSound, float, 1)
+ ATTRIB(ListBox, selectedItem, float, 0)
+ ATTRIB(ListBox, size, vector, '0 0 0')
+ ATTRIB(ListBox, origin, vector, '0 0 0')
+ ATTRIB(ListBox, scrollPos, float, 0) // measured in window heights, fixed when needed
+ ATTRIB(ListBox, previousValue, float, 0)
+ ATTRIB(ListBox, pressed, float, 0) // 0 = normal, 1 = scrollbar dragging, 2 = item dragging, 3 = released
+ ATTRIB(ListBox, pressOffset, float, 0)
+
+ METHOD(ListBox, updateControlTopBottom, void(entity))
+ ATTRIB(ListBox, controlTop, float, 0)
+ ATTRIB(ListBox, controlBottom, float, 0)
+ ATTRIB(ListBox, controlWidth, float, 0)
+ ATTRIB(ListBox, dragScrollTimer, float, 0)
+ ATTRIB(ListBox, dragScrollPos, vector, '0 0 0')
+
+ ATTRIB(ListBox, src, string, string_null) // scrollbar
+ ATTRIB(ListBox, color, vector, '1 1 1')
+ ATTRIB(ListBox, color2, vector, '1 1 1')
+ ATTRIB(ListBox, colorC, vector, '1 1 1')
+ ATTRIB(ListBox, colorF, vector, '1 1 1')
+ ATTRIB(ListBox, tolerance, vector, '0 0 0') // drag tolerance
+ ATTRIB(ListBox, scrollbarWidth, float, 0) // pixels
+ ATTRIB(ListBox, nItems, float, 42)
+ ATTRIB(ListBox, itemHeight, float, 0)
+ ATTRIB(ListBox, colorBG, vector, '0 0 0')
+ ATTRIB(ListBox, alphaBG, float, 0)
+
+ ATTRIB(ListBox, lastClickedItem, float, -1)
+ ATTRIB(ListBox, lastClickedTime, float, 0)
+
+ METHOD(ListBox, drawListBoxItem, void(entity, float, vector, float)) // item number, width/height, selected
+ METHOD(ListBox, clickListBoxItem, void(entity, float, vector)) // item number, relative clickpos
+ METHOD(ListBox, doubleClickListBoxItem, void(entity, float, vector)) // item number, relative clickpos
+ METHOD(ListBox, setSelected, void(entity, float))
+
+ METHOD(ListBox, getLastFullyVisibleItemAtScrollPos, float(entity, float))
+ METHOD(ListBox, getFirstFullyVisibleItemAtScrollPos, float(entity, float))
+
+ // NOTE: override these four methods if you want variable sized list items
+ METHOD(ListBox, getTotalHeight, float(entity))
+ METHOD(ListBox, getItemAtPos, float(entity, float))
+ METHOD(ListBox, getItemStart, float(entity, float))
+ METHOD(ListBox, getItemHeight, float(entity, float))
+ // NOTE: if getItemAt* are overridden, it may make sense to cache the
+ // start and height of the last item returned by getItemAtPos and fast
+ // track returning their properties for getItemStart and getItemHeight.
+ // The "hot" code path calls getItemAtPos first, then will query
+ // getItemStart and getItemHeight on it soon.
+ // When overriding, the following consistency rules must hold:
+ // getTotalHeight() == SUM(getItemHeight(i), i, 0, me.nItems-1)
+ // getItemStart(i+1) == getItemStart(i) + getItemHeight(i)
+ // for 0 <= i < me.nItems-1
+ // getItemStart(0) == 0
+ // getItemStart(getItemAtPos(p)) <= p
+ // if p >= 0
+ // getItemAtPos(p) == 0
+ // if p < 0
+ // getItemStart(getItemAtPos(p)) + getItemHeight(getItemAtPos(p)) > p
+ // if p < getTotalHeigt()
+ // getItemAtPos(p) == me.nItems - 1
+ // if p >= getTotalHeight()
+ENDCLASS(ListBox)
+#endif
+
+#ifdef IMPLEMENTATION
+void ListBox_setSelected(entity me, float i)
+{
+ me.selectedItem = bound(0, i, me.nItems - 1);
+}
+void ListBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ SUPER(ListBox).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
+ me.controlWidth = me.scrollbarWidth / absSize.x;
+}
+void ListBox_configureListBox(entity me, float theScrollbarWidth, float theItemHeight)
+{
+ me.scrollbarWidth = theScrollbarWidth;
+ me.itemHeight = theItemHeight;
+}
+
+float ListBox_getTotalHeight(entity me)
+{
+ return me.nItems * me.itemHeight;
+}
+float ListBox_getItemAtPos(entity me, float pos)
+{
+ return floor(pos / me.itemHeight);
+}
+float ListBox_getItemStart(entity me, float i)
+{
+ return me.itemHeight * i;
+}
+float ListBox_getItemHeight(entity me, float i)
+{
+ return me.itemHeight;
+}
+
+float ListBox_getLastFullyVisibleItemAtScrollPos(entity me, float pos)
+{
+ return me.getItemAtPos(me, pos + 1.001) - 1;
+}
+float ListBox_getFirstFullyVisibleItemAtScrollPos(entity me, float pos)
+{
+ return me.getItemAtPos(me, pos - 0.001) + 1;
+}
+float ListBox_keyDown(entity me, float key, float ascii, float shift)
+{
+ me.dragScrollTimer = time;
+ if(key == K_MWHEELUP)
+ {
+ me.scrollPos = max(me.scrollPos - 0.5, 0);
+ me.setSelected(me, min(me.selectedItem, me.getLastFullyVisibleItemAtScrollPos(me, me.scrollPos)));
+ }
+ else if(key == K_MWHEELDOWN)
+ {
+ me.scrollPos = min(me.scrollPos + 0.5, me.getTotalHeight(me) - 1);
+ me.setSelected(me, max(me.selectedItem, me.getFirstFullyVisibleItemAtScrollPos(me, me.scrollPos)));
+ }
+ else if(key == K_PGUP || key == K_KP_PGUP)
+ {
+ float i = me.selectedItem;
+ float a = me.getItemHeight(me, i);
+ for (;;)
+ {
+ --i;
+ if (i < 0)
+ break;
+ a += me.getItemHeight(me, i);
+ if (a >= 1)
+ break;
+ }
+ me.setSelected(me, i + 1);
+ }
+ else if(key == K_PGDN || key == K_KP_PGDN)
+ {
+ float i = me.selectedItem;
+ float a = me.getItemHeight(me, i);
+ for (;;)
+ {
+ ++i;
+ if (i >= me.nItems)
+ break;
+ a += me.getItemHeight(me, i);
+ if (a >= 1)
+ break;
+ }
+ me.setSelected(me, i - 1);
+ }
+ else if(key == K_UPARROW || key == K_KP_UPARROW)
+ me.setSelected(me, me.selectedItem - 1);
+ else if(key == K_DOWNARROW || key == K_KP_DOWNARROW)
+ me.setSelected(me, me.selectedItem + 1);
+ else if(key == K_HOME || key == K_KP_HOME)
+ {
+ me.scrollPos = 0;
+ me.setSelected(me, 0);
+ }
+ else if(key == K_END || key == K_KP_END)
+ {
+ me.scrollPos = max(0, me.getTotalHeight(me) - 1);
+ me.setSelected(me, me.nItems - 1);
+ }
+ else
+ return 0;
+ return 1;
+}
+float ListBox_mouseDrag(entity me, vector pos)
+{
+ float hit;
+ float i;
+ me.updateControlTopBottom(me);
+ me.dragScrollPos = pos;
+ if(me.pressed == 1)
+ {
+ hit = 1;
+ if(pos.x < 1 - me.controlWidth - me.tolerance.y * me.controlWidth) hit = 0;
+ if(pos.y < 0 - me.tolerance.x) hit = 0;
+ if(pos.x >= 1 + me.tolerance.y * me.controlWidth) hit = 0;
+ if(pos.y >= 1 + me.tolerance.x) hit = 0;
+ if(hit)
+ {
+ // calculate new pos to v
+ float d;
+ d = (pos.y - me.pressOffset) / (1 - (me.controlBottom - me.controlTop)) * (me.getTotalHeight(me) - 1);
+ me.scrollPos = me.previousValue + d;
+ }
+ else
+ me.scrollPos = me.previousValue;
+ me.scrollPos = min(me.scrollPos, me.getTotalHeight(me) - 1);
+ me.scrollPos = max(me.scrollPos, 0);
+ i = min(me.selectedItem, me.getLastFullyVisibleItemAtScrollPos(me, me.scrollPos));
+ i = max(i, ListBox_getFirstFullyVisibleItemAtScrollPos(me, me.scrollPos));
+ me.setSelected(me, i);
+ }
+ else if(me.pressed == 2)
+ {
+ me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos.y));
+ }
+ return 1;
+}
+float ListBox_mousePress(entity me, vector pos)
+{
+ if(pos.x < 0) return 0;
+ if(pos.y < 0) return 0;
+ if(pos.x >= 1) return 0;
+ if(pos.y >= 1) return 0;
+ me.dragScrollPos = pos;
+ me.updateControlTopBottom(me);
+ me.dragScrollTimer = time;
+ if(pos.x >= 1 - me.controlWidth)
+ {
+ // if hit, set me.pressed, otherwise scroll by one page
+ if(pos.y < me.controlTop)
+ {
+ // page up
+ me.scrollPos = max(me.scrollPos - 1, 0);
+ me.setSelected(me, min(me.selectedItem, ListBox_getLastFullyVisibleItemAtScrollPos(me, me.scrollPos)));
+ }
+ else if(pos.y > me.controlBottom)
+ {
+ // page down
+ me.scrollPos = min(me.scrollPos + 1, me.getTotalHeight(me) - 1);
+ me.setSelected(me, max(me.selectedItem, ListBox_getFirstFullyVisibleItemAtScrollPos(me, me.scrollPos)));
+ }
+ else
+ {
+ me.pressed = 1;
+ me.pressOffset = pos.y;
+ me.previousValue = me.scrollPos;
+ }
+ }
+ else
+ {
+ // continue doing that while dragging (even when dragging outside). When releasing, forward the click to the then selected item.
+ me.pressed = 2;
+ // an item has been clicked. Select it, ...
+ me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos.y));
+ }
+ return 1;
+}
+float ListBox_mouseRelease(entity me, vector pos)
+{
+ if(me.pressed == 1)
+ {
+ // slider dragging mode
+ // in that case, nothing happens on releasing
+ }
+ else if(me.pressed == 2)
+ {
+ me.pressed = 3; // do that here, so setSelected can know the mouse has been released
+ // item dragging mode
+ // select current one one last time...
+ me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos.y));
+ // and give it a nice click event
+ if(me.nItems > 0)
+ {
+ vector where = globalToBox(pos, eY * (me.getItemStart(me, me.selectedItem) - me.scrollPos), eX * (1 - me.controlWidth) + eY * me.getItemHeight(me, me.selectedItem));
+
+ if((me.selectedItem == me.lastClickedItem) && (time < me.lastClickedTime + 0.3))
+ me.doubleClickListBoxItem(me, me.selectedItem, where);
+ else
+ me.clickListBoxItem(me, me.selectedItem, where);
+
+ me.lastClickedItem = me.selectedItem;
+ me.lastClickedTime = time;
+ }
+ }
+ me.pressed = 0;
+ return 1;
+}
+void ListBox_focusLeave(entity me)
+{
+ // Reset the var pressed in case listbox loses focus
+ // by a mouse click on an item of the list
+ // for example showing a dialog on right click
+ me.pressed = 0;
+}
+void ListBox_updateControlTopBottom(entity me)
+{
+ float f;
+ // scrollPos is in 0..1 and indicates where the "page" currently shown starts.
+ if(me.getTotalHeight(me) <= 1)
+ {
+ // we don't need no stinkin' scrollbar, we don't need no view control...
+ me.controlTop = 0;
+ me.controlBottom = 1;
+ me.scrollPos = 0;
+ }
+ else
+ {
+ if(frametime) // only do this in draw frames
+ {
+ if(me.dragScrollTimer < time)
+ {
+ float save;
+ save = me.scrollPos;
+ // if selected item is below listbox, increase scrollpos so it is in
+ me.scrollPos = max(me.scrollPos, me.getItemStart(me, me.selectedItem) + me.getItemHeight(me, me.selectedItem) - 1);
+ // if selected item is above listbox, decrease scrollpos so it is in
+ me.scrollPos = min(me.scrollPos, me.getItemStart(me, me.selectedItem));
+ if(me.scrollPos != save)
+ me.dragScrollTimer = time + 0.2;
+ }
+ }
+ // if scroll pos is below end of list, fix it
+ me.scrollPos = min(me.scrollPos, me.getTotalHeight(me) - 1);
+ // if scroll pos is above beginning of list, fix it
+ me.scrollPos = max(me.scrollPos, 0);
+ // now that we know where the list is scrolled to, find out where to draw the control
+ me.controlTop = max(0, me.scrollPos / me.getTotalHeight(me));
+ me.controlBottom = min((me.scrollPos + 1) / me.getTotalHeight(me), 1);
+
+ float minfactor;
+ minfactor = 2 * me.controlWidth / me.size.y * me.size.x;
+ f = me.controlBottom - me.controlTop;
+ if(f < minfactor) // FIXME good default?
+ {
+ // f * X + 1 * (1-X) = minfactor
+ // (f - 1) * X + 1 = minfactor
+ // (f - 1) * X = minfactor - 1
+ // X = (minfactor - 1) / (f - 1)
+ f = (minfactor - 1) / (f - 1);
+ me.controlTop = me.controlTop * f + 0 * (1 - f);
+ me.controlBottom = me.controlBottom * f + 1 * (1 - f);
+ }
+ }
+}
+void ListBox_draw(entity me)
+{
+ float i;
+ vector absSize, fillSize = '0 0 0';
+ vector oldshift, oldscale;
+ if(me.pressed == 2)
+ me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event
+ me.updateControlTopBottom(me);
+ fillSize.x = (1 - me.controlWidth);
+ if(me.alphaBG)
+ draw_Fill('0 0 0', '0 1 0' + fillSize, me.colorBG, me.alphaBG);
+ if(me.controlWidth)
+ {
+ draw_VertButtonPicture(eX * (1 - me.controlWidth), strcat(me.src, "_s"), eX * me.controlWidth + eY, me.color2, 1);
+ if(me.getTotalHeight(me) > 1)
+ {
+ vector o, s;
+ o = eX * (1 - me.controlWidth) + eY * me.controlTop;
+ s = eX * me.controlWidth + eY * (me.controlBottom - me.controlTop);
+ if(me.pressed == 1)
+ draw_VertButtonPicture(o, strcat(me.src, "_c"), s, me.colorC, 1);
+ else if(me.focused)
+ draw_VertButtonPicture(o, strcat(me.src, "_f"), s, me.colorF, 1);
+ else
+ draw_VertButtonPicture(o, strcat(me.src, "_n"), s, me.color, 1);
+ }
+ }
+ draw_SetClip();
+ oldshift = draw_shift;
+ oldscale = draw_scale;
+ float y;
+ i = me.getItemAtPos(me, me.scrollPos);
+ y = me.getItemStart(me, i) - me.scrollPos;
+ for (; i < me.nItems && y < 1; ++i)
+ {
+ draw_shift = boxToGlobal(eY * y, oldshift, oldscale);
+ vector relSize = eX * (1 - me.controlWidth) + eY * me.getItemHeight(me, i);
+ absSize = boxToGlobalSize(relSize, me.size);
+ draw_scale = boxToGlobalSize(relSize, oldscale);
+ me.drawListBoxItem(me, i, absSize, (me.selectedItem == i));
+ y += relSize.y;
+ }
+ draw_ClearClip();
+
+ draw_shift = oldshift;
+ draw_scale = oldscale;
+ SUPER(ListBox).draw(me);
+}
+
+void ListBox_clickListBoxItem(entity me, float i, vector where)
+{
+ // template method
+}
+
+void ListBox_doubleClickListBoxItem(entity me, float i, vector where)
+{
+ // template method
+}
+
+void ListBox_drawListBoxItem(entity me, float i, vector absSize, float selected)
+{
+ draw_Text('0 0 0', sprintf(_("Item %d"), i), eX * (8 / absSize.x) + eY * (8 / absSize.y), (selected ? '0 1 0' : '1 1 1'), 1, 0);
+}
+#endif
--- /dev/null
- f = (e.ModalController_factor = min(1, e.ModalController_factor + df));
- if(e.ModalController_state)
- if(f < 1)
- animating = 1;
-
- if(f < 1)
- {
- prevFactor = (1 - f) / (1 - f + df);
- targetFactor = df / (1 - f + df);
- }
- else
- {
- prevFactor = 0;
- targetFactor = 1;
- }
-
+#ifdef INTERFACE
+CLASS(ModalController) EXTENDS(Container)
+ METHOD(ModalController, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(ModalController, draw, void(entity))
+ METHOD(ModalController, showChild, void(entity, entity, vector, vector, float))
+ METHOD(ModalController, hideChild, void(entity, entity, float))
+ METHOD(ModalController, hideAll, void(entity, float))
+ METHOD(ModalController, addItem, void(entity, entity, vector, vector, float))
+ METHOD(ModalController, addTab, void(entity, entity, entity))
+
+ METHOD(ModalController, initializeDialog, void(entity, entity))
+
+ METHOD(ModalController, switchState, void(entity, entity, float, float))
+ ATTRIB(ModalController, origin, vector, '0 0 0')
+ ATTRIB(ModalController, size, vector, '0 0 0')
+ ATTRIB(ModalController, previousButton, entity, NULL)
+ ATTRIB(ModalController, fadedAlpha, float, 0.3)
+ENDCLASS(ModalController)
+
+.entity tabSelectingButton;
+.vector origin;
+.vector size;
+void TabButton_Click(entity button, entity tab); // assumes a button has set the above fields to its own absolute origin, its size, and the tab to activate
+void DialogOpenButton_Click(entity button, entity tab); // assumes a button has set the above fields to its own absolute origin, its size, and the tab to activate
+void DialogOpenButton_Click_withCoords(entity button, entity tab, vector theOrigin, vector theSize);
+void DialogCloseButton_Click(entity button, entity tab); // assumes a button has set the above fields to the tab to close
+#endif
+
+#ifdef IMPLEMENTATION
+
+// modal dialog controller
+// handles a stack of dialog elements
+// each element can have one of the following states:
+// 0: hidden (fading out)
+// 1: visible (zooming in)
+// 2: greyed out (inactive)
+// While an animation is running, no item has focus. When an animation is done,
+// the topmost item gets focus.
+// The items are assumed to be added in overlapping order, that is, the lowest
+// window must get added first.
+//
+// Possible uses:
+// - to control a modal dialog:
+// - show modal dialog: me.showChild(me, childItem, buttonAbsOrigin, buttonAbsSize, 0) // childItem also gets focus
+// - dismiss modal dialog: me.hideChild(me, childItem, 0) // childItem fades out and relinquishes focus
+// - show first screen in m_show: me.hideAll(me, 1); me.showChild(me, me.firstChild, '0 0 0', '0 0 0', 1);
+// - to show a temporary dialog instead of the menu (teamselect): me.hideAll(me, 1); me.showChild(me, teamSelectDialog, '0 0 0', '0 0 0', 1);
+// - as a tabbed dialog control:
+// - to initialize: me.hideAll(me, 1); me.showChild(me, me.firstChild, '0 0 0', '0 0 0', 1);
+// - to show a tab: me.hideChild(me, currentTab, 0); me.showChild(me, newTab, buttonAbsOrigin, buttonAbsSize, 0);
+
+.vector ModalController_initialSize;
+.vector ModalController_initialOrigin;
+.vector ModalController_initialFontScale;
+.float ModalController_initialAlpha;
+.vector ModalController_buttonSize;
+.vector ModalController_buttonOrigin;
+.float ModalController_state;
+.float ModalController_factor;
+.entity ModalController_controllingButton;
+
+void ModalController_initializeDialog(entity me, entity root)
+{
+ me.hideAll(me, 1);
+ me.showChild(me, root, '0 0 0', '0 0 0', 1); // someone else animates for us
+}
+
+void TabButton_Click(entity button, entity tab)
+{
+ if(tab.ModalController_state == 1)
+ return;
+ tab.parent.hideAll(tab.parent, 0);
+ button.forcePressed = 1;
+ tab.ModalController_controllingButton = button;
+ tab.parent.showChild(tab.parent, tab, button.origin, button.size, 0);
+}
+
+void DialogOpenButton_Click(entity button, entity tab)
+{
+ DialogOpenButton_Click_withCoords(button, tab, button.origin, button.size);
+}
+
+void DialogOpenButton_Click_withCoords(entity button, entity tab, vector theOrigin, vector theSize)
+{
+ if(tab.ModalController_state)
+ return;
+ if(button)
+ button.forcePressed = 1;
+ if(tab.parent.focusedChild)
+ tab.parent.focusedChild.saveFocus(tab.parent.focusedChild);
+ tab.ModalController_controllingButton = button;
+ tab.parent.showChild(tab.parent, tab, theOrigin, theSize, 0);
+}
+
+void DialogCloseButton_Click(entity button, entity tab)
+{
+ tab.parent.hideChild(tab.parent, tab, 0);
+}
+
+void ModalController_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ me.resizeNotifyLie(me, relOrigin, relSize, absOrigin, absSize, ModalController_initialOrigin, ModalController_initialSize, ModalController_initialFontScale);
+}
+
+void ModalController_switchState(entity me, entity other, float state, float skipAnimation)
+{
+ float previousState;
+ previousState = other.ModalController_state;
+ if(state == previousState && !skipAnimation)
+ return;
+ other.ModalController_state = state;
+ switch(state)
+ {
+ case 0:
+ other.ModalController_factor = 1 - other.Container_alpha / other.ModalController_initialAlpha;
+ // fading out
+ break;
+ case 1:
+ other.ModalController_factor = other.Container_alpha / other.ModalController_initialAlpha;
+ if(previousState == 0 && !skipAnimation)
+ {
+ other.Container_origin = other.ModalController_buttonOrigin;
+ other.Container_size = other.ModalController_buttonSize;
+ }
+ // zooming in
+ break;
+ case 2:
+ other.ModalController_factor = bound(0, (1 - other.Container_alpha / other.ModalController_initialAlpha) / me.fadedAlpha, 1);
+ // fading out halfway
+ break;
+ }
+ if(skipAnimation)
+ other.ModalController_factor = 1;
+}
+
+void ModalController_draw(entity me)
+{
+ entity e;
+ entity front;
+ float animating;
+ float f; // animation factor
+ float df; // animation step size
+ float prevFactor, targetFactor;
+ vector targetOrigin, targetSize; float targetAlpha;
+ vector fs;
+ animating = 0;
+
+ front = world;
+ for(e = me.firstChild; e; e = e.nextSibling)
+ if(e.ModalController_state)
+ {
+ if(front)
++ {
+ me.switchState(me, front, 2, 0);
++ if(front.ModalController_factor < 1)
++ animating = 1;
++ }
+ front = e;
+ }
+ if(front)
++ {
+ me.switchState(me, front, 1, 0);
++ if(front.ModalController_factor < 1)
++ animating = 1;
++ }
++
++ if(front && front.Container_alpha == front.ModalController_initialAlpha)
++ goto update_done; // update isn't needed, everything stay as is
+
+ df = frametime * 3; // animation speed
+
+ for(e = me.firstChild; e; e = e.nextSibling)
+ {
- if(f < 1)
- animating = 1;
+ if(e.ModalController_state == 2)
+ {
+ // fading out partially
+ targetOrigin = e.Container_origin; // stay as is
+ targetSize = e.Container_size; // stay as is
+ targetAlpha = me.fadedAlpha * e.ModalController_initialAlpha;
+ }
+ else if(e.ModalController_state == 1)
+ {
+ // zooming in
+ targetOrigin = e.ModalController_initialOrigin;
+ targetSize = e.ModalController_initialSize;
+ targetAlpha = e.ModalController_initialAlpha;
+ }
+ else
+ {
+ // fading out
- e.Container_origin = e.Container_origin * prevFactor + targetOrigin * targetFactor;
- e.Container_size = e.Container_size * prevFactor + targetSize * targetFactor;
- me.setAlphaOf(me, e, e.Container_alpha * prevFactor + targetAlpha * targetFactor);
+ targetOrigin = e.Container_origin; // stay as is
+ targetSize = e.Container_size; // stay as is
+ targetAlpha = 0;
+ }
+
++ f = (e.ModalController_factor = min(1, e.ModalController_factor + df));
+ if(f == 1)
+ {
++ prevFactor = 0;
++ targetFactor = 1;
+ e.Container_origin = targetOrigin;
+ e.Container_size = targetSize;
+ me.setAlphaOf(me, e, targetAlpha);
+ }
+ else
+ {
- fs = globalToBoxSize(e.Container_size, e.ModalController_initialSize);
- e.Container_fontscale_x = fs.x * e.ModalController_initialFontScale.x;
- e.Container_fontscale_y = fs.y * e.ModalController_initialFontScale.y;
++ prevFactor = (1 - f) / (1 - f + df);
++ if(!e.ModalController_state) // optimize code and avoid precision errors
++ me.setAlphaOf(me, e, e.Container_alpha * prevFactor);
++ else
++ {
++ targetFactor = df / (1 - f + df);
++
++ if(e.ModalController_state == 1)
++ {
++ e.Container_origin = e.Container_origin * prevFactor + targetOrigin * targetFactor;
++ e.Container_size = e.Container_size * prevFactor + targetSize * targetFactor;
++ }
++ me.setAlphaOf(me, e, e.Container_alpha * prevFactor + targetAlpha * targetFactor);
++ }
+ }
+ // assume: o == to * f_prev + X * (1 - f_prev)
+ // make: o' = to * f + X * (1 - f)
+ // -->
+ // X == (o - to * f_prev) / (1 - f_prev)
+ // o' = to * f + (o - to * f_prev) / (1 - f_prev) * (1 - f)
+ // --> (maxima)
+ // o' = (to * (f - f_prev) + o * (1 - f)) / (1 - f_prev)
+
++ if(e.ModalController_state == 1)
++ {
++ fs = globalToBoxSize(e.Container_size, e.ModalController_initialSize);
++ e.Container_fontscale_x = fs.x * e.ModalController_initialFontScale.x;
++ e.Container_fontscale_y = fs.y * e.ModalController_initialFontScale.y;
++ }
+ }
++ :update_done
++
+ if(animating || !me.focused)
+ me.setFocus(me, NULL);
+ else
+ me.setFocus(me, front);
+ SUPER(ModalController).draw(me);
+}
+
+void ModalController_addTab(entity me, entity other, entity tabButton)
+{
+ me.addItem(me, other, '0 0 0', '1 1 1', 1);
+ tabButton.onClick = TabButton_Click;
+ tabButton.onClickEntity = other;
+ other.tabSelectingButton = tabButton;
+ if(other == me.firstChild)
+ {
+ tabButton.forcePressed = 1;
+ other.ModalController_controllingButton = tabButton;
+ me.showChild(me, other, '0 0 0', '0 0 0', 1);
+ }
+}
+
+void ModalController_addItem(entity me, entity other, vector theOrigin, vector theSize, float theAlpha)
+{
+ SUPER(ModalController).addItem(me, other, theOrigin, theSize, (other == me.firstChild) ? theAlpha : 0);
+ other.ModalController_initialFontScale = other.Container_fontscale;
+ other.ModalController_initialSize = other.Container_size;
+ other.ModalController_initialOrigin = other.Container_origin;
+ other.ModalController_initialAlpha = theAlpha; // hope Container never modifies this
+ if(other.ModalController_initialFontScale == '0 0 0')
+ other.ModalController_initialFontScale = '1 1 0';
+}
+
+void ModalController_showChild(entity me, entity other, vector theOrigin, vector theSize, float skipAnimation)
+{
+ if(other.ModalController_state == 0 || skipAnimation)
+ {
+ me.setFocus(me, NULL);
+ if(!skipAnimation)
+ {
+ other.ModalController_buttonOrigin = globalToBox(theOrigin, me.origin, me.size);
+ other.ModalController_buttonSize = globalToBoxSize(theSize, me.size);
+ }
+ me.switchState(me, other, 1, skipAnimation);
+ } // zoom in from button (factor increases)
+}
+
+void ModalController_hideAll(entity me, float skipAnimation)
+{
+ entity e;
+ for(e = me.firstChild; e; e = e.nextSibling)
+ me.hideChild(me, e, skipAnimation);
+}
+
+void ModalController_hideChild(entity me, entity other, float skipAnimation)
+{
+ if(other.ModalController_state || skipAnimation)
+ {
+ me.setFocus(me, NULL);
+ me.switchState(me, other, 0, skipAnimation);
+ if(other.ModalController_controllingButton)
+ {
+ other.ModalController_controllingButton.forcePressed = 0;
+ other.ModalController_controllingButton = NULL;
+ }
+ } // just alpha fade out (factor increases and decreases alpha)
+}
+#endif
--- /dev/null
+#ifdef INTERFACE
+CLASS(Nexposee) EXTENDS(Container)
+ METHOD(Nexposee, draw, void(entity))
+ METHOD(Nexposee, keyDown, float(entity, float, float, float))
+ METHOD(Nexposee, keyUp, float(entity, float, float, float))
+ METHOD(Nexposee, mousePress, float(entity, vector))
+ METHOD(Nexposee, mouseMove, float(entity, vector))
+ METHOD(Nexposee, mouseRelease, float(entity, vector))
+ METHOD(Nexposee, mouseDrag, float(entity, vector))
+ METHOD(Nexposee, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(Nexposee, focusEnter, void(entity))
+ METHOD(Nexposee, close, void(entity))
+
+ ATTRIB(Nexposee, animationState, float, -1)
+ ATTRIB(Nexposee, animationFactor, float, 0)
+ ATTRIB(Nexposee, selectedChild, entity, NULL)
+ ATTRIB(Nexposee, mouseFocusedChild, entity, NULL)
+ METHOD(Nexposee, addItem, void(entity, entity, vector, vector, float))
+ METHOD(Nexposee, calc, void(entity))
+ METHOD(Nexposee, setNexposee, void(entity, entity, vector, float, float))
+ ATTRIB(Nexposee, mousePosition, vector, '0 0 0')
+ METHOD(Nexposee, pullNexposee, void(entity, entity, vector))
+ENDCLASS(Nexposee)
+
+void ExposeeCloseButton_Click(entity button, entity other); // un-exposees the current state
+#endif
+
+// animation states:
+// 0 = thumbnails seen
+// 1 = zooming in
+// 2 = zoomed in
+// 3 = zooming out
+// animation factor: 0 = minimum theSize, 1 = maximum theSize
+
+#ifdef IMPLEMENTATION
+
+.vector Nexposee_initialSize;
+.vector Nexposee_initialFontScale;
+.vector Nexposee_initialOrigin;
+.float Nexposee_initialAlpha;
+
+.vector Nexposee_smallSize;
+.vector Nexposee_smallOrigin;
+.float Nexposee_smallAlpha;
+.float Nexposee_mediumAlpha;
+.vector Nexposee_scaleCenter;
+.vector Nexposee_align;
+.float Nexposee_animationFactor;
+
+void Nexposee_close(entity me)
+{
+ // user must override this
+}
+
+void ExposeeCloseButton_Click(entity button, entity other)
+{
+ other.selectedChild = other.focusedChild;
+ other.setFocus(other, NULL);
+ other.animationState = 3;
+}
+
+void Nexposee_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ me.calc(me);
+ me.resizeNotifyLie(me, relOrigin, relSize, absOrigin, absSize, Nexposee_initialOrigin, Nexposee_initialSize, Nexposee_initialFontScale);
+}
+
+void Nexposee_Calc_Scale(entity me, float scale)
+{
+ entity e;
+ for(e = me.firstChild; e; e = e.nextSibling)
+ {
+ e.Nexposee_smallOrigin = (e.Nexposee_initialOrigin - e.Nexposee_scaleCenter) * scale + e.Nexposee_scaleCenter;
+ e.Nexposee_smallSize = e.Nexposee_initialSize * scale;
+ if(e.Nexposee_align.x > 0)
+ e.Nexposee_smallOrigin_x = 1 - e.Nexposee_align.x * scale;
+ if(e.Nexposee_align.x < 0)
+ e.Nexposee_smallOrigin_x = -e.Nexposee_smallSize.x + e.Nexposee_align.x * scale;
+ if(e.Nexposee_align.y > 0)
+ e.Nexposee_smallOrigin_y = 1 - e.Nexposee_align.y * scale;
+ if(e.Nexposee_align.y < 0)
+ e.Nexposee_smallOrigin_y = -e.Nexposee_smallSize.y + e.Nexposee_align.y * scale;
+ }
+}
+
+void Nexposee_calc(entity me)
+{
+ /*
+ * patented by Apple
+ * can't put that here ;)
+ */
+ float scale;
+ entity e, e2;
+ vector emins, emaxs, e2mins, e2maxs;
+
+ for(scale = 0.7;; scale *= 0.99)
+ {
+ Nexposee_Calc_Scale(me, scale);
+
+ for(e = me.firstChild; e; e = e.nextSibling)
+ {
+ emins = e.Nexposee_smallOrigin;
+ emaxs = emins + e.Nexposee_smallSize;
+ for(e2 = e.nextSibling; e2; e2 = e2.nextSibling)
+ {
+ e2mins = e2.Nexposee_smallOrigin;
+ e2maxs = e2mins + e2.Nexposee_smallSize;
+
+ // two intervals [amins, amaxs] and [bmins, bmaxs] overlap if:
+ // amins < bmins < amaxs < bmaxs
+ // for which suffices
+ // bmins < amaxs
+ // amins < bmaxs
+ if((e2mins.x - emaxs.x) * (emins.x - e2maxs.x) > 0) // x overlap
+ if((e2mins.y - emaxs.y) * (emins.y - e2maxs.y) > 0) // y overlap
+ {
+ goto have_overlap;
+ }
+ }
+ }
+
+ break;
+:have_overlap
+ }
+
+ scale *= 0.95;
+
+ Nexposee_Calc_Scale(me, scale);
+}
+
+void Nexposee_setNexposee(entity me, entity other, vector scalecenter, float a0, float a1)
+{
+ other.Nexposee_scaleCenter = scalecenter;
+ other.Nexposee_smallAlpha = a0;
+ me.setAlphaOf(me, other, a0);
+ other.Nexposee_mediumAlpha = a1;
+}
+
+void Nexposee_draw(entity me)
+{
+ float a;
+ float a0;
+ entity e;
+ float f;
+ vector fs;
+
+ if(me.animationState == -1)
+ {
+ me.animationState = 0;
+ }
+
+ f = min(1, frametime * 5);
+ switch(me.animationState)
+ {
+ case 0:
+ me.animationFactor = 0;
+ break;
+ case 1:
+ me.animationFactor += f;
+ if(me.animationFactor >= 1)
+ {
+ me.animationFactor = 1;
+ me.animationState = 2;
+ SUPER(Nexposee).setFocus(me, me.selectedChild);
+ }
+ break;
+ case 2:
+ me.animationFactor = 1;
+ break;
+ case 3:
+ me.animationFactor -= f;
+ me.mouseFocusedChild = me.itemFromPoint(me, me.mousePosition);
+ if(me.animationFactor <= 0)
+ {
+ me.animationFactor = 0;
+ me.animationState = 0;
+ me.selectedChild = me.mouseFocusedChild;
+ }
+ break;
+ }
+
+ f = min(1, frametime * 10);
+ for(e = me.firstChild; e; e = e.nextSibling)
+ {
+ if(e == me.selectedChild)
+ {
+ e.Container_origin = e.Nexposee_smallOrigin * (1 - me.animationFactor) + e.Nexposee_initialOrigin * me.animationFactor;
+ e.Container_size = e.Nexposee_smallSize * (1 - me.animationFactor) + e.Nexposee_initialSize * me.animationFactor;
+ e.Nexposee_animationFactor = me.animationFactor;
+ a0 = e.Nexposee_mediumAlpha;
+ if(me.animationState == 3)
+ if(e != me.mouseFocusedChild)
+ a0 = e.Nexposee_smallAlpha;
+ a = a0 * (1 - me.animationFactor) + me.animationFactor;
+ }
+ else
+ {
+ // minimum theSize counts
+ e.Container_origin = e.Nexposee_smallOrigin;
+ e.Container_size = e.Nexposee_smallSize;
+ e.Nexposee_animationFactor = 0;
+ a = e.Nexposee_smallAlpha * (1 - me.animationFactor);
+ }
+ me.setAlphaOf(me, e, e.Container_alpha * (1 - f) + a * f);
+
+ fs = globalToBoxSize(e.Container_size, e.Nexposee_initialSize);
+ e.Container_fontscale_x = fs.x * e.Nexposee_initialFontScale.x;
+ e.Container_fontscale_y = fs.y * e.Nexposee_initialFontScale.y;
+ }
+
+ SUPER(Nexposee).draw(me);
+}
+
+float Nexposee_mousePress(entity me, vector pos)
+{
+ if(me.animationState == 0)
+ {
+ me.mouseFocusedChild = NULL;
+ Nexposee_mouseMove(me, pos);
+ if(me.mouseFocusedChild)
+ {
++ m_play_click_sound(MENU_SOUND_OPEN);
+ me.animationState = 1;
+ SUPER(Nexposee).setFocus(me, NULL);
+ }
+ else
+ me.close(me);
+ return 1;
+ }
+ else if(me.animationState == 2)
+ {
+ if (!(SUPER(Nexposee).mousePress(me, pos)))
+ {
++ m_play_click_sound(MENU_SOUND_CLOSE);
+ me.animationState = 3;
+ SUPER(Nexposee).setFocus(me, NULL);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+float Nexposee_mouseRelease(entity me, vector pos)
+{
+ if(me.animationState == 2)
+ return SUPER(Nexposee).mouseRelease(me, pos);
+ return 0;
+}
+
+float Nexposee_mouseDrag(entity me, vector pos)
+{
+ if(me.animationState == 2)
+ return SUPER(Nexposee).mouseDrag(me, pos);
+ return 0;
+}
+
+float Nexposee_mouseMove(entity me, vector pos)
+{
+ entity e;
+ me.mousePosition = pos;
+ e = me.mouseFocusedChild;
+ me.mouseFocusedChild = me.itemFromPoint(me, pos);
+ if(me.animationState == 2)
+ return SUPER(Nexposee).mouseMove(me, pos);
+ if(me.animationState == 0)
+ {
+ if(me.mouseFocusedChild)
+ if(me.mouseFocusedChild != e || me.mouseFocusedChild != me.selectedChild)
+ me.selectedChild = me.mouseFocusedChild;
+ return 1;
+ }
+ return 0;
+}
+
+float Nexposee_keyUp(entity me, float scan, float ascii, float shift)
+{
+ if(me.animationState == 2)
+ return SUPER(Nexposee).keyUp(me, scan, ascii, shift);
+ return 0;
+}
+
+float Nexposee_keyDown(entity me, float scan, float ascii, float shift)
+{
+ float nexposeeKey = 0;
+ if(me.animationState == 2)
+ if(SUPER(Nexposee).keyDown(me, scan, ascii, shift))
+ return 1;
+ if(scan == K_TAB)
+ {
+ if(me.animationState == 0)
+ {
+ if(shift & S_SHIFT)
+ {
+ if(me.selectedChild)
+ me.selectedChild = me.selectedChild.prevSibling;
+ if (!me.selectedChild)
+ me.selectedChild = me.lastChild;
+ }
+ else
+ {
+ if(me.selectedChild)
+ me.selectedChild = me.selectedChild.nextSibling;
+ if (!me.selectedChild)
+ me.selectedChild = me.firstChild;
+ }
+ }
+ }
+ switch(me.animationState)
+ {
+ default:
+ case 0:
+ case 3:
+ nexposeeKey = ((scan == K_SPACE) || (scan == K_ENTER) || (scan == K_KP_ENTER));
+ break;
+ case 1:
+ case 2:
+ nexposeeKey = (scan == K_ESCAPE);
+ break;
+ }
+ if(nexposeeKey)
+ {
+ switch(me.animationState)
+ {
+ default:
+ case 0:
+ case 3:
++ m_play_click_sound(MENU_SOUND_OPEN);
+ me.animationState = 1;
+ break;
+ case 1:
+ case 2:
++ m_play_click_sound(MENU_SOUND_CLOSE);
+ me.animationState = 3;
+ break;
+ }
+ if(me.focusedChild)
+ me.selectedChild = me.focusedChild;
+ if (!me.selectedChild)
+ me.animationState = 0;
+ SUPER(Nexposee).setFocus(me, NULL);
+ return 1;
+ }
+ return 0;
+}
+
+void Nexposee_addItem(entity me, entity other, vector theOrigin, vector theSize, float theAlpha)
+{
+ SUPER(Nexposee).addItem(me, other, theOrigin, theSize, theAlpha);
+ other.Nexposee_initialFontScale = other.Container_fontscale;
+ other.Nexposee_initialSize = other.Container_size;
+ other.Nexposee_initialOrigin = other.Container_origin;
+ other.Nexposee_initialAlpha = other.Container_alpha;
+ if(other.Nexposee_initialFontScale == '0 0 0')
+ other.Nexposee_initialFontScale = '1 1 0';
+}
+
+void Nexposee_focusEnter(entity me)
+{
+ if(me.animationState == 2)
+ SUPER(Nexposee).setFocus(me, me.selectedChild);
+}
+
+void Nexposee_pullNexposee(entity me, entity other, vector theAlign)
+{
+ other.Nexposee_align = theAlign;
+}
+#endif
--- /dev/null
- METHOD(Slider, focusEnter, void(entity))
+// Note:
+// to use this, you FIRST call configureSliderVisuals, then configureSliderValues
+#ifdef INTERFACE
+CLASS(Slider) EXTENDS(Label)
+ METHOD(Slider, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(Slider, configureSliderVisuals, void(entity, float, float, float, string))
+ METHOD(Slider, configureSliderValues, void(entity, float, float, float, float, float, float))
+ METHOD(Slider, draw, void(entity))
+ METHOD(Slider, keyDown, float(entity, float, float, float))
++ METHOD(Slider, keyUp, float(entity, float, float, float))
+ METHOD(Slider, mousePress, float(entity, vector))
+ METHOD(Slider, mouseDrag, float(entity, vector))
+ METHOD(Slider, mouseRelease, float(entity, vector))
- // TODO more keys
+ METHOD(Slider, valueToText, string(entity, float))
+ METHOD(Slider, toString, string(entity))
+ METHOD(Slider, setValue, void(entity, float))
+ METHOD(Slider, setSliderValue, void(entity, float))
+ METHOD(Slider, showNotify, void(entity))
+ ATTRIB(Slider, src, string, string_null)
+ ATTRIB(Slider, focusable, float, 1)
++ ATTRIB(Slider, allowFocusSound, float, 1)
+ ATTRIB(Slider, value, float, 0)
+ ATTRIB(Slider, animated, float, 1)
+ ATTRIB(Slider, sliderValue, float, 0)
+ ATTRIB(Slider, valueMin, float, 0)
+ ATTRIB(Slider, valueMax, float, 0)
+ ATTRIB(Slider, valueStep, float, 0)
+ ATTRIB(Slider, valueDigits, float, 0)
+ ATTRIB(Slider, valueKeyStep, float, 0)
+ ATTRIB(Slider, valuePageStep, float, 0)
+ ATTRIB(Slider, valueDisplayMultiplier, float, 1.0)
+ ATTRIB(Slider, textSpace, float, 0)
+ ATTRIB(Slider, controlWidth, float, 0)
+ ATTRIB(Slider, pressed, float, 0)
+ ATTRIB(Slider, pressOffset, float, 0)
+ ATTRIB(Slider, previousValue, float, 0)
+ ATTRIB(Slider, tolerance, vector, '0 0 0')
+ ATTRIB(Slider, disabled, float, 0)
+ ATTRIB(Slider, color, vector, '1 1 1')
+ ATTRIB(Slider, color2, vector, '1 1 1')
+ ATTRIB(Slider, colorD, vector, '1 1 1')
+ ATTRIB(Slider, colorC, vector, '1 1 1')
+ ATTRIB(Slider, colorF, vector, '1 1 1')
+ ATTRIB(Slider, disabledAlpha, float, 0.3)
+ENDCLASS(Slider)
+#endif
+
+#ifdef IMPLEMENTATION
+void Slider_setValue(entity me, float val)
+{
+ if (me.animated) {
+ anim.removeObjAnim(anim, me);
+ makeHostedEasing(me, Slider_setSliderValue, easingQuadInOut, 1, me.sliderValue, val);
+ } else {
+ me.setSliderValue(me, val);
+ }
+ me.value = val;
+}
+void Slider_setSliderValue(entity me, float val)
+{
+ me.sliderValue = val;
+}
+string Slider_toString(entity me)
+{
+ return sprintf("%d (%s)", me.value, me.valueToText(me, me.value));
+}
+void Slider_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ SUPER(Slider).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
+ me.controlWidth = absSize.y / absSize.x;
+}
+string Slider_valueToText(entity me, float val)
+{
+ if(almost_in_bounds(me.valueMin, val, me.valueMax))
+ return ftos_decimals(val * me.valueDisplayMultiplier, me.valueDigits);
+ return "";
+}
+void Slider_configureSliderVisuals(entity me, float sz, float theAlign, float theTextSpace, string gfx)
+{
+ SUPER(Slider).configureLabel(me, string_null, sz, theAlign);
+ me.textSpace = theTextSpace;
+ me.keepspaceLeft = (theTextSpace == 0) ? 0 : (1 - theTextSpace);
+ me.src = gfx;
+}
+void Slider_configureSliderValues(entity me, float theValueMin, float theValue, float theValueMax, float theValueStep, float theValueKeyStep, float theValuePageStep)
+{
+ me.value = theValue;
+ me.sliderValue = theValue;
+ me.valueStep = theValueStep;
+ me.valueMin = theValueMin;
+ me.valueMax = theValueMax;
+ me.valueKeyStep = theValueKeyStep;
+ me.valuePageStep = theValuePageStep;
+ me.valueDigits = 3;
+ if(fabs(floor(me.valueStep * 100 + 0.5) - (me.valueStep * 100)) < 0.01) // about a whole number of 100ths
+ me.valueDigits = 2;
+ if(fabs(floor(me.valueStep * 10 + 0.5) - (me.valueStep * 10)) < 0.01) // about a whole number of 10ths
+ me.valueDigits = 1;
+ if(fabs(floor(me.valueStep * 1 + 0.5) - (me.valueStep * 1)) < 0.01) // about a whole number
+ me.valueDigits = 0;
+}
+float Slider_keyDown(entity me, float key, float ascii, float shift)
+{
+ float inRange;
+ if(me.disabled)
+ return 0;
+ inRange = (almost_in_bounds(me.valueMin, me.value, me.valueMax));
+ if(key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_MWHEELDOWN)
+ {
+ if(inRange)
+ me.setValue(me, median(me.valueMin, me.value - me.valueKeyStep, me.valueMax));
+ else
+ me.setValue(me, me.valueMax);
+ return 1;
+ }
+ if(key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_MWHEELUP)
+ {
+ if(inRange)
+ me.setValue(me, median(me.valueMin, me.value + me.valueKeyStep, me.valueMax));
+ else
+ me.setValue(me, me.valueMin);
+ return 1;
+ }
+ if(key == K_PGDN || key == K_KP_PGDN)
+ {
+ if(inRange)
+ me.setValue(me, median(me.valueMin, me.value - me.valuePageStep, me.valueMax));
+ else
+ me.setValue(me, me.valueMax);
+ return 1;
+ }
+ if(key == K_PGUP || key == K_KP_PGUP)
+ {
+ if(inRange)
+ me.setValue(me, median(me.valueMin, me.value + me.valuePageStep, me.valueMax));
+ else
+ me.setValue(me, me.valueMin);
+ return 1;
+ }
+ if(key == K_HOME || key == K_KP_HOME)
+ {
+ me.setValue(me, me.valueMin);
+ return 1;
+ }
+ if(key == K_END || key == K_KP_END)
+ {
+ me.setValue(me, me.valueMax);
+ return 1;
+ }
- if(cvar("menu_sounds"))
- localsound("sound/misc/menu2.wav");
++ // TODO more keys (NOTE also add them to Slider_keyUp)
++ return 0;
++}
++float Slider_keyUp(entity me, float key, float ascii, float shift)
++{
++ if(me.disabled)
++ return 0;
++ switch(key)
++ {
++ case K_LEFTARROW:
++ case K_KP_LEFTARROW:
++ case K_RIGHTARROW:
++ case K_KP_RIGHTARROW:
++ case K_PGUP:
++ case K_KP_PGUP:
++ case K_PGDN:
++ case K_KP_PGDN:
++ case K_HOME:
++ case K_KP_HOME:
++ case K_END:
++ case K_KP_END:
++ m_play_click_sound(MENU_SOUND_SLIDE);
++ }
+ return 0;
+}
+float Slider_mouseDrag(entity me, vector pos)
+{
+ float hit;
+ float v, animed;
+ if(me.disabled)
+ return 0;
+
+ anim.removeObjAnim(anim, me);
+ animed = me.animated;
+ me.animated = false;
+
+ if(me.pressed)
+ {
+ hit = 1;
+ if(pos.x < 0 - me.tolerance.x) hit = 0;
+ if(pos.y < 0 - me.tolerance.y) hit = 0;
+ if(pos.x >= 1 - me.textSpace + me.tolerance.x) hit = 0;
+ if(pos.y >= 1 + me.tolerance.y) hit = 0;
+ if(hit)
+ {
+ v = median(0, (pos.x - me.pressOffset - 0.5 * me.controlWidth) / (1 - me.textSpace - me.controlWidth), 1) * (me.valueMax - me.valueMin) + me.valueMin;
+ if(me.valueStep)
+ v = floor(0.5 + v / me.valueStep) * me.valueStep;
+ me.setValue(me, v);
+ }
+ else
+ me.setValue(me, me.previousValue);
+ }
+
+ me.animated = animed;
+
+ return 1;
+}
+float Slider_mousePress(entity me, vector pos)
+{
+ float controlCenter;
+ if(me.disabled)
+ return 0;
+ if(pos.x < 0) return 0;
+ if(pos.y < 0) return 0;
+ if(pos.x >= 1 - me.textSpace) return 0;
+ if(pos.y >= 1) return 0;
+ controlCenter = (me.value - me.valueMin) / (me.valueMax - me.valueMin) * (1 - me.textSpace - me.controlWidth) + 0.5 * me.controlWidth;
+ if(fabs(pos.x - controlCenter) <= 0.5 * me.controlWidth)
+ {
+ me.pressed = 1;
+ me.pressOffset = pos.x - controlCenter;
+ me.previousValue = me.value;
+ //me.mouseDrag(me, pos);
+ }
+ else
+ {
+ float clickValue, pageValue, inRange;
+ clickValue = median(0, (pos.x - me.pressOffset - 0.5 * me.controlWidth) / (1 - me.textSpace - me.controlWidth), 1) * (me.valueMax - me.valueMin) + me.valueMin;
+ inRange = (almost_in_bounds(me.valueMin, me.value, me.valueMax));
+ if(pos.x < controlCenter)
+ {
+ pageValue = me.value - me.valuePageStep;
+ if(me.valueStep)
+ clickValue = floor(clickValue / me.valueStep) * me.valueStep;
+ pageValue = max(pageValue, clickValue);
+ if(inRange)
+ me.setValue(me, median(me.valueMin, pageValue, me.valueMax));
+ else
+ me.setValue(me, me.valueMax);
+ }
+ else
+ {
+ pageValue = me.value + me.valuePageStep;
+ if(me.valueStep)
+ clickValue = ceil(clickValue / me.valueStep) * me.valueStep;
+ pageValue = min(pageValue, clickValue);
+ if(inRange)
+ me.setValue(me, median(me.valueMin, pageValue, me.valueMax));
+ else
+ me.setValue(me, me.valueMax);
+ }
+ if(pageValue == clickValue)
+ {
+ controlCenter = (me.value - me.valueMin) / (me.valueMax - me.valueMin) * (1 - me.textSpace - me.controlWidth) + 0.5 * me.controlWidth;
+ me.pressed = 1;
+ me.pressOffset = pos.x - controlCenter;
+ me.previousValue = me.value;
+ //me.mouseDrag(me, pos);
+ }
+ }
+ return 1;
+}
+float Slider_mouseRelease(entity me, vector pos)
+{
+ me.pressed = 0;
+ if(me.disabled)
+ return 0;
- void Slider_focusEnter(entity me)
- {
- if(cvar("menu_sounds") > 1)
- localsound("sound/misc/menu1.wav");
- SUPER(Slider).focusEnter(me);
- }
++ m_play_click_sound(MENU_SOUND_SLIDE);
+ return 1;
+}
+void Slider_showNotify(entity me)
+{
+ me.focusable = !me.disabled;
+}
+void Slider_draw(entity me)
+{
+ float controlLeft;
+ float save;
+ me.focusable = !me.disabled;
+ save = draw_alpha;
+ if(me.disabled)
+ draw_alpha *= me.disabledAlpha;
+ draw_ButtonPicture('0 0 0', strcat(me.src, "_s"), eX * (1 - me.textSpace) + eY, me.color2, 1);
+ if(almost_in_bounds(me.valueMin, me.sliderValue, me.valueMax))
+ {
+ controlLeft = (me.sliderValue - me.valueMin) / (me.valueMax - me.valueMin) * (1 - me.textSpace - me.controlWidth);
+ if(me.disabled)
+ draw_Picture(eX * controlLeft, strcat(me.src, "_d"), eX * me.controlWidth + eY, me.colorD, 1);
+ else if(me.pressed)
+ draw_Picture(eX * controlLeft, strcat(me.src, "_c"), eX * me.controlWidth + eY, me.colorC, 1);
+ else if(me.focused)
+ draw_Picture(eX * controlLeft, strcat(me.src, "_f"), eX * me.controlWidth + eY, me.colorF, 1);
+ else
+ draw_Picture(eX * controlLeft, strcat(me.src, "_n"), eX * me.controlWidth + eY, me.color, 1);
+ }
+ me.setText(me, me.valueToText(me, me.value));
+ draw_alpha = save;
+ SUPER(Slider).draw(me);
+ me.text = string_null; // TEMPSTRING!
+}
+#endif
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+#endif
+
///////////////////////////////////////////////
// Menu Source File
///////////////////////
float mouseButtonsPressed;
vector menuMousePos;
-float menuShiftState;
+int menuShiftState;
float menuPrevTime;
float menuAlpha;
float menuLogoAlpha;
}
fclose(fh);
- glob = search_begin(strcat(draw_currentSkin, "/*.tga"), TRUE, TRUE);
+ glob = search_begin(strcat(draw_currentSkin, "/*.tga"), true, true);
if(glob >= 0)
{
n = search_getsize(glob);
{
// detect a click outside of the game window
vector p = getmousepos();
- if(p_x < 0 || p_x > realconwidth || p_y < 0 || p_y > realconheight)
+ if(p.x < 0 || p.x > realconwidth || p.y < 0 || p.y > realconheight)
{
++mouseButtonsPressed;
return;
float width_is_larger;
sz = draw_PictureSize(img);
- width_is_larger = (sz_x * draw_scale_y >= sz_y * draw_scale_x);
- isz_w = '1 0 0' + '0 1 0' * ((sz_y / sz_x) * (draw_scale_x / draw_scale_y));
- isz_h = '0 1 0' + '1 0 0' * ((sz_x / sz_y) * (draw_scale_y / draw_scale_x));
+ width_is_larger = (sz.x * draw_scale.y >= sz.y * draw_scale.x);
+ isz_w = '1 0 0' + '0 1 0' * ((sz.y / sz.x) * (draw_scale.x / draw_scale.y));
+ isz_h = '0 1 0' + '1 0 0' * ((sz.x / sz.y) * (draw_scale.y / draw_scale.x));
-#ifdef GMQCC
- isz = '0 0 0';
-#endif
switch(scalemode)
{
default:
break;
}
- org = eX * (algn_x * (1 - isz_x)) + eY * (algn_y * (1 - isz_y));
+ org = eX * (algn.x * (1 - isz.x)) + eY * (algn.y * (1 - isz.y));
draw_Picture(org, img, isz, '1 1 1', a);
}
string c;
float scalemode;
- v_z = 0;
+ v.z = 0;
scalemode = SCALEMODE_CROP;
case "h": scalemode = SCALEMODE_HEIGHT; goto nopic;
case "w": scalemode = SCALEMODE_WIDTH; goto nopic;
case "s": scalemode = SCALEMODE_STRETCH; goto nopic;
- case "1": case "4": case "7": v_x = 0.0; break;
- case "2": case "5": case "8": v_x = 0.5; break;
- case "3": case "6": case "9": v_x = 1.0; break;
- default: v_x = random(); break;
+ case "1": case "4": case "7": v.x = 0.0; break;
+ case "2": case "5": case "8": v.x = 0.5; break;
+ case "3": case "6": case "9": v.x = 1.0; break;
+ default: v.x = random(); break;
}
switch(c)
{
- case "7": case "8": case "9": v_y = 0.0; break;
- case "4": case "5": case "6": v_y = 0.5; break;
- case "1": case "2": case "3": v_y = 1.0; break;
- default: v_y = random(); break;
+ case "7": case "8": case "9": v.y = 0.0; break;
+ case "4": case "5": case "6": v.y = 0.5; break;
+ case "1": case "2": case "3": v.y = 1.0; break;
+ default: v.y = random(); break;
}
if(l == 0)
draw_Picture_Aligned(v, scalemode, img, a);
float menuTooltipState; // 0: static, 1: fading in, 2: fading out
float m_testmousetooltipbox(vector pos)
{
- if(pos_x >= menuTooltipOrigin_x && pos_x < menuTooltipOrigin_x + menuTooltipSize_x)
- if(pos_y >= menuTooltipOrigin_y && pos_y < menuTooltipOrigin_y + menuTooltipSize_y)
- return FALSE;
- return TRUE;
+ if(pos.x >= menuTooltipOrigin.x && pos.x < menuTooltipOrigin.x + menuTooltipSize.x)
+ if(pos.y >= menuTooltipOrigin.y && pos.y < menuTooltipOrigin.y + menuTooltipSize.y)
+ return false;
+ return true;
}
float m_testtooltipbox(vector tooltippos)
{
- if(tooltippos_x < 0)
- return FALSE;
- if(tooltippos_y < 0)
- return FALSE;
- if(tooltippos_x + menuTooltipSize_x > 1)
- return FALSE;
- if(tooltippos_y + menuTooltipSize_y > 1)
- return FALSE;
+ if(tooltippos.x < 0)
+ return false;
+ if(tooltippos.y < 0)
+ return false;
+ if(tooltippos.x + menuTooltipSize.x > 1)
+ return false;
+ if(tooltippos.y + menuTooltipSize.y > 1)
+ return false;
menuTooltipOrigin = tooltippos;
- return TRUE;
+ return true;
}
float m_allocatetooltipbox(vector pos)
{
vector avoidplus, avoidminus;
vector v;
- avoidplus_x = (SKINAVOID_TOOLTIP_x + SKINSIZE_CURSOR_x - SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth;
- avoidplus_y = (SKINAVOID_TOOLTIP_y + SKINSIZE_CURSOR_y - SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight;
- avoidplus_z = 0;
+ avoidplus.x = (SKINAVOID_TOOLTIP_x + SKINSIZE_CURSOR_x - SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth;
+ avoidplus.y = (SKINAVOID_TOOLTIP_y + SKINSIZE_CURSOR_y - SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight;
+ avoidplus.z = 0;
- avoidminus_x = (SKINAVOID_TOOLTIP_x + SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth + menuTooltipSize_x;
- avoidminus_y = (SKINAVOID_TOOLTIP_y + SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight + menuTooltipSize_y;
- avoidminus_z = 0;
+ avoidminus.x = (SKINAVOID_TOOLTIP_x + SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth + menuTooltipSize.x;
+ avoidminus.y = (SKINAVOID_TOOLTIP_y + SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight + menuTooltipSize.y;
+ avoidminus.z = 0;
// bottom right
v = pos + avoidplus;
if(m_testtooltipbox(v))
- return TRUE;
+ return true;
// bottom center
- v_x = pos_x - menuTooltipSize_x * 0.5;
+ v.x = pos.x - menuTooltipSize.x * 0.5;
if(m_testtooltipbox(v))
- return TRUE;
+ return true;
// bottom left
- v_x = pos_x - avoidminus_x;
+ v.x = pos.x - avoidminus.x;
if(m_testtooltipbox(v))
- return TRUE;
+ return true;
// top left
- v_y = pos_y - avoidminus_y;
+ v.y = pos.y - avoidminus.y;
if(m_testtooltipbox(v))
- return TRUE;
+ return true;
// top center
- v_x = pos_x - menuTooltipSize_x * 0.5;
+ v.x = pos.x - menuTooltipSize.x * 0.5;
if(m_testtooltipbox(v))
- return TRUE;
+ return true;
// top right
- v_x = pos_x + avoidplus_x;
+ v.x = pos.x + avoidplus.x;
if(m_testtooltipbox(v))
- return TRUE;
+ return true;
- return FALSE;
+ return false;
}
entity m_findtooltipitem(entity root, vector pos)
{
menuTooltipState = 1;
menuTooltipItem = it;
- menuTooltipOrigin_x = -1; // unallocated
+ menuTooltipOrigin.x = -1; // unallocated
if (menuTooltipText)
strunzone(menuTooltipText);
{
s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithoutColors);
++i;
- f = draw_TextWidth(s, FALSE, fontsize);
+ f = draw_TextWidth(s, false, fontsize);
if(f > w)
w = f;
}
- menuTooltipSize_x = w + 2 * (SKINMARGIN_TOOLTIP_x / conwidth);
- menuTooltipSize_y = i * fontsize_y + 2 * (SKINMARGIN_TOOLTIP_y / conheight);
- menuTooltipSize_z = 0;
+ menuTooltipSize.x = w + 2 * (SKINMARGIN_TOOLTIP_x / conwidth);
+ menuTooltipSize.y = i * fontsize.y + 2 * (SKINMARGIN_TOOLTIP_y / conheight);
+ menuTooltipSize.z = 0;
}
break;
case 1:
menuTooltipItem = world; // reload tooltip next frame
menu_tooltips_old = menu_tooltips;
}
- else if(menuTooltipOrigin_x < 0) // unallocated?
+ else if(menuTooltipOrigin.x < 0) // unallocated?
m_allocatetooltipbox(pos);
- if(menuTooltipOrigin_x >= 0)
+ if(menuTooltipOrigin.x >= 0)
{
// draw the tooltip!
p = SKINBORDER_TOOLTIP;
- p_x *= 1 / conwidth;
- p_y *= 1 / conheight;
+ p.x *= 1 / conwidth;
+ p.y *= 1 / conheight;
draw_BorderPicture(menuTooltipOrigin, SKINGFX_TOOLTIP, menuTooltipSize, '1 1 1', menuTooltipAlpha, p);
p = menuTooltipOrigin;
- p_x += SKINMARGIN_TOOLTIP_x / conwidth;
- p_y += SKINMARGIN_TOOLTIP_y / conheight;
+ p.x += SKINMARGIN_TOOLTIP_x / conwidth;
+ p.y += SKINMARGIN_TOOLTIP_y / conheight;
getWrappedLine_remaining = menuTooltipText;
while(getWrappedLine_remaining)
{
s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithoutColors);
- draw_Text(p, s, fontsize, SKINCOLOR_TOOLTIP, SKINALPHA_TOOLTIP * menuTooltipAlpha, FALSE);
- p_y += fontsize_y;
+ draw_Text(p, s, fontsize, SKINCOLOR_TOOLTIP, SKINALPHA_TOOLTIP * menuTooltipAlpha, false);
+ p.y += fontsize.y;
}
}
}
{
draw_reset_full();
draw_Fill('0 0 0', '1 1 0', SKINCOLOR_BACKGROUND, 1);
- drawBackground(SKINGFX_BACKGROUND, bound(0, menuLogoAlpha, 1), SKINALIGN_BACKGROUND, TRUE);
+ drawBackground(SKINGFX_BACKGROUND, bound(0, menuLogoAlpha, 1), SKINALIGN_BACKGROUND, true);
draw_reset_cropped();
if(menuAlpha <= 0 && SKINALPHA_CURSOR_INTRO > 0)
{
if(menuAlpha > 0)
{
draw_reset_full();
- drawBackground(SKINGFX_BACKGROUND_INGAME, menuAlpha * SKINALPHA_BACKGROUND_INGAME, SKINALIGN_BACKGROUND_INGAME, FALSE);
+ drawBackground(SKINGFX_BACKGROUND_INGAME, menuAlpha * SKINALPHA_BACKGROUND_INGAME, SKINALIGN_BACKGROUND_INGAME, false);
draw_reset_cropped();
}
}
maxpos = globalToBox(eX * (realconwidth - 1) + eY * (realconheight - 1), draw_shift, draw_scale);
dMouse = globalToBoxSize(dMouse, draw_scale);
menuMousePos += dMouse * cvar("menu_mouse_speed");
- menuMousePos_x = bound(minpos_x, menuMousePos_x, maxpos_x);
- menuMousePos_y = bound(minpos_y, menuMousePos_y, maxpos_y);
+ menuMousePos.x = bound(minpos.x, menuMousePos.x, maxpos.x);
+ menuMousePos.y = bound(minpos.y, menuMousePos.y, maxpos.y);
if(mouseButtonsPressed)
main.mouseDrag(main, menuMousePos);
else
if(focus)
{
menuMousePos = focus.origin + 0.5 * focus.size;
- menuMousePos_x *= 1 / conwidth;
- menuMousePos_y *= 1 / conheight;
+ menuMousePos.x *= 1 / conwidth;
+ menuMousePos.y *= 1 / conheight;
if(wnd.focused) // why does this never happen?
m_focus_item_chain(wnd, focus);
}
}
}
}
+
+ float menuLastFocusSoundTime;
+ void m_play_focus_sound()
+ {
+ if(cvar("menu_sounds") > 1)
+ if(time - menuLastFocusSoundTime > 0.25)
+ {
+ localsound(MENU_SOUND_FOCUS);
+ menuLastFocusSoundTime = time;
+ }
+ }
+
+ void m_play_click_sound(string soundfile)
+ {
+ if(cvar("menu_sounds"))
+ localsound(soundfile);
+ }
+#ifndef MENU_H
+#define MENU_H
+
+#include "draw.qh"
+#include "skin.qh"
+
+#include "oo/base.qh"
+
+#include "xonotic/util.qh"
+
+#include "../common/constants.qh"
+#include "../common/test.qh"
+#include "../common/util.qh"
+
#define localcmd cmd
#define NULL (null_entity)
// constants
-const float GAME_ISSERVER = 1;
-const float GAME_CONNECTED = 2;
-const float GAME_DEVELOPER = 4;
+const int GAME_ISSERVER = 1;
+const int GAME_CONNECTED = 2;
+const int GAME_DEVELOPER = 4;
// prototypes
float Menu_Active;
-float gamestatus;
+int gamestatus;
-const float S_SHIFT = 1;
-const float S_CTRL = 2;
-const float S_ALT = 4;
+const int S_SHIFT = 1;
+const int S_CTRL = 2;
+const int S_ALT = 4;
float frametime;
float time;
void postMenuDraw(); // this is run just after the menu is drawn (or not). Useful to draw something over everything else.
void m_sync();
+
+ // sounds
+
+ const string MENU_SOUND_CLEAR = "sound/menu/clear.wav";
+ const string MENU_SOUND_CLOSE = "sound/menu/close.wav";
+ const string MENU_SOUND_EXECUTE = "sound/menu/execute.wav";
+ const string MENU_SOUND_FOCUS = "sound/menu/focus.wav";
+ const string MENU_SOUND_OPEN = "sound/menu/open.wav";
+ const string MENU_SOUND_SELECT = "sound/menu/select.wav";
+ const string MENU_SOUND_SLIDE = "sound/menu/slide.wav";
+ const string MENU_SOUND_WINNER = "sound/menu/winner.wav";
+
+ void m_play_focus_sound();
+ void m_play_click_sound(string soundfile);
+#endif
--- /dev/null
+#ifdef INTERFACE
+CLASS(XonoticColorpicker) EXTENDS(Image)
+ METHOD(XonoticColorpicker, configureXonoticColorpicker, void(entity, entity))
+ METHOD(XonoticColorpicker, mousePress, float(entity, vector))
+ METHOD(XonoticColorpicker, mouseRelease, float(entity, vector))
+ METHOD(XonoticColorpicker, mouseDrag, float(entity, vector))
+ ATTRIB(XonoticColorpicker, controlledTextbox, entity, NULL)
+ ATTRIB(XonoticColorpicker, image, string, SKINGFX_COLORPICKER)
+ ATTRIB(XonoticColorpicker, imagemargin, vector, SKINMARGIN_COLORPICKER)
+ ATTRIB(XonoticColorpicker, focusable, float, 1)
+ METHOD(XonoticColorpicker, focusLeave, void(entity))
+ METHOD(XonoticColorpicker, keyDown, float(entity, float, float, float))
+ METHOD(XonoticColorpicker, draw, void(entity))
+ENDCLASS(XonoticColorpicker)
+entity makeXonoticColorpicker(entity theTextbox);
+#endif
+
+#ifdef IMPLEMENTATION
+entity makeXonoticColorpicker(entity theTextbox)
+{
+ entity me;
+ me = spawnXonoticColorpicker();
+ me.configureXonoticColorpicker(me, theTextbox);
+ return me;
+}
+
+void XonoticColorpicker_configureXonoticColorpicker(entity me, entity theTextbox)
+{
+ me.controlledTextbox = theTextbox;
+ me.configureImage(me, me.image);
+}
+
+float XonoticColorpicker_mousePress(entity me, vector coords)
+{
+ me.mouseDrag(me, coords);
+ return 1;
+}
+
+// must match hslimage.c
+vector hslimage_color(vector v, vector margin)
+{
+ v_x = (v.x - margin.x) / (1 - 2 * margin.x);
+ v_y = (v.y - margin.y) / (1 - 2 * margin.y);
+ if(v.x < 0) v_x = 0;
+ if(v.y < 0) v_y = 0;
+ if(v.x > 1) v_x = 1;
+ if(v.y > 1) v_y = 1;
+ if(v.y > 0.875) // grey bar
+ return hsl_to_rgb(eZ * v.x);
+ else
+ return hsl_to_rgb(v.x * 6 * eX + eY + v.y / 0.875 * eZ);
+}
+
+vector color_hslimage(vector v, vector margin)
+{
+ vector pos = '0 0 0';
+ v = rgb_to_hsl(v);
+ if (v.y)
+ {
+ pos_x = v.x / 6;
+ pos_y = v.z * 0.875;
+ }
+ else // grey scale
+ {
+ pos_x = v.z;
+ pos_y = 0.875 + 0.07;
+ }
+ pos_x = margin.x + pos.x * (1 - 2 * margin.x);
+ pos_y = margin.y + pos.y * (1 - 2 * margin.y);
+ return pos;
+}
+
+float XonoticColorpicker_mouseDrag(entity me, vector coords)
+{
+ float i, carets;
+ for (;;)
+ {
+ i = me.controlledTextbox.cursorPos;
+ if(i >= 2)
+ {
+ if(substring(me.controlledTextbox.text, i-2, 1) == "^")
+ {
+ carets = 1;
+ while (i - 2 - carets >= 0 && substring(me.controlledTextbox.text, i - 2 - carets, 1) == "^")
+ ++carets;
+ if (carets & 1)
+ if(strstrofs("0123456789", substring(me.controlledTextbox.text, i-1, 1), 0) >= 0)
+ {
+ me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0);
+ me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0);
+ continue;
+ }
+ }
+ }
+
+ if(i >= 5)
+ {
+ if(substring(me.controlledTextbox.text, i-5, 2) == "^x")
+ {
+ carets = 1;
+ while (i - 5 - carets >= 0 && substring(me.controlledTextbox.text, i - 5 - carets, 1) == "^")
+ ++carets;
+ if (carets & 1)
+ if(strstrofs("0123456789abcdefABCDEF", substring(me.controlledTextbox.text, i-3, 1), 0) >= 0)
+ if(strstrofs("0123456789abcdefABCDEF", substring(me.controlledTextbox.text, i-2, 1), 0) >= 0)
+ if(strstrofs("0123456789abcdefABCDEF", substring(me.controlledTextbox.text, i-1, 1), 0) >= 0)
+ {
+ me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0);
+ me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0);
+ me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0);
+ me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0);
+ me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0);
+ continue;
+ }
+ }
+ }
+ break;
+ }
+
+ if(substring(me.controlledTextbox.text, i-1, 1) == "^")
+ {
+ carets = 1;
+ while (i - 1 - carets >= 0 && substring(me.controlledTextbox.text, i - 1 - carets, 1) == "^")
+ ++carets;
+ if (carets & 1)
+ me.controlledTextbox.enterText(me.controlledTextbox, "^"); // escape previous caret
+ }
+
+ vector margin;
+ margin = me.imagemargin;
+ if(coords.x >= margin.x)
+ if(coords.y >= margin.y)
+ if(coords.x <= 1 - margin.x)
+ if(coords.y <= 1 - margin.y)
+ me.controlledTextbox.enterText(me.controlledTextbox, rgb_to_hexcolor(hslimage_color(coords, margin)));
+
+ return 1;
+}
+
+float XonoticColorpicker_mouseRelease(entity me, vector coords)
+{
++ m_play_click_sound(MENU_SOUND_SLIDE);
+ me.mouseDrag(me, coords);
+ return 1;
+}
+
+void XonoticColorpicker_focusLeave(entity me)
+{
+ me.controlledTextbox.saveCvars(me.controlledTextbox);
+}
+float XonoticColorpicker_keyDown(entity me, float key, float ascii, float shift)
+{
+ return me.controlledTextbox.keyDown(me.controlledTextbox, key, ascii, shift);
+}
+void XonoticColorpicker_draw(entity me)
+{
+ SUPER(XonoticColorpicker).draw(me);
+
+ float B, C, aC;
+ C = cvar("r_textcontrast");
+ B = cvar("r_textbrightness");
+
+ // for this to work, C/(1-B) must be in 0..1
+ // B must be < 1
+ // C must be < 1-B
+
+ B = bound(0, B, 1);
+ C = bound(0, C, 1-B);
+
+ aC = 1 - C / (1 - B);
+
+ draw_Picture(me.imgOrigin, strcat(me.src, "_m"), me.imgSize, '0 0 0', aC);
+ draw_Picture(me.imgOrigin, strcat(me.src, "_m"), me.imgSize, me.color, B);
+}
+#endif
--- /dev/null
+#ifdef INTERFACE
+CLASS(XonoticColorpickerString) EXTENDS(Image)
+ METHOD(XonoticColorpickerString, configureXonoticColorpickerString, void(entity, string, string))
+ METHOD(XonoticColorpickerString, mousePress, float(entity, vector))
+ METHOD(XonoticColorpickerString, mouseRelease, float(entity, vector))
+ METHOD(XonoticColorpickerString, mouseDrag, float(entity, vector))
+ ATTRIB(XonoticColorpickerString, cvarName, string, string_null)
+ METHOD(XonoticColorPickerString, loadCvars, void(entity))
+ METHOD(XonoticColorPickerString, saveCvars, void(entity))
+ ATTRIB(XonoticColorpickerString, prevcoords, vector, '0 0 0')
+ ATTRIB(XonoticColorpickerString, image, string, SKINGFX_COLORPICKER)
+ ATTRIB(XonoticColorpickerString, imagemargin, vector, SKINMARGIN_COLORPICKER)
+ ATTRIB(XonoticColorpickerString, focusable, float, 1)
+ METHOD(XonoticColorpickerString, draw, void(entity))
+ ATTRIB(XonoticColorpickerString, disabledAlpha, float, 0.3)
+ENDCLASS(XonoticColorpickerString)
+entity makeXonoticColorpickerString(string theCvar, string theDefaultCvar);
+#endif
+
+#ifdef IMPLEMENTATION
+entity makeXonoticColorpickerString(string theCvar, string theDefaultCvar)
+{
+ entity me;
+ me = spawnXonoticColorpickerString();
+ me.configureXonoticColorpickerString(me, theCvar, theDefaultCvar);
+ return me;
+}
+
+void XonoticColorpickerString_configureXonoticColorpickerString(entity me, string theCvar, string theDefaultCvar)
+{
+ me.cvarName = theCvar;
+ me.configureImage(me, me.image);
+ if(theCvar)
+ {
+ me.cvarName = theCvar;
+ me.tooltip = getZonedTooltipForIdentifier(theCvar);
+ me.loadCvars(me);
+ }
+}
+
+void XonoticColorPickerString_loadCvars(entity me)
+{
+ if (!me.cvarName)
+ return;
+
+ if(substring(me.cvarName, -1, 1) == "_")
+ {
+ me.prevcoords = color_hslimage(
+ eX * cvar(strcat(me.cvarName, "red")) +
+ eY * cvar(strcat(me.cvarName, "green")) +
+ eZ * cvar(strcat(me.cvarName, "blue")),
+ me.imagemargin);
+ }
+ else
+ me.prevcoords = color_hslimage(stov(cvar_string(me.cvarName)), me.imagemargin);
+}
+
+void XonoticColorPickerString_saveCvars(entity me)
+{
+ if (!me.cvarName)
+ return;
+
+ if(substring(me.cvarName, -1, 1) == "_")
+ {
+ vector v = hslimage_color(me.prevcoords, me.imagemargin);
+ cvar_set(strcat(me.cvarName, "red"), ftos(v.x));
+ cvar_set(strcat(me.cvarName, "green"), ftos(v.y));
+ cvar_set(strcat(me.cvarName, "blue"), ftos(v.z));
+ }
+ else
+ cvar_set(me.cvarName, sprintf("%v", hslimage_color(me.prevcoords, me.imagemargin)));
+}
+
+float XonoticColorpickerString_mousePress(entity me, vector coords)
+{
+ me.mouseDrag(me, coords);
+ return 1;
+}
+
+float XonoticColorpickerString_mouseDrag(entity me, vector coords)
+{
+ if(me.disabled)
+ return 0;
+ vector margin;
+ margin = me.imagemargin;
+ if(coords.x >= margin.x)
+ if(coords.y >= margin.y)
+ if(coords.x <= 1 - margin.x)
+ if(coords.y <= 1 - margin.y)
+ {
+ me.prevcoords = coords;
+ me.saveCvars(me);
+ }
+
+ return 1;
+}
+
+float XonoticColorpickerString_mouseRelease(entity me, vector coords)
+{
++ m_play_click_sound(MENU_SOUND_SLIDE);
+ me.mouseDrag(me, coords);
+ return 1;
+}
+
+void XonoticColorpickerString_draw(entity me)
+{
+ float save;
+ save = draw_alpha;
+ if(me.disabled)
+ draw_alpha *= me.disabledAlpha;
+
+ SUPER(XonoticColorpickerString).draw(me);
+
+ vector sz;
+ sz = draw_PictureSize(strcat(me.src, "_selected"));
+ sz = globalToBoxSize(sz, draw_scale);
+
+ if(!me.disabled)
+ draw_Picture(me.imgOrigin + me.prevcoords - 0.5 * sz, strcat(me.src, "_selected"), sz, '1 1 1', 1);
+
+ draw_alpha = save;
+}
+#endif
--- /dev/null
- me.TD(me, 1, 2, e = makeXonoticTextSlider("cl_gender"));
+#ifdef INTERFACE
+CLASS(XonoticProfileTab) EXTENDS(XonoticTab)
+ METHOD(XonoticProfileTab, fill, void(entity))
+ METHOD(XonoticProfileTab, draw, void(entity))
+ ATTRIB(XonoticProfileTab, title, string, _("Profile"))
+ ATTRIB(XonoticProfileTab, intendedWidth, float, 0.9)
+ ATTRIB(XonoticProfileTab, rows, float, 23)
+ ATTRIB(XonoticProfileTab, columns, float, 6.1) // added extra .2 for center space
+ ATTRIB(XonoticProfileTab, playerNameLabel, entity, NULL)
+ ATTRIB(XonoticProfileTab, playerNameLabelAlpha, float, SKINALPHA_HEADER)
+ENDCLASS(XonoticProfileTab)
+entity makeXonoticProfileTab();
+#endif
+
+#ifdef IMPLEMENTATION
+entity makeXonoticProfileTab()
+{
+ entity me;
+ me = spawnXonoticProfileTab();
+ me.configureDialog(me);
+ return me;
+}
+void XonoticProfileTab_draw(entity me)
+{
+ if(cvar_string("_cl_name") == "Player")
+ me.playerNameLabel.alpha = ((mod(time * 2, 2) < 1) ? 1 : 0);
+ else
+ me.playerNameLabel.alpha = me.playerNameLabelAlpha;
+ SUPER(XonoticProfileTab).draw(me);
+}
+void XonoticProfileTab_fill(entity me)
+{
+ entity e, pms, label, box;
+ float i;
+
+ // ==============
+ // NAME SECTION
+ // ==============
+ me.gotoRC(me, 0.5, 0);
+ me.TD(me, 1, 3, me.playerNameLabel = makeXonoticHeaderLabel(_("Name")));
+
+ me.gotoRC(me, 1.5, 0);
+ me.TD(me, 1, 3, label = makeXonoticTextLabel(0.5, string_null));
+ label.allowCut = 1;
+ label.allowColors = 1;
+ label.alpha = 1;
+ label.isBold = true;
+ label.fontSize = SKINFONTSIZE_TITLE;
+
+ me.gotoRC(me, 2.5, 0);
+ me.TD(me, 1, 3.0, box = makeXonoticInputBox(1, "_cl_name"));
+ box.forbiddenCharacters = "\r\n\\\"$"; // don't care, isn't getting saved
+ box.maxLength = -127; // negative means encoded length in bytes
+ box.saveImmediately = 1;
+ box.enableClearButton = 0;
+ label.textEntity = box;
+ me.TR(me);
+ me.TD(me, 5, 1, e = makeXonoticColorpicker(box));
+ me.TD(me, 5, 2, e = makeXonoticCharmap(box));
+
+ // ===============
+ // MODEL SECTION
+ // ===============
+ //me.gotoRC(me, 0.5, 3.1); me.setFirstColumn(me, me.currentColumn); // TOP RIGHT
+ //me.gotoRC(me, 9, 3.1); me.setFirstColumn(me, me.currentColumn); // BOTTOM RIGHT
+ me.gotoRC(me, 9, 0); me.setFirstColumn(me, me.currentColumn); // BOTTOM LEFT
+ me.TD(me, 1, 3, e = makeXonoticHeaderLabel(_("Model")));
+
+ me.TR(me);
+ //me.TDempty(me, 0); // MODEL LEFT, COLOR RIGHT
+ me.TDempty(me, 1); // MODEL RIGHT, COLOR LEFT
+ pms = makeXonoticPlayerModelSelector();
+ me.TD(me, 1, 0.3, e = makeXonoticButton("<<", '0 0 0'));
+ e.onClick = PlayerModelSelector_Prev_Click;
+ e.onClickEntity = pms;
+ me.TD(me, 11.5, 1.4, pms);
+ me.TD(me, 1, 0.3, e = makeXonoticButton(">>", '0 0 0'));
+ e.onClick = PlayerModelSelector_Next_Click;
+ e.onClickEntity = pms;
+
+ //me.setFirstColumn(me, me.currentColumn + 2); // MODEL LEFT, COLOR RIGHT
+ me.gotoRC(me, me.currentRow, 0); me.setFirstColumn(me, me.currentColumn); // MODEL RIGHT, COLOR LEFT
+ me.TR(me);
+ me.TD(me, 1, 1, e = makeXonoticHeaderLabel(_("Glowing color")));
+ for(i = 0; i < 15; ++i)
+ {
+ if(mod(i, 5) == 0)
+ me.TR(me);
+ me.TDNoMargin(me, 1, 0.2, e = makeXonoticColorButton(1, 0, i), '0 1 0');
+ }
+ me.TR(me);
+ me.TR(me);
+ me.TD(me, 1, 1, e = makeXonoticHeaderLabel(_("Detail color")));
+ for(i = 0; i < 15; ++i)
+ {
+ if(mod(i, 5) == 0)
+ me.TR(me);
+ me.TDNoMargin(me, 1, 0.2, e = makeXonoticColorButton(2, 1, i), '0 1 0');
+ }
+
+ // ====================
+ // STATISTICS SECTION
+ // ====================
+ me.gotoRC(me, 0.5, 3.1); me.setFirstColumn(me, me.currentColumn); // TOP RIGHT
+ //me.gotoRC(me, 9, 3.1); me.setFirstColumn(me, me.currentColumn); // BOTTOM RIGHT
+ //me.gotoRC(me, 9, 0); me.setFirstColumn(me, me.currentColumn); // BOTTOM LEFT
+ me.TD(me, 1, 3, e = makeXonoticHeaderLabel(_("Statistics")));
+
+ me.TR(me);
+ me.TDempty(me, 0.25);
+ me.TD(me, 1, 2.5, e = makeXonoticCheckBox(0, "cl_allow_uidtracking", _("Allow player statistics to track your client")));
+ me.TR(me);
+ me.TDempty(me, 0.25);
+ me.TD(me, 1, 2.5, e = makeXonoticCheckBox(0, "cl_allow_uid2name", _("Allow player statistics to use your nickname")));
+ setDependent(e, "cl_allow_uidtracking", 1, 1);
+ me.gotoRC(me, 4, 3.1); // TOP RIGHT
+ //me.gotoRC(me, 12.5, 3.1); // BOTTOM RIGHT
+ //me.gotoRC(me, 12.5, 0); // BOTTOM LEFT
+ me.TDempty(me, 0.25);
+ me.TD(me, 9, 2.5, statslist = makeXonoticStatsList());
+ //setDependent(statslist, "cl_allow_uidtracking", 1, 1);
+
+ // =================
+ // COUNTRY SECTION
+ // =================
+ me.gotoRC(me, 16, 3.1); me.setFirstColumn(me, me.currentColumn); // BOTTOM SECTION, TOP POS
+ //me.gotoRC(me, 13.5, 3.1); me.setFirstColumn(me, me.currentColumn); // BOTTOM SECTION, TOP POS
+ //me.gotoRC(me, 0.5, 3.1); me.setFirstColumn(me, me.currentColumn); // TOP SECTION, TOP POS
+ me.TD(me, 1, 3, e = makeXonoticHeaderLabel(_("Country")));
+
+ me.TR(me);
+ me.TDempty(me, 0.5);
+ me.TD(me, 4.5, 2, e = makeXonoticLanguageList()); // todo: cl_country: create proper country list
+
+
+ // ================
+ // GENDER SECTION
+ // ================
+ me.gotoRC(me, 13.5, 3.1); me.setFirstColumn(me, me.currentColumn); // BOTTOM SECTION, TOP POS
+ //me.gotoRC(me, 19.5, 3.1); me.setFirstColumn(me, me.currentColumn); // BOTTOM SECTION, BOTTOM POS
+ //me.gotoRC(me, 6.5, 3.1); me.setFirstColumn(me, me.currentColumn); // TOP SECTION, BOTTOM POS
+ #if 0
+ me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Gender:")));
- me.TD(me, 1, GENDERWIDTH_ITEM, e = makeXonoticRadioButton(3, "cl_gender", "2", _("Female")));
- me.TD(me, 1, GENDERWIDTH_ITEM, e = makeXonoticRadioButton(3, "cl_gender", "1", _("Male")));
- me.TD(me, 1, GENDERWIDTH_ITEM, e = makeXonoticRadioButton(3, "cl_gender", "0", _("Undisclosed")));
++ me.TD(me, 1, 2, e = makeXonoticTextSlider("_cl_gender"));
+ e.addValue(e, ZCTX(_("GENDER^Undisclosed")), "0");
+ e.addValue(e, ZCTX(_("GENDER^Female")), "1");
+ e.addValue(e, ZCTX(_("GENDER^Male")), "2");
+ e.configureXonoticTextSliderValues(e);
+ #else
+ me.TD(me, 1, 3, e = makeXonoticHeaderLabel(_("Gender")));
+ me.TR(me);
+ #define GENDERWIDTH_OFFSET 0.25
+ #define GENDERWIDTH_LENGTH 2.5
+ #define GENDERWIDTH_ITEM (GENDERWIDTH_LENGTH / 3)
+ me.TDempty(me, GENDERWIDTH_OFFSET);
++ me.TD(me, 1, GENDERWIDTH_ITEM, e = makeXonoticRadioButton(3, "_cl_gender", "2", _("Female")));
++ me.TD(me, 1, GENDERWIDTH_ITEM, e = makeXonoticRadioButton(3, "_cl_gender", "1", _("Male")));
++ me.TD(me, 1, GENDERWIDTH_ITEM, e = makeXonoticRadioButton(3, "_cl_gender", "0", _("Undisclosed")));
+ #endif
+
+ me.gotoRC(me, me.rows - 1, 0);
+ me.TD(me, 1, me.columns, makeXonoticCommandButton(_("Apply immediately"), '0 0 0', "color -1 -1;name \"$_cl_name\";sendcvar cl_weaponpriority;sendcvar cl_autoswitch;sendcvar cl_forceplayermodels;sendcvar cl_forceplayermodelsfromxonotic;playermodel $_cl_playermodel;playerskin $_cl_playerskin", COMMANDBUTTON_APPLY));
+}
+#endif
--- /dev/null
- me.TD(me, 1, 3, makeXonoticCheckBoxEx(2, 0, "menu_sounds", _("Menu sounds")));
+#ifdef INTERFACE
+CLASS(XonoticAudioSettingsTab) EXTENDS(XonoticTab)
+ METHOD(XonoticAudioSettingsTab, fill, void(entity))
+ ATTRIB(XonoticAudioSettingsTab, title, string, _("Audio"))
+ ATTRIB(XonoticAudioSettingsTab, intendedWidth, float, 0.9)
+ ATTRIB(XonoticAudioSettingsTab, rows, float, 15.5)
+ ATTRIB(XonoticAudioSettingsTab, columns, float, 6.2) // added extra .2 for center space
++ ATTRIB(XonoticAudioSettingsTab, hiddenMenuSoundsSlider, entity, NULL)
+ENDCLASS(XonoticAudioSettingsTab)
+entity makeXonoticAudioSettingsTab();
+#endif
+
+#ifdef IMPLEMENTATION
+entity makeXonoticAudioSettingsTab()
+{
+ entity me;
+ me = spawnXonoticAudioSettingsTab();
+ me.configureDialog(me);
+ return me;
+}
+
+void XonoticAudioSettingsTab_fill(entity me)
+{
+ entity e, s;
+
+ me.TR(me);
+ s = makeXonoticDecibelsSlider(-40, 0, 0.4, "mastervolume");
+ me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Master:")));
+ me.TD(me, 1, 2, s);
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ s = makeXonoticDecibelsSlider(-40, 0, 0.4, "bgmvolume");
+ makeMulti(s, "snd_channel8volume");
+ me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Music:")));
+ me.TD(me, 1, 2, s);
+ setDependentStringNotEqual(e, "mastervolume", "0");
+ setDependentStringNotEqual(s, "mastervolume", "0");
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ s = makeXonoticDecibelsSlider(-40, 0, 0.4, "snd_staticvolume");
+ makeMulti(s, "snd_channel9volume");
+ me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, ZCTX(_("VOL^Ambient:"))));
+ me.TD(me, 1, 2, s);
+ setDependentStringNotEqual(e, "mastervolume", "0");
+ setDependentStringNotEqual(s, "mastervolume", "0");
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ s = makeXonoticDecibelsSlider(-40, 0, 0.4, "snd_channel0volume");
+ me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Info:")));
+ me.TD(me, 1, 2, s);
+ setDependentStringNotEqual(e, "mastervolume", "0");
+ setDependentStringNotEqual(s, "mastervolume", "0");
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ s = makeXonoticDecibelsSlider(-40, 0, 0.4, "snd_channel3volume");
+ me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Items:")));
+ me.TD(me, 1, 2, s);
+ setDependentStringNotEqual(e, "mastervolume", "0");
+ setDependentStringNotEqual(s, "mastervolume", "0");
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ s = makeXonoticDecibelsSlider(-40, 0, 0.4, "snd_channel6volume");
+ me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Pain:")));
+ me.TD(me, 1, 2, s);
+ setDependentStringNotEqual(e, "mastervolume", "0");
+ setDependentStringNotEqual(s, "mastervolume", "0");
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ s = makeXonoticDecibelsSlider(-40, 0, 0.4, "snd_channel7volume");
+ me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Player:")));
+ me.TD(me, 1, 2, s);
+ setDependentStringNotEqual(e, "mastervolume", "0");
+ setDependentStringNotEqual(s, "mastervolume", "0");
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ s = makeXonoticDecibelsSlider(-40, 0, 0.4, "snd_channel4volume");
+ me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Shots:")));
+ me.TD(me, 1, 2, s);
+ setDependentStringNotEqual(e, "mastervolume", "0");
+ setDependentStringNotEqual(s, "mastervolume", "0");
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ s = makeXonoticDecibelsSlider(-40, 0, 0.4, "snd_channel2volume");
+ me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Voice:")));
+ me.TD(me, 1, 2, s);
+ setDependentStringNotEqual(e, "mastervolume", "0");
+ setDependentStringNotEqual(s, "mastervolume", "0");
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ s = makeXonoticDecibelsSlider(-40, 0, 0.4, "snd_channel1volume");
+ makeMulti(s, "snd_channel5volume"); // @!#%'n Tuba
+ me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Weapons:")));
+ me.TD(me, 1, 2, s);
+ setDependentStringNotEqual(e, "mastervolume", "0");
+ setDependentStringNotEqual(s, "mastervolume", "0");
+ me.TR(me);
+ me.TR(me);
+ me.TD(me, 1, 3, makeXonoticCheckBox(0, "menu_snd_attenuation_method", _("New style sound attenuation")));
+ me.TR(me);
+ me.TD(me, 1, 3, makeXonoticCheckBox(0, "snd_mutewhenidle", _("Mute sounds when not active")));
+
+ me.gotoRC(me, 0, 3.2); me.setFirstColumn(me, me.currentColumn);
+ me.TD(me, 1, 1, makeXonoticTextLabel(0, _("Frequency:")));
+ me.TD(me, 1, 2, e = makeXonoticTextSlider("snd_speed"));
+ e.addValue(e, _("8 kHz"), "8000");
+ e.addValue(e, _("11.025 kHz"), "11025");
+ e.addValue(e, _("16 kHz"), "16000");
+ e.addValue(e, _("22.05 kHz"), "22050");
+ e.addValue(e, _("24 kHz"), "24000");
+ e.addValue(e, _("32 kHz"), "32000");
+ e.addValue(e, _("44.1 kHz"), "44100");
+ e.addValue(e, _("48 kHz"), "48000");
+ e.configureXonoticTextSliderValues(e);
+ me.TR(me);
+ me.TD(me, 1, 1, makeXonoticTextLabel(0, _("Channels:")));
+ me.TD(me, 1, 2, e = makeXonoticTextSlider("snd_channels"));
+ e.addValue(e, _("Mono"), "1");
+ e.addValue(e, _("Stereo"), "2");
+ e.addValue(e, _("2.1"), "3");
+ e.addValue(e, _("4"), "4");
+ e.addValue(e, _("5"), "5");
+ e.addValue(e, _("5.1"), "6");
+ e.addValue(e, _("6.1"), "7");
+ e.addValue(e, _("7.1"), "8");
+ e.configureXonoticTextSliderValues(e);
+ me.TR(me);
+ me.TR(me);
+ me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "snd_swapstereo", _("Swap stereo output channels")));
+ setDependent(e, "snd_channels", 1.5, 0.5);
+ me.TR(me);
+ me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "snd_spatialization_control", _("Headphone friendly mode")));
+ setDependent(e, "snd_channels", 1.5, 0.5);
+ me.TR(me);
+ me.TR(me);
+ me.TD(me, 1, 3, makeXonoticCheckBox(0, "cl_hitsound", _("Hit indication sound")));
+ e.sendCvars = true;
+ me.TR(me);
+ me.TD(me, 1, 3, makeXonoticCheckBox(0, "con_chatsound", _("Chat message sound")));
+ me.TR(me);
++ me.hiddenMenuSoundsSlider = makeXonoticSlider(1, 1, 1, "menu_sounds");
++ me.TD(me, 1, 1.2, makeXonoticSliderCheckBox(0, 1, me.hiddenMenuSoundsSlider, _("Menu sounds")));
++ me.TD(me, 1, 1.8, e = makeXonoticSliderCheckBox(2, 0, me.hiddenMenuSoundsSlider, _("Focus sounds")));
++ setDependent(e, "menu_sounds", 1, 2);
+ me.TR(me);
+ me.TR(me);
+ me.TD(me, 1, 1, makeXonoticTextLabel(0, _("Time announcer:")));
+ me.TD(me, 1, 2, e = makeXonoticTextSlider("cl_announcer_maptime"));
+ e.addValue(e, ZCTX(_("WRN^Disabled")), "0");
+ e.addValue(e, _("1 minute"), "1");
+ e.addValue(e, _("5 minutes"), "2");
+ e.addValue(e, ZCTX(_("WRN^Both")), "3");
+ e.configureXonoticTextSliderValues(e);
+ me.TR(me);
+ me.TD(me, 1, 1, makeXonoticTextLabel(0, _("Automatic taunts:")));
+ me.TD(me, 1, 2, e = makeXonoticTextSlider("cl_autotaunt"));
+ e.addValue(e, _("Never"), "0");
+ e.addValue(e, _("Sometimes"), "0.35");
+ e.addValue(e, _("Often"), "0.65");
+ e.addValue(e, _("Always"), "1");
+ e.configureXonoticTextSliderValues(e);
+ e.sendCvars = true;
+ me.TR(me);
+ me.TR(me);
+ if(cvar("developer"))
+ me.TD(me, 1, 3, makeXonoticCheckBox(0, "showsound", _("Debug info about sounds")));
+
+ me.gotoRC(me, me.rows - 1, 0);
+ me.TD(me, 1, me.columns, makeXonoticCommandButton(_("Apply immediately"), '0 0 0', "snd_restart; snd_attenuation_method_${menu_snd_attenuation_method}", COMMANDBUTTON_APPLY));
+}
+#endif
--- /dev/null
+#ifdef INTERFACE
+CLASS(XonoticGameCrosshairSettingsTab) EXTENDS(XonoticTab)
+ //METHOD(XonoticGameCrosshairSettingsTab, toString, string(entity))
+ METHOD(XonoticGameCrosshairSettingsTab, fill, void(entity))
+ METHOD(XonoticGameCrosshairSettingsTab, showNotify, void(entity))
+ ATTRIB(XonoticGameCrosshairSettingsTab, title, string, _("Crosshair"))
+ ATTRIB(XonoticGameCrosshairSettingsTab, intendedWidth, float, 0.9)
+ ATTRIB(XonoticGameCrosshairSettingsTab, rows, float, 13)
+ ATTRIB(XonoticGameCrosshairSettingsTab, columns, float, 6.2)
+ENDCLASS(XonoticGameCrosshairSettingsTab)
+entity makeXonoticGameCrosshairSettingsTab();
+#endif
+
+#ifdef IMPLEMENTATION
+void XonoticGameCrosshairSettingsTab_showNotify(entity me)
+{
+ loadAllCvars(me);
+}
+entity makeXonoticGameCrosshairSettingsTab()
+{
+ entity me;
+ me = spawnXonoticGameCrosshairSettingsTab();
+ me.configureDialog(me);
+ return me;
+}
+
+void XonoticGameCrosshairSettingsTab_fill(entity me)
+{
+ entity e;
+ float i;
+
+ // crosshair_enabled: 0 = no crosshair options, 1 = no crosshair selection, but everything else enabled, 2 = all crosshair options enabled
+ // FIXME: In the future, perhaps make one global crosshair_type cvar which has 0 for disabled, 1 for custom, 2 for per weapon, etc?
+ me.TR(me); //me.gotoRC(me, 0, 3.2); me.setFirstColumn(me, me.currentColumn);
+ me.TD(me, 1, 1, e = makeXonoticRadioButton(3, "crosshair_enabled", "0", _("No crosshair")));
+ //me.TR(me);
+ me.TD(me, 1, 1, e = makeXonoticRadioButton(3, "crosshair_per_weapon", string_null, _("Per weapon")));
+ makeMulti(e, "crosshair_enabled");
+ //me.TR(me);
+ me.TD(me, 1, 1, e = makeXonoticRadioButton(3, "crosshair_enabled", "2", _("Custom")));
+ me.TR(me);
+ me.TDempty(me, 0.1);
+ for(i = 1; i <= 14; ++i) {
+ me.TDNoMargin(me, 1, 2 / 14, e = makeXonoticCrosshairButton(4, i), '1 1 0');
+ setDependentAND(e, "crosshair_per_weapon", 0, 0, "crosshair_enabled", 1, 2);
+ }
+ // show a larger preview of the selected crosshair
+ me.TDempty(me, 0.1);
+ me.TDNoMargin(me, 3, 0.8, e = makeXonoticCrosshairButton(7, -1), '1 1 0'); // crosshair -1 makes this a preview
+ setDependentAND(e, "crosshair_per_weapon", 0, 0, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TDempty(me, 0.1);
+ for(i = 15; i <= 28; ++i) {
+ me.TDNoMargin(me, 1, 2 / 14, e = makeXonoticCrosshairButton(4, i), '1 1 0');
+ setDependentAND(e, "crosshair_per_weapon", 0, 0, "crosshair_enabled", 1, 2);
+ }
+ me.TR(me);
++ me.TDempty(me, 0.1);
++ for(i = 29; i <= 42; ++i) {
++ me.TDNoMargin(me, 1, 2 / 14, e = makeXonoticCrosshairButton(4, i), '1 1 0');
++ setDependentAND(e, "crosshair_per_weapon", 0, 0, "crosshair_enabled", 1, 2);
++ }
+ me.TR(me);
+ me.TDempty(me, 0.1);
+ me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Crosshair size:")));
+ setDependent(e, "crosshair_enabled", 1, 2);
+ me.TD(me, 1, 1.9, e = makeXonoticSlider(0.1, 1.0, 0.01, "crosshair_size"));
+ setDependent(e, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TDempty(me, 0.1);
+ me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Crosshair alpha:")));
+ setDependent(e, "crosshair_enabled", 1, 2);
+ me.TD(me, 1, 1.9, e = makeXonoticSlider(0, 1, 0.1, "crosshair_alpha"));
+ setDependent(e, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TDempty(me, 0.1);
+ me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Crosshair color:")));
+ setDependent(e, "crosshair_enabled", 1, 2);
+ me.TD(me, 1, 0.9, e = makeXonoticRadioButton(5, "crosshair_color_special", "1", _("Per weapon")));
+ setDependent(e, "crosshair_enabled", 1, 2);
+ me.TD(me, 1, 1, e = makeXonoticRadioButton(5, "crosshair_color_special", "2", _("By health")));
+ setDependent(e, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ me.TD(me, 1, 0.8, e = makeXonoticRadioButton(5, "crosshair_color_special", "0", _("Custom")));
+ setDependent(e, "crosshair_enabled", 1, 2);
+ me.TD(me, 2, 2, e = makeXonoticColorpickerString("crosshair_color", "crosshair_color"));
+ setDependentAND(e, "crosshair_color_special", 0, 0, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TR(me);
+ me.TR(me);
+ me.TDempty(me, 0.1);
+ me.TD(me, 1, 2.9, e = makeXonoticCheckBox(0, "crosshair_ring", _("Use rings to indicate weapon status")));
+ makeMulti(e, "crosshair_ring_reload");
+ setDependent(e, "crosshair_enabled", 1, 2);
+ //me.TR(me);
+ // me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Ring size:")));
+ // setDependentAND(e, "crosshair_ring", 1, 1, "crosshair_enabled", 1, 2);
+ // me.TD(me, 1, 2, e = makeXonoticSlider(2, 4, 0.1, "crosshair_ring_size"));
+ // setDependentAND(e, "crosshair_ring", 1, 1, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TDempty(me, 0.3);
+ me.TD(me, 1, 0.9, e = makeXonoticTextLabel(0, _("Ring alpha:")));
+ setDependentAND(e, "crosshair_ring", 1, 1, "crosshair_enabled", 1, 2);
+ me.TD(me, 1, 1.8, e = makeXonoticSlider(0.1, 1, 0.1, "crosshair_ring_alpha"));
+ setDependentAND(e, "crosshair_ring", 1, 1, "crosshair_enabled", 1, 2);
+
+ me.gotoRC(me, 0, 3.2); me.setFirstColumn(me, me.currentColumn);
+ me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "crosshair_dot", _("Enable center crosshair dot")));
+ setDependent(e, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TDempty(me, 0.1);
+ me.TD(me, 1, 0.9, e = makeXonoticTextLabel(0, _("Dot size:")));
+ setDependentAND(e, "crosshair_dot", 1, 1, "crosshair_enabled", 1, 2);
+ me.TD(me, 1, 2, e = makeXonoticSlider(0.2, 2, 0.1, "crosshair_dot_size"));
+ setDependentAND(e, "crosshair_dot", 1, 1, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TDempty(me, 0.1);
+ me.TD(me, 1, 0.9, e = makeXonoticTextLabel(0, _("Dot alpha:")));
+ setDependentAND(e, "crosshair_dot", 1, 1, "crosshair_enabled", 1, 2);
+ me.TD(me, 1, 2, e = makeXonoticSlider(0.1, 1, 0.1, "crosshair_dot_alpha"));
+ setDependentAND(e, "crosshair_dot", 1, 1, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TDempty(me, 0.1);
+ me.TD(me, 1, 0.9, e = makeXonoticTextLabel(0, _("Dot color:")));
+ setDependentAND(e, "crosshair_dot", 1, 1, "crosshair_enabled", 1, 2);
+ me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "crosshair_dot_color_custom", "0", _("Use normal crosshair color")));
+ setDependentAND(e, "crosshair_dot", 1, 1, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ me.TD(me, 1, 0.8, e = makeXonoticRadioButton(1, "crosshair_dot_color_custom", "1", _("Custom")));
+ setDependentAND(e, "crosshair_dot", 1, 1, "crosshair_enabled", 1, 2);
+ me.TD(me, 2, 2, e = makeXonoticColorpickerString("crosshair_dot_color", "crosshair_dot_color"));
+ setDependentAND3(e, "crosshair_dot", 1, 1, "crosshair_enabled", 1, 2, "crosshair_dot_color_custom", 1, 1);
+ me.TR(me);
+ me.TR(me);
+ me.TR(me);
+ me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "crosshair_effect_scalefade", _("Smooth effects of crosshairs")));
+ setDependent(e, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "crosshair_hittest_blur", _("Blur crosshair if the shot is obstructed")));
+ setDependentAND(e, "crosshair_hittest", 1, 100, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TD(me, 1, 3, e = makeXonoticCheckBoxEx(1.25, 0, "crosshair_hittest_scale", _("Enlarge crosshair if targeting an enemy")));
+ setDependentAND(e, "crosshair_hittest", 1, 100, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TD(me, 1, 3, e = makeXonoticCheckBoxEx(0.5, 0, "crosshair_hitindication", _("Animate crosshair when hitting an enemy")));
+ setDependent(e, "crosshair_enabled", 1, 2);
+ me.TR(me);
+ me.TD(me, 1, 3, e = makeXonoticCheckBoxEx(0.25, 0, "crosshair_pickup", _("Animate crosshair when picking up an item")));
+ setDependent(e, "crosshair_enabled", 1, 2);
+ /*me.TR(me);
+ me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Hit testing:")));
+ me.TD(me, 1, 2, e = makeXonoticTextSlider("crosshair_hittest"));
+ e.addValue(e, ZCTX(_("HTTST^Disabled")), "0");
+ e.addValue(e, ZCTX(_("HTTST^TrueAim")), "1");
+ e.addValue(e, ZCTX(_("HTTST^Enemies")), "1.25");
+ e.configureXonoticTextSliderValues(e);
+ setDependent(e, "crosshair_enabled", 1, 2);*/
+
+ /*me.TR(me);
+
+ me.gotoRC(me, me.rows - 1, 0);
+ me.TD(me, 1, me.columns, e = makeXonoticButton(_("OK"), '0 0 0'));
+ e.onClick = Dialog_Close;
+ e.onClickEntity = me;*/
+}
+#endif
--- /dev/null
+#ifdef INTERFACE
+CLASS(XonoticWinnerDialog) EXTENDS(XonoticDialog)
+ METHOD(XonoticWinnerDialog, fill, void(entity))
++ METHOD(XonoticWinnerDialog, focusEnter, void(entity))
+ ATTRIB(XonoticWinnerDialog, title, string, _("Winner"))
+ ATTRIB(XonoticWinnerDialog, color, vector, SKINCOLOR_DIALOG_SINGLEPLAYER)
+ ATTRIB(XonoticWinnerDialog, intendedWidth, float, 0.32)
+ ATTRIB(XonoticWinnerDialog, rows, float, 12)
+ ATTRIB(XonoticWinnerDialog, columns, float, 3)
+ENDCLASS(XonoticWinnerDialog)
+#endif
+
+#ifdef IMPLEMENTATION
+void XonoticWinnerDialog_fill(entity me)
+{
+ entity e;
+
+ me.TR(me);
+ me.TD(me, me.rows - 2, me.columns, e = makeXonoticImage("/gfx/winner", -1));
+
+ me.gotoRC(me, me.rows - 1, 0);
+ me.TD(me, 1, me.columns, e = makeXonoticButton(_("OK"), '0 0 0'));
+ e.onClick = Dialog_Close;
+ e.onClickEntity = me;
+}
++void XonoticWinnerDialog_focusEnter(entity me)
++{
++ m_play_click_sound(MENU_SOUND_WINNER);
++}
+#endif
--- /dev/null
-
+#ifdef INTERFACE
+CLASS(XonoticGametypeList) EXTENDS(XonoticListBox)
+ METHOD(XonoticGametypeList, configureXonoticGametypeList, void(entity))
+ ATTRIB(XonoticGametypeList, rowsPerItem, float, 2)
+ METHOD(XonoticGametypeList, drawListBoxItem, void(entity, float, vector, float))
+ METHOD(XonoticGametypeList, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(XonoticGametypeList, setSelected, void(entity, float))
+ METHOD(XonoticGametypeList, loadCvars, void(entity))
+ METHOD(XonoticGametypeList, saveCvars, void(entity))
+ METHOD(XonoticGametypeList, keyDown, float(entity, float, float, float))
++ METHOD(XonoticGametypeList, clickListBoxItem, void(entity, float, vector))
+
+ ATTRIB(XonoticGametypeList, realFontSize, vector, '0 0 0')
+ ATTRIB(XonoticGametypeList, realUpperMargin, float, 0)
+ ATTRIB(XonoticGametypeList, columnIconOrigin, float, 0)
+ ATTRIB(XonoticGametypeList, columnIconSize, float, 0)
+ ATTRIB(XonoticGametypeList, columnNameOrigin, float, 0)
+ ATTRIB(XonoticGametypeList, columnNameSize, float, 0)
+ENDCLASS(XonoticGametypeList)
+entity makeXonoticGametypeList();
+#endif
+
+#ifdef IMPLEMENTATION
+
+entity makeXonoticGametypeList(void)
+{
+ entity me;
+ me = spawnXonoticGametypeList();
+ me.configureXonoticGametypeList(me);
+ return me;
+}
+void XonoticGametypeList_configureXonoticGametypeList(entity me)
+{
+ float i;
+ me.configureXonoticListBox(me);
+ me.nItems = GameType_GetCount();
+
+ // we want the pics mipmapped
+ for(i = 0; i < GameType_GetCount(); ++i)
+ draw_PreloadPictureWithFlags(GameType_GetIcon(i), PRECACHE_PIC_MIPMAP);
+
+ me.loadCvars(me);
+}
+void XonoticGametypeList_setSelected(entity me, float i)
+{
+ SUPER(XonoticGametypeList).setSelected(me, i);
+ me.saveCvars(me);
+}
-
+void XonoticGametypeList_loadCvars(entity me)
+{
+ float t;
+ t = MapInfo_CurrentGametype();
+ float i;
+ for(i = 0; i < GameType_GetCount(); ++i)
+ if(t == GameType_GetID(i))
+ break;
+ if(i >= GameType_GetCount())
+ {
+ for(i = 0; i < GameType_GetCount(); ++i)
+ if(t == MAPINFO_TYPE_DEATHMATCH)
+ break;
+ if(i >= GameType_GetCount())
+ i = 0;
+ }
+ me.setSelected(me, i);
+ // do we need this: me.parent.gameTypeChangeNotify(me.parent); // to make sure
+}
+void XonoticGametypeList_saveCvars(entity me)
+{
+ float t;
+ t = GameType_GetID(me.selectedItem);
+ if(t == MapInfo_CurrentGametype())
+ return;
+ MapInfo_SwitchGameType(t);
+ me.parent.gameTypeChangeNotify(me.parent);
+}
+void XonoticGametypeList_drawListBoxItem(entity me, float i, vector absSize, float isSelected)
+{
+ string s1, s2;
+
+ if(isSelected)
+ draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
+
+ draw_Picture(me.columnIconOrigin * eX, GameType_GetIcon(i), me.columnIconSize * eX + eY, '1 1 1', SKINALPHA_LISTBOX_SELECTED);
+ s1 = GameType_GetName(i);
+
+ if(_MapInfo_GetTeamPlayBool(GameType_GetID(i)))
+ s2 = _("teamplay");
+ else
+ s2 = _("free for all");
+
+ vector save_fontscale = draw_fontscale;
+ float f = draw_CondensedFontFactor(strcat(s1, " ", s2), false, me.realFontSize, 1);
+ draw_fontscale.x *= f;
+ vector fs = me.realFontSize;
+ fs.x *= f;
+ draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s1, fs, '1 1 1', SKINALPHA_TEXT, 0);
+ draw_Text(me.realUpperMargin * eY + (me.columnNameOrigin + 1.0 * (me.columnNameSize - draw_TextWidth(s2, 0, fs))) * eX, s2, fs, SKINCOLOR_TEXT, SKINALPHA_TEXT, 0);
+ draw_fontscale = save_fontscale;
+}
+void XonoticGametypeList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ me.itemAbsSize = '0 0 0';
+ SUPER(XonoticServerList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
+
+ me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight));
+ me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth)));
+ me.realUpperMargin = 0.5 * (1 - me.realFontSize.y);
+ me.columnIconOrigin = 0;
+ me.columnIconSize = me.itemAbsSize.y / me.itemAbsSize.x;
+ me.columnNameOrigin = me.columnIconOrigin + me.columnIconSize + (0.5 * me.realFontSize.x);
+ me.columnNameSize = 1 - me.columnIconSize - (1.5 * me.realFontSize.x);
+}
+float XonoticGametypeList_keyDown(entity me, float scan, float ascii, float shift)
+{
+ if(scan == K_ENTER || scan == K_KP_ENTER)
+ {
++ m_play_click_sound(MENU_SOUND_EXECUTE);
+ me.parent.gameTypeSelectNotify(me.parent);
+ return 1;
+ }
+
+ return SUPER(XonoticGametypeList).keyDown(me, scan, ascii, shift);
+}
++void XonoticGametypeList_clickListBoxItem(entity me, float i, vector where)
++{
++ m_play_click_sound(MENU_SOUND_SELECT);
++}
+#endif
--- /dev/null
- localcmd("exec binds-default.cfg\n");
+#ifdef INTERFACE
+CLASS(XonoticKeyBinder) EXTENDS(XonoticListBox)
+ METHOD(XonoticKeyBinder, configureXonoticKeyBinder, void(entity))
+ ATTRIB(XonoticKeyBinder, rowsPerItem, float, 1)
+ METHOD(XonoticKeyBinder, drawListBoxItem, void(entity, float, vector, float))
+ METHOD(XonoticKeyBinder, doubleClickListBoxItem, void(entity, float, vector))
+ METHOD(XonoticKeyBinder, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(XonoticKeyBinder, setSelected, void(entity, float))
+ METHOD(XonoticKeyBinder, keyDown, float(entity, float, float, float))
+ METHOD(XonoticKeyBinder, keyGrabbed, void(entity, float, float))
+
+ ATTRIB(XonoticKeyBinder, realFontSize, vector, '0 0 0')
+ ATTRIB(XonoticKeyBinder, realUpperMargin, float, 0)
+ ATTRIB(XonoticKeyBinder, columnFunctionOrigin, float, 0)
+ ATTRIB(XonoticKeyBinder, columnFunctionSize, float, 0)
+ ATTRIB(XonoticKeyBinder, columnKeysOrigin, float, 0)
+ ATTRIB(XonoticKeyBinder, columnKeysSize, float, 0)
+
+ ATTRIB(XonoticKeyBinder, previouslySelected, float, -1)
+ ATTRIB(XonoticKeyBinder, inMouseHandler, float, 0)
+ ATTRIB(XonoticKeyBinder, userbindEditButton, entity, NULL)
+ ATTRIB(XonoticKeyBinder, keyGrabButton, entity, NULL)
+ ATTRIB(XonoticKeyBinder, clearButton, entity, NULL)
+ ATTRIB(XonoticKeyBinder, userbindEditDialog, entity, NULL)
+ METHOD(XonoticKeyBinder, editUserbind, void(entity, string, string, string))
+ENDCLASS(XonoticKeyBinder)
+entity makeXonoticKeyBinder();
+void KeyBinder_Bind_Change(entity btn, entity me);
+void KeyBinder_Bind_Clear(entity btn, entity me);
+void KeyBinder_Bind_Edit(entity btn, entity me);
+#endif
+
+#ifdef IMPLEMENTATION
+
+const string KEY_NOT_BOUND_CMD = "// not bound";
+
+const float MAX_KEYS_PER_FUNCTION = 2;
+const float MAX_KEYBINDS = 256;
+string Xonotic_KeyBinds_Functions[MAX_KEYBINDS];
+string Xonotic_KeyBinds_Descriptions[MAX_KEYBINDS];
+float Xonotic_KeyBinds_Count = -1;
+
+void Xonotic_KeyBinds_Read()
+{
+ float fh;
+ string s;
+
+ Xonotic_KeyBinds_Count = 0;
+ fh = fopen(language_filename("keybinds.txt"), FILE_READ);
+ if(fh < 0)
+ return;
+ while((s = fgets(fh)))
+ {
+ if(tokenize_console(s) != 2)
+ continue;
+ Xonotic_KeyBinds_Functions[Xonotic_KeyBinds_Count] = strzone(argv(0));
+ Xonotic_KeyBinds_Descriptions[Xonotic_KeyBinds_Count] = strzone(argv(1));
+ ++Xonotic_KeyBinds_Count;
+ if(Xonotic_KeyBinds_Count >= MAX_KEYBINDS)
+ break;
+ }
+ fclose(fh);
+}
+
+entity makeXonoticKeyBinder()
+{
+ entity me;
+ me = spawnXonoticKeyBinder();
+ me.configureXonoticKeyBinder(me);
+ return me;
+}
+void replace_bind(string from, string to)
+{
+ float n, j, k;
+ n = tokenize(findkeysforcommand(from, 0)); // uses '...' strings
+ for(j = 0; j < n; ++j)
+ {
+ k = stof(argv(j));
+ if(k != -1)
+ localcmd("\nbind \"", keynumtostring(k), "\" \"", to, "\"\n");
+ }
+ if(n)
+ cvar_set("_hud_showbinds_reload", "1");
+}
+void XonoticKeyBinder_configureXonoticKeyBinder(entity me)
+{
+ me.configureXonoticListBox(me);
+ if(Xonotic_KeyBinds_Count < 0)
+ Xonotic_KeyBinds_Read();
+ me.nItems = Xonotic_KeyBinds_Count;
+ me.setSelected(me, 0);
+
+ // TEMP: Xonotic 0.1 to later
+ replace_bind("impulse 1", "weapon_group_1");
+ replace_bind("impulse 2", "weapon_group_2");
+ replace_bind("impulse 3", "weapon_group_3");
+ replace_bind("impulse 4", "weapon_group_4");
+ replace_bind("impulse 5", "weapon_group_5");
+ replace_bind("impulse 6", "weapon_group_6");
+ replace_bind("impulse 7", "weapon_group_7");
+ replace_bind("impulse 8", "weapon_group_8");
+ replace_bind("impulse 9", "weapon_group_9");
+ replace_bind("impulse 14", "weapon_group_0");
+}
+void XonoticKeyBinder_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ SUPER(XonoticKeyBinder).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);
+
+ me.columnFunctionOrigin = 0;
+ me.columnKeysSize = me.realFontSize.x * 12;
+ me.columnFunctionSize = 1 - me.columnKeysSize - 2 * me.realFontSize.x;
+ me.columnKeysOrigin = me.columnFunctionOrigin + me.columnFunctionSize + me.realFontSize.x;
+
+ if(me.userbindEditButton)
+ me.userbindEditButton.disabled = (substring(Xonotic_KeyBinds_Descriptions[me.selectedItem], 0, 1) != "$");
+}
+void KeyBinder_Bind_Change(entity btn, entity me)
+{
+ string func;
+
+ func = Xonotic_KeyBinds_Functions[me.selectedItem];
+ if(func == "")
+ return;
+
+ me.keyGrabButton.forcePressed = 1;
+ me.clearButton.disabled = 1;
+ keyGrabber = me;
+}
+void XonoticKeyBinder_keyGrabbed(entity me, float key, float ascii)
+{
+ float n, j, k, nvalid;
+ string func;
+
+ me.keyGrabButton.forcePressed = 0;
+ me.clearButton.disabled = 0;
+
+ if(key == K_ESCAPE)
+ return;
+
+ // forbid these keys from being bound in the menu
+ if(key == K_CAPSLOCK || key == K_NUMLOCK)
+ {
+ KeyBinder_Bind_Change(me, me);
+ return;
+ }
+
+ func = Xonotic_KeyBinds_Functions[me.selectedItem];
+ if(func == "")
+ return;
+
+ n = tokenize(findkeysforcommand(func, 0)); // uses '...' strings
+ nvalid = 0;
+ for(j = 0; j < n; ++j)
+ {
+ k = stof(argv(j));
+ if(k != -1)
+ ++nvalid;
+ }
+ if(nvalid >= MAX_KEYS_PER_FUNCTION)
+ {
+ for(j = 0; j < n; ++j)
+ {
+ k = stof(argv(j));
+ if(k != -1)
+ //localcmd("\nunbind \"", keynumtostring(k), "\"\n");
+ localcmd("\nbind \"", keynumtostring(k), "\" \"", KEY_NOT_BOUND_CMD, "\"\n");
+ }
+ }
++ m_play_click_sound(MENU_SOUND_SELECT);
+ localcmd("\nbind \"", keynumtostring(key), "\" \"", func, "\"\n");
+ localcmd("-zoom\n"); // to make sure we aren't in togglezoom'd state
+ cvar_set("_hud_showbinds_reload", "1");
+}
+void XonoticKeyBinder_editUserbind(entity me, string theName, string theCommandPress, string theCommandRelease)
+{
+ string func, descr;
+
+ if(!me.userbindEditDialog)
+ return;
+
+ func = Xonotic_KeyBinds_Functions[me.selectedItem];
+ if(func == "")
+ return;
+
+ descr = Xonotic_KeyBinds_Descriptions[me.selectedItem];
+ if(substring(descr, 0, 1) != "$")
+ return;
+ descr = substring(descr, 1, strlen(descr) - 1);
+
+ // Hooray! It IS a user bind!
+ cvar_set(strcat(descr, "_description"), theName);
+ cvar_set(strcat(descr, "_press"), theCommandPress);
+ cvar_set(strcat(descr, "_release"), theCommandRelease);
+}
+void KeyBinder_Bind_Edit(entity btn, entity me)
+{
+ string func, descr;
+
+ if(!me.userbindEditDialog)
+ return;
+
+ func = Xonotic_KeyBinds_Functions[me.selectedItem];
+ if(func == "")
+ return;
+
+ descr = Xonotic_KeyBinds_Descriptions[me.selectedItem];
+ if(substring(descr, 0, 1) != "$")
+ return;
+ descr = substring(descr, 1, strlen(descr) - 1);
+
+ // Hooray! It IS a user bind!
+ me.userbindEditDialog.loadUserBind(me.userbindEditDialog, cvar_string(strcat(descr, "_description")), cvar_string(strcat(descr, "_press")), cvar_string(strcat(descr, "_release")));
+
+ DialogOpenButton_Click(btn, me.userbindEditDialog);
+}
+void KeyBinder_Bind_Clear(entity btn, entity me)
+{
+ float n, j, k;
+ string func;
+
+ func = Xonotic_KeyBinds_Functions[me.selectedItem];
+ if(func == "")
+ return;
+
+ n = tokenize(findkeysforcommand(func, 0)); // uses '...' strings
+ for(j = 0; j < n; ++j)
+ {
+ k = stof(argv(j));
+ if(k != -1)
+ //localcmd("\nunbind \"", keynumtostring(k), "\"\n");
+ localcmd("\nbind \"", keynumtostring(k), "\" \"", KEY_NOT_BOUND_CMD, "\"\n");
+ }
++ m_play_click_sound(MENU_SOUND_CLEAR);
+ localcmd("-zoom\n"); // to make sure we aren't in togglezoom'd state
+ cvar_set("_hud_showbinds_reload", "1");
+}
+void KeyBinder_Bind_Reset_All(entity btn, entity me)
+{
+ localcmd("unbindall\n");
++ localcmd("exec binds-xonotic.cfg\n");
+ localcmd("-zoom\n"); // to make sure we aren't in togglezoom'd state
+ cvar_set("_hud_showbinds_reload", "1");
+}
+void XonoticKeyBinder_doubleClickListBoxItem(entity me, float i, vector where)
+{
+ KeyBinder_Bind_Change(NULL, me);
+}
+void XonoticKeyBinder_setSelected(entity me, float i)
+{
+ // handling of "unselectable" items
+ i = floor(0.5 + bound(0, i, me.nItems - 1));
+ if(me.pressed == 0 || me.pressed == 1) // keyboard or scrolling - skip unselectable items
+ {
+ if(i > me.previouslySelected)
+ {
+ while((i < me.nItems - 1) && (Xonotic_KeyBinds_Functions[i] == ""))
+ ++i;
+ }
+ while((i > 0) && (Xonotic_KeyBinds_Functions[i] == ""))
+ --i;
+ while((i < me.nItems - 1) && (Xonotic_KeyBinds_Functions[i] == ""))
+ ++i;
+ }
+ if(me.pressed == 3) // released the mouse - fall back to last valid item
+ {
+ if(Xonotic_KeyBinds_Functions[i] == "")
+ i = me.previouslySelected;
+ }
+ if(Xonotic_KeyBinds_Functions[i] != "")
+ me.previouslySelected = i;
+ if(me.userbindEditButton)
+ me.userbindEditButton.disabled = (substring(Xonotic_KeyBinds_Descriptions[i], 0, 1) != "$");
+ SUPER(XonoticKeyBinder).setSelected(me, i);
+}
+float XonoticKeyBinder_keyDown(entity me, float key, float ascii, float shift)
+{
+ float r;
+ r = 1;
+ switch(key)
+ {
+ case K_ENTER:
+ case K_KP_ENTER:
+ case K_SPACE:
+ KeyBinder_Bind_Change(me, me);
+ break;
+ case K_DEL:
+ case K_KP_DEL:
+ case K_BACKSPACE:
+ KeyBinder_Bind_Clear(me, me);
+ break;
+ default:
+ r = SUPER(XonoticKeyBinder).keyDown(me, key, ascii, shift);
+ break;
+ }
+ return r;
+}
+void XonoticKeyBinder_drawListBoxItem(entity me, float i, vector absSize, float isSelected)
+{
+ string s;
+ float j, k, n;
+ vector theColor;
+ float theAlpha;
+ string func, descr;
+ float extraMargin;
+
+ descr = Xonotic_KeyBinds_Descriptions[i];
+ func = Xonotic_KeyBinds_Functions[i];
+
+ if(func == "")
+ {
+ theAlpha = 1;
+ theColor = SKINCOLOR_KEYGRABBER_TITLES;
+ theAlpha = SKINALPHA_KEYGRABBER_TITLES;
+ extraMargin = 0;
+ }
+ else
+ {
+ if(isSelected)
+ {
+ if(keyGrabber == me)
+ draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_WAITING, SKINALPHA_LISTBOX_WAITING);
+ else
+ draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
+ }
+ theAlpha = SKINALPHA_KEYGRABBER_KEYS;
+ theColor = SKINCOLOR_KEYGRABBER_KEYS;
+ extraMargin = me.realFontSize.x * 0.5;
+ }
+
+ if(substring(descr, 0, 1) == "$")
+ {
+ s = substring(descr, 1, strlen(descr) - 1);
+ descr = cvar_string(strcat(s, "_description"));
+ if(descr == "")
+ descr = s;
+ if(cvar_string(strcat(s, "_press")) == "")
+ if(cvar_string(strcat(s, "_release")) == "")
+ theAlpha *= SKINALPHA_DISABLED;
+ }
+
+ s = draw_TextShortenToWidth(descr, me.columnFunctionSize, 0, me.realFontSize);
+ draw_Text(me.realUpperMargin * eY + extraMargin * eX, s, me.realFontSize, theColor, theAlpha, 0);
+ if(func != "")
+ {
+ n = tokenize(findkeysforcommand(func, 0)); // uses '...' strings
+ s = "";
+ for(j = 0; j < n; ++j)
+ {
+ k = stof(argv(j));
+ if(k != -1)
+ {
+ if(s != "")
+ s = strcat(s, ", ");
+ s = strcat(s, keynumtostring(k));
+ }
+ }
+ s = draw_TextShortenToWidth(s, me.columnKeysSize, 0, me.realFontSize);
+ draw_CenterText(me.realUpperMargin * eY + (me.columnKeysOrigin + 0.5 * me.columnKeysSize) * eX, s, me.realFontSize, theColor, theAlpha, 0);
+ }
+}
+#endif
--- /dev/null
- if(scan == K_ENTER || scan == K_KP_ENTER) {
+#ifdef INTERFACE
+CLASS(XonoticLanguageList) EXTENDS(XonoticListBox)
+ METHOD(XonoticLanguageList, configureXonoticLanguageList, void(entity))
+ ATTRIB(XonoticLanguageList, rowsPerItem, float, 1)
+ METHOD(XonoticLanguageList, drawListBoxItem, void(entity, float, vector, float))
+ METHOD(XonoticLanguageList, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(XonoticLanguageList, setSelected, void(entity, float))
+ METHOD(XonoticLanguageList, loadCvars, void(entity))
+ METHOD(XonoticLanguageList, saveCvars, void(entity))
+
+ ATTRIB(XonoticLanguageList, realFontSize, vector, '0 0 0')
+ ATTRIB(XonoticLanguageList, realUpperMargin, float, 0)
+ ATTRIB(XonoticLanguageList, columnNameOrigin, float, 0)
+ ATTRIB(XonoticLanguageList, columnNameSize, float, 0)
+ ATTRIB(XonoticLanguageList, columnPercentageOrigin, float, 0)
+ ATTRIB(XonoticLanguageList, columnPercentageSize, float, 0)
+
+ METHOD(XonoticLanguageList, doubleClickListBoxItem, void(entity, float, vector))
+ METHOD(XonoticLanguageList, keyDown, float(entity, float, float, float)) // enter handling
+
+ METHOD(XonoticLanguageList, destroy, void(entity))
+
+ ATTRIB(XonoticLanguageList, languagelist, float, -1)
+ METHOD(XonoticLanguageList, getLanguages, void(entity))
+ METHOD(XonoticLanguageList, setLanguage, void(entity))
+ METHOD(XonoticLanguageList, languageParameter, string(entity, float, float))
+
+ ATTRIB(XonoticLanguageList, name, string, "languageselector") // change this to make it noninteractive (for first run dialog)
+ENDCLASS(XonoticLanguageList)
+
+entity makeXonoticLanguageList();
+void SetLanguage_Click(entity btn, entity me);
+#endif
+
+#ifdef IMPLEMENTATION
+
+const float LANGPARM_ID = 0;
+const float LANGPARM_NAME = 1;
+const float LANGPARM_NAME_LOCALIZED = 2;
+const float LANGPARM_PERCENTAGE = 3;
+const float LANGPARM_COUNT = 4;
+
+entity makeXonoticLanguageList()
+{
+ entity me;
+ me = spawnXonoticLanguageList();
+ me.configureXonoticLanguageList(me);
+ return me;
+}
+
+void XonoticLanguageList_configureXonoticLanguageList(entity me)
+{
+ me.configureXonoticListBox(me);
+ me.getLanguages(me);
+ me.loadCvars(me);
+}
+
+void XonoticLanguageList_drawListBoxItem(entity me, float i, vector absSize, float isSelected)
+{
+ string s, p;
+ if(isSelected)
+ draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
+
+ s = me.languageParameter(me, i, LANGPARM_NAME_LOCALIZED);
+
+ vector save_fontscale = draw_fontscale;
+ float f = draw_CondensedFontFactor(s, false, me.realFontSize, 1);
+ draw_fontscale.x *= f;
+ vector fs = me.realFontSize;
+ fs.x *= f;
+ draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, fs, SKINCOLOR_TEXT, SKINALPHA_TEXT, 0);
+ draw_fontscale = save_fontscale;
+
+ p = me.languageParameter(me, i, LANGPARM_PERCENTAGE);
+ if(p != "")
+ {
+ vector save_fontscale = draw_fontscale;
+ float f = draw_CondensedFontFactor(p, false, me.realFontSize, 1);
+ draw_fontscale.x *= f;
+ vector fs = me.realFontSize;
+ fs.x *= f;
+ draw_Text(me.realUpperMargin * eY + (me.columnPercentageOrigin + (me.columnPercentageSize - draw_TextWidth(p, 0, fs))) * eX, p, fs, SKINCOLOR_TEXT, SKINALPHA_TEXT, 0);
+ draw_fontscale = save_fontscale;
+ }
+}
+
+void XonoticLanguageList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ SUPER(XonoticLanguageList).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);
+ me.columnPercentageSize = me.realFontSize.x * 3;
+ me.columnPercentageOrigin = 1 - me.columnPercentageSize;
+ me.columnNameOrigin = 0;
+ me.columnNameSize = me.columnPercentageOrigin;
+}
+
+void XonoticLanguageList_setSelected(entity me, float i)
+{
+ SUPER(XonoticLanguageList).setSelected(me, i);
+ me.saveCvars(me);
+}
+
+void XonoticLanguageList_loadCvars(entity me)
+{
+ string s;
+ float i, n;
+ s = cvar_string("_menu_prvm_language");
+ n = me.nItems;
+
+ // default to English
+ for(i = 0; i < n; ++i)
+ {
+ if(me.languageParameter(me, i, LANGPARM_ID) == "en")
+ {
+ me.selectedItem = i;
+ break;
+ }
+ }
+
+ // otherwise, find the language
+ for(i = 0; i < n; ++i)
+ {
+ if(me.languageParameter(me, i, LANGPARM_ID) == s)
+ {
+ me.selectedItem = i;
+ break;
+ }
+ }
+
+ // save it off (turning anything unknown into "en")
+ me.saveCvars(me);
+}
+
+void XonoticLanguageList_saveCvars(entity me)
+{
+ cvar_set("_menu_prvm_language", me.languageParameter(me, me.selectedItem, LANGPARM_ID));
+}
+
+void XonoticLanguageList_doubleClickListBoxItem(entity me, float i, vector where)
+{
++ m_play_click_sound(MENU_SOUND_EXECUTE);
+ me.setLanguage(me);
+}
+
+float XonoticLanguageList_keyDown(entity me, float scan, float ascii, float shift)
+{
++ if(scan == K_ENTER || scan == K_KP_ENTER)
++ {
++ m_play_click_sound(MENU_SOUND_EXECUTE);
+ me.setLanguage(me);
+ return 1;
+ }
+ else
+ return SUPER(XonoticLanguageList).keyDown(me, scan, ascii, shift);
+}
+
+void XonoticLanguageList_destroy(entity me)
+{
+ buf_del(me.languagelist);
+}
+
+void XonoticLanguageList_getLanguages(entity me)
+{
+ float buf, i, n, fh;
+ string s;
+
+ buf = buf_create();
+
+ fh = fopen("languages.txt", FILE_READ);
+ i = 0;
+ while((s = fgets(fh)))
+ {
+ n = tokenize_console(s);
+ if(n < 3)
+ continue;
+ bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_ID, argv(0));
+ bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_NAME, argv(1));
+ float k = strstrofs(argv(2), "(", 0);
+ if(k > 0)
+ if(substring(argv(2), strlen(argv(2)) - 1, 1) == ")")
+ {
+ string percent = substring(argv(2), k + 1, -2);
+ if(percent != "100%")
+ bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_PERCENTAGE, percent);
+ }
+ bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_NAME_LOCALIZED, (k < 0) ? argv(2) : substring(argv(2), 0, k - 1));
+ ++i;
+ }
+ fclose(fh);
+
+ me.languagelist = buf;
+ me.nItems = i;
+}
+
+void XonoticLanguageList_setLanguage(entity me)
+{
+ if(prvm_language != cvar_string("_menu_prvm_language"))
+ {
+ if(!(gamestatus & GAME_CONNECTED))
+ localcmd("\nprvm_language \"$_menu_prvm_language\"; menu_restart; menu_cmd languageselect\n");
+ else
+ DialogOpenButton_Click(me, main.languageWarningDialog);
+ }
+}
+
+string XonoticLanguageList_languageParameter(entity me, float i, float key)
+{
+ return bufstr_get(me.languagelist, i * LANGPARM_COUNT + key);
+}
+
+void SetLanguage_Click(entity btn, entity me)
+{
+ me.setLanguage(me);
+}
+
+#endif
--- /dev/null
+#ifdef INTERFACE
+CLASS(XonoticMapList) EXTENDS(XonoticListBox)
+ METHOD(XonoticMapList, configureXonoticMapList, void(entity))
+ ATTRIB(XonoticMapList, rowsPerItem, float, 4)
+ METHOD(XonoticMapList, draw, void(entity))
+ METHOD(XonoticMapList, drawListBoxItem, void(entity, float, vector, float))
+ METHOD(XonoticMapList, clickListBoxItem, void(entity, float, vector))
+ METHOD(XonoticMapList, doubleClickListBoxItem, void(entity, float, vector))
+ METHOD(XonoticMapList, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(XonoticMapList, refilter, void(entity))
+ METHOD(XonoticMapList, refilterCallback, void(entity, entity))
+ METHOD(XonoticMapList, keyDown, float(entity, float, float, float))
+
+ ATTRIB(XonoticMapList, realFontSize, vector, '0 0 0')
+ ATTRIB(XonoticMapList, columnPreviewOrigin, float, 0)
+ ATTRIB(XonoticMapList, columnPreviewSize, float, 0)
+ ATTRIB(XonoticMapList, columnNameOrigin, float, 0)
+ ATTRIB(XonoticMapList, columnNameSize, float, 0)
+ ATTRIB(XonoticMapList, checkMarkOrigin, vector, '0 0 0')
+ ATTRIB(XonoticMapList, checkMarkSize, vector, '0 0 0')
+ ATTRIB(XonoticMapList, realUpperMargin1, float, 0)
+ ATTRIB(XonoticMapList, realUpperMargin2, float, 0)
+
+ ATTRIB(XonoticMapList, lastGametype, float, 0)
+ ATTRIB(XonoticMapList, lastFeatures, float, 0)
+
+ ATTRIB(XonoticMapList, origin, vector, '0 0 0')
+ ATTRIB(XonoticMapList, itemAbsSize, vector, '0 0 0')
+
+ ATTRIB(XonoticMapList, g_maplistCache, string, string_null)
+ METHOD(XonoticMapList, g_maplistCacheToggle, void(entity, float))
+ METHOD(XonoticMapList, g_maplistCacheQuery, float(entity, float))
+
+ ATTRIB(XonoticMapList, startButton, entity, NULL)
+
+ METHOD(XonoticMapList, loadCvars, void(entity))
+
+ ATTRIB(XonoticMapList, typeToSearchString, string, string_null)
+ ATTRIB(XonoticMapList, typeToSearchTime, float, 0)
+
+ METHOD(XonoticMapList, destroy, void(entity))
+
+ ATTRIB(XonoticListBox, alphaBG, float, 0)
+ENDCLASS(XonoticMapList)
+entity makeXonoticMapList();
+void MapList_All(entity btn, entity me);
+void MapList_None(entity btn, entity me);
+void MapList_LoadMap(entity btn, entity me);
+#endif
+
+#ifdef IMPLEMENTATION
+void XonoticMapList_destroy(entity me)
+{
+ MapInfo_Shutdown();
+}
+
+entity makeXonoticMapList()
+{
+ entity me;
+ me = spawnXonoticMapList();
+ me.configureXonoticMapList(me);
+ return me;
+}
+
+void XonoticMapList_configureXonoticMapList(entity me)
+{
+ me.configureXonoticListBox(me);
+ me.refilter(me);
+}
+
+void XonoticMapList_loadCvars(entity me)
+{
+ me.refilter(me);
+}
+
+float XonoticMapList_g_maplistCacheQuery(entity me, float i)
+{
+ return stof(substring(me.g_maplistCache, i, 1));
+}
+void XonoticMapList_g_maplistCacheToggle(entity me, float i)
+{
+ string a, b, c, s, bspname;
+ float n;
+ s = me.g_maplistCache;
+ if (!s)
+ return;
+ b = substring(s, i, 1);
+ if(b == "0")
+ b = "1";
+ else if(b == "1")
+ b = "0";
+ else
+ return; // nothing happens
+ a = substring(s, 0, i);
+ c = substring(s, i+1, strlen(s) - (i+1));
+ strunzone(s);
+ me.g_maplistCache = strzone(strcat(a, b, c));
+ // TODO also update the actual cvar
+ if (!((bspname = MapInfo_BSPName_ByID(i))))
+ return;
+ if(b == "1")
+ cvar_set("g_maplist", strcat(bspname, " ", cvar_string("g_maplist")));
+ else
+ {
+ s = "";
+ n = tokenize_console(cvar_string("g_maplist"));
+ for(i = 0; i < n; ++i)
+ if(argv(i) != bspname)
+ s = strcat(s, " ", argv(i));
+ cvar_set("g_maplist", substring(s, 1, strlen(s) - 1));
+ }
+}
+
+void XonoticMapList_draw(entity me)
+{
+ if(me.startButton)
+ me.startButton.disabled = ((me.selectedItem < 0) || (me.selectedItem >= me.nItems));
+ SUPER(XonoticMapList).draw(me);
+}
+
+void XonoticMapList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ me.itemAbsSize = '0 0 0';
+ SUPER(XonoticMapList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
+
+ me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight));
+ me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth)));
+ me.realUpperMargin1 = 0.5 * (1 - 2.5 * me.realFontSize.y);
+ me.realUpperMargin2 = me.realUpperMargin1 + 1.5 * me.realFontSize.y;
+
+ me.columnPreviewOrigin = 0;
+ me.columnPreviewSize = me.itemAbsSize.y / me.itemAbsSize.x * 4 / 3;
+ me.columnNameOrigin = me.columnPreviewOrigin + me.columnPreviewSize + me.realFontSize.x;
+ me.columnNameSize = 1 - me.columnPreviewSize - 2 * me.realFontSize.x;
+
+ me.checkMarkSize = (eX * (me.itemAbsSize.y / me.itemAbsSize.x) + eY) * 0.5;
+ me.checkMarkOrigin = eY + eX * (me.columnPreviewOrigin + me.columnPreviewSize) - me.checkMarkSize;
+}
+
+void XonoticMapList_clickListBoxItem(entity me, float i, vector where)
+{
+ if(where.x <= me.columnPreviewOrigin + me.columnPreviewSize)
+ if(where.x >= 0)
++ {
++ m_play_click_sound(MENU_SOUND_SELECT);
+ me.g_maplistCacheToggle(me, i);
++ }
+}
+
+void XonoticMapList_doubleClickListBoxItem(entity me, float i, vector where)
+{
+ if(where.x >= me.columnNameOrigin)
+ if(where.x <= 1)
+ {
+ // pop up map info screen
++ m_play_click_sound(MENU_SOUND_OPEN);
+ main.mapInfoDialog.loadMapInfo(main.mapInfoDialog, i, me);
+ DialogOpenButton_Click_withCoords(NULL, main.mapInfoDialog, me.origin + eX * (me.columnNameOrigin * me.size.x) + eY * ((me.itemHeight * i - me.scrollPos) * me.size.y), eY * me.itemAbsSize.y + eX * (me.itemAbsSize.x * me.columnNameSize));
+ }
+}
+
+void XonoticMapList_drawListBoxItem(entity me, float i, vector absSize, float isSelected)
+{
+ // layout: Ping, Map name, Map name, NP, TP, MP
+ string s;
+ float theAlpha;
+ float included;
+
+ if(!MapInfo_Get_ByID(i))
+ return;
+
+ included = me.g_maplistCacheQuery(me, i);
+ if(included || isSelected)
+ theAlpha = SKINALPHA_MAPLIST_INCLUDEDFG;
+ else
+ theAlpha = SKINALPHA_MAPLIST_NOTINCLUDEDFG;
+
+ if(isSelected)
+ draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
+ else if(included)
+ draw_Fill('0 0 0', '1 1 0', SKINCOLOR_MAPLIST_INCLUDEDBG, SKINALPHA_MAPLIST_INCLUDEDBG);
+
+ if(draw_PictureSize(strcat("/maps/", MapInfo_Map_bspname)) == '0 0 0')
+ draw_Picture(me.columnPreviewOrigin * eX, "nopreview_map", me.columnPreviewSize * eX + eY, '1 1 1', theAlpha);
+ else
+ draw_Picture(me.columnPreviewOrigin * eX, strcat("/maps/", MapInfo_Map_bspname), me.columnPreviewSize * eX + eY, '1 1 1', theAlpha);
+
+ if(included)
+ draw_Picture(me.checkMarkOrigin, "checkmark", me.checkMarkSize, '1 1 1', 1);
+ s = draw_TextShortenToWidth(strdecolorize(MapInfo_Map_titlestring), me.columnNameSize, 0, me.realFontSize);
+ draw_Text(me.realUpperMargin1 * eY + (me.columnNameOrigin + 0.00 * (me.columnNameSize - draw_TextWidth(s, 0, me.realFontSize))) * eX, s, me.realFontSize, SKINCOLOR_MAPLIST_TITLE, theAlpha, 0);
+ s = draw_TextShortenToWidth(strdecolorize(MapInfo_Map_author), me.columnNameSize, 0, me.realFontSize);
+ draw_Text(me.realUpperMargin2 * eY + (me.columnNameOrigin + 1.00 * (me.columnNameSize - draw_TextWidth(s, 0, me.realFontSize))) * eX, s, me.realFontSize, SKINCOLOR_MAPLIST_AUTHOR, theAlpha, 0);
+
+ MapInfo_ClearTemps();
+}
+
+void XonoticMapList_refilter(entity me)
+{
+ float i, j, n;
+ string s;
+ float gt, f;
+ gt = MapInfo_CurrentGametype();
+ f = MapInfo_CurrentFeatures();
+ MapInfo_FilterGametype(gt, f, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
+ me.nItems = MapInfo_count;
+ for(i = 0; i < MapInfo_count; ++i)
+ draw_PreloadPicture(strcat("/maps/", MapInfo_BSPName_ByID(i)));
+ if(me.g_maplistCache)
+ strunzone(me.g_maplistCache);
+ s = "0";
+ for(i = 1; i < MapInfo_count; i *= 2)
+ s = strcat(s, s);
+ n = tokenize_console(cvar_string("g_maplist"));
+ for(i = 0; i < n; ++i)
+ {
+ j = MapInfo_FindName(argv(i));
+ if(j >= 0)
+ s = strcat(
+ substring(s, 0, j),
+ "1",
+ substring(s, j+1, MapInfo_count - (j+1))
+ );
+ }
+ me.g_maplistCache = strzone(s);
+ if(gt != me.lastGametype || f != me.lastFeatures)
+ {
+ me.lastGametype = gt;
+ me.lastFeatures = f;
+ me.setSelected(me, 0);
+ }
+}
+
+void XonoticMapList_refilterCallback(entity me, entity cb)
+{
+ me.refilter(me);
+}
+
+void MapList_All(entity btn, entity me)
+{
+ float i;
+ string s;
+ MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, MapInfo_ForbiddenFlags(), 0); // all
+ s = "";
+ for(i = 0; i < MapInfo_count; ++i)
+ s = strcat(s, " ", MapInfo_BSPName_ByID(i));
+ cvar_set("g_maplist", substring(s, 1, strlen(s) - 1));
+ me.refilter(me);
+}
+
+void MapList_None(entity btn, entity me)
+{
+ cvar_set("g_maplist", "");
+ me.refilter(me);
+}
+
+void MapList_LoadMap(entity btn, entity me)
+{
+ string m;
+ float i;
+
+ i = me.selectedItem;
+
+ if(btn.parent.instanceOfXonoticMapInfoDialog)
+ {
+ i = btn.parent.currentMapIndex;
+ Dialog_Close(btn, btn.parent);
+ }
+
+ if(i >= me.nItems || i < 0)
+ return;
+
+ m = MapInfo_BSPName_ByID(i);
+ if (!m)
+ {
+ print(_("Huh? Can't play this (m is NULL). Refiltering so this won't happen again.\n"));
+ me.refilter(me);
+ return;
+ }
+ if(MapInfo_CheckMap(m))
+ {
+ localcmd("\nmenu_loadmap_prepare\n");
+ if(cvar("menu_use_default_hostname"))
+ localcmd("hostname \"", sprintf(_("%s's Xonotic Server"), strdecolorize(cvar_string("_cl_name"))), "\"\n");
+ MapInfo_LoadMap(m, 1);
+ }
+ else
+ {
+ print(_("Huh? Can't play this (invalid game type). Refiltering so this won't happen again.\n"));
+ me.refilter(me);
+ return;
+ }
+}
+
+float XonoticMapList_keyDown(entity me, float scan, float ascii, float shift)
+{
+ string ch, save;
+ if(me.nItems <= 0)
+ return SUPER(XonoticMapList).keyDown(me, scan, ascii, shift);
+ if(scan == K_MOUSE2 || scan == K_SPACE || scan == K_ENTER || scan == K_KP_ENTER)
+ {
+ // pop up map info screen
++ m_play_click_sound(MENU_SOUND_OPEN);
+ main.mapInfoDialog.loadMapInfo(main.mapInfoDialog, me.selectedItem, me);
+ DialogOpenButton_Click_withCoords(NULL, main.mapInfoDialog, me.origin + eX * (me.columnNameOrigin * me.size.x) + eY * ((me.itemHeight * me.selectedItem - me.scrollPos) * me.size.y), eY * me.itemAbsSize.y + eX * (me.itemAbsSize.x * me.columnNameSize));
+ }
+ else if(scan == K_MOUSE3 || scan == K_INS || scan == K_KP_INS)
+ {
++ m_play_click_sound(MENU_SOUND_SELECT);
+ me.g_maplistCacheToggle(me, me.selectedItem);
+ }
+ else if(ascii == 43) // +
+ {
+ if (!me.g_maplistCacheQuery(me, me.selectedItem))
++ {
++ m_play_click_sound(MENU_SOUND_SELECT);
+ me.g_maplistCacheToggle(me, me.selectedItem);
++ }
+ }
+ else if(ascii == 45) // -
+ {
+ if(me.g_maplistCacheQuery(me, me.selectedItem))
++ {
++ m_play_click_sound(MENU_SOUND_SELECT);
+ me.g_maplistCacheToggle(me, me.selectedItem);
++ }
+ }
+ else if(scan == K_BACKSPACE)
+ {
+ if(time < me.typeToSearchTime)
+ {
+ save = substring(me.typeToSearchString, 0, strlen(me.typeToSearchString) - 1);
+ if(me.typeToSearchString)
+ strunzone(me.typeToSearchString);
+ me.typeToSearchString = strzone(save);
+ me.typeToSearchTime = time + 0.5;
+ if(strlen(me.typeToSearchString))
+ {
+ MapInfo_FindName(me.typeToSearchString);
+ if(MapInfo_FindName_firstResult >= 0)
+ me.setSelected(me, MapInfo_FindName_firstResult);
+ }
+ }
+ }
+ else if(ascii >= 32 && ascii != 127)
+ {
+ ch = chr(ascii);
+ if(time > me.typeToSearchTime)
+ save = ch;
+ else
+ save = strcat(me.typeToSearchString, ch);
+ if(me.typeToSearchString)
+ strunzone(me.typeToSearchString);
+ me.typeToSearchString = strzone(save);
+ me.typeToSearchTime = time + 0.5;
+ MapInfo_FindName(me.typeToSearchString);
+ if(MapInfo_FindName_firstResult >= 0)
+ me.setSelected(me, MapInfo_FindName_firstResult);
+ }
+ else
+ return SUPER(XonoticMapList).keyDown(me, scan, ascii, shift);
+ return 1;
+}
+
+#endif
--- /dev/null
+#ifdef INTERFACE
+CLASS(XonoticPlayerList) EXTENDS(XonoticListBox)
+ ATTRIB(XonoticPlayerList, rowsPerItem, float, 1)
+ METHOD(XonoticPlayerList, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(XonoticPlayerList, drawListBoxItem, void(entity, float, vector, float))
++ ATTRIB(XonoticPlayerList, allowFocusSound, float, 0)
+ ATTRIB(XonoticPlayerList, realFontSize, vector, '0 0 0')
+ ATTRIB(XonoticPlayerList, columnNameOrigin, float, 0)
+ ATTRIB(XonoticPlayerList, columnNameSize, float, 0)
+ ATTRIB(XonoticPlayerList, columnScoreOrigin, float, 0)
+ ATTRIB(XonoticPlayerList, columnScoreSize, float, 0)
+ ATTRIB(XonoticPlayerList, realUpperMargin, float, 0)
+ ATTRIB(XonoticPlayerList, origin, vector, '0 0 0')
+ ATTRIB(XonoticPlayerList, itemAbsSize, vector, '0 0 0')
+ METHOD(XonoticPlayerList, setPlayerList, void(entity, string))
+ METHOD(XonoticPlayerList, getPlayerList, string(entity, float, float))
+ ATTRIB(XonoticPlayerList, playerList, float, -1)
+ENDCLASS(XonoticPlayerList)
+entity makeXonoticPlayerList();
+#endif
+
+#ifdef IMPLEMENTATION
+
+const float PLAYERPARM_SCORE = 0;
+const float PLAYERPARM_PING = 1;
+const float PLAYERPARM_TEAM = 2;
+const float PLAYERPARM_NAME = 3;
+const float PLAYERPARM_COUNT = 4;
+
+entity makeXonoticPlayerList()
+{
+ entity me;
+ me = spawnXonoticPlayerList();
+ me.configureXonoticListBox(me);
+ return me;
+}
+
+void XonoticPlayerList_setPlayerList(entity me, string plist)
+{
+ int buf,i,n;
+ string s;
+
+ buf = buf_create();
+ me.nItems = tokenizebyseparator(plist, "\n");
+ for(i = 0; i < me.nItems; ++i)
+ {
+ bufstr_set(buf, i * PLAYERPARM_COUNT + PLAYERPARM_NAME, argv(i)); // -666 100 "^4Nex ^2Player"
+ }
+
+ for(i = 0; i < me.nItems; ++i)
+ {
+ s = bufstr_get(buf, i * PLAYERPARM_COUNT + PLAYERPARM_NAME);
+ n = tokenize_console(s);
+
+ if(n == 4)
+ {
+ bufstr_set(buf, i * PLAYERPARM_COUNT + PLAYERPARM_SCORE, argv(0)); // -666
+ bufstr_set(buf, i * PLAYERPARM_COUNT + PLAYERPARM_PING, argv(1)); // 100
+ bufstr_set(buf, i * PLAYERPARM_COUNT + PLAYERPARM_TEAM, argv(2)); // 0 for spec, else 1, 2, 3, 4
+ bufstr_set(buf, i * PLAYERPARM_COUNT + PLAYERPARM_NAME, argv(3)); // ^4Nex ^2Player
+ }
+ else
+ {
+ bufstr_set(buf, i * PLAYERPARM_COUNT + PLAYERPARM_SCORE, argv(0)); // -666
+ bufstr_set(buf, i * PLAYERPARM_COUNT + PLAYERPARM_PING, argv(1)); // 100
+ bufstr_set(buf, i * PLAYERPARM_COUNT + PLAYERPARM_TEAM, "-1");
+ bufstr_set(buf, i * PLAYERPARM_COUNT + PLAYERPARM_NAME, argv(2)); // ^4Nex ^2Player
+ }
+ }
+ me.playerList = buf;
+}
+
+string XonoticPlayerList_getPlayerList(entity me, float i, float key)
+{
+ return bufstr_get(me.playerList, i * PLAYERPARM_COUNT + key);
+}
+
+void XonoticPlayerList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ me.itemAbsSize = '0 0 0';
+ SUPER(XonoticPlayerList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
+
+ me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight));
+ me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth)));
+ me.realUpperMargin = 0.5 * (1 - me.realFontSize.y);
+
+ // this list does 1 char left and right margin
+ me.columnScoreSize = 5 * me.realFontSize.x;
+ me.columnNameSize = 1 - 3 * me.realFontSize.x - me.columnScoreSize;
+
+ me.columnNameOrigin = me.realFontSize.x;
+ me.columnScoreOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize.x;
+}
+
+void XonoticPlayerList_drawListBoxItem(entity me, float i, vector absSize, float isSelected)
+{
+ string s;
+ string score;
+ float t;
+ vector rgb;
+
+ t = stof(me.getPlayerList(me, i, PLAYERPARM_TEAM));
+ if(t == 1)
+ rgb = colormapPaletteColor(4, 0);
+ else if(t == 2)
+ rgb = colormapPaletteColor(13, 0);
+ else if(t == 3)
+ rgb = colormapPaletteColor(12, 0);
+ else if(t == 4)
+ rgb = colormapPaletteColor(9, 0);
+ else
+ rgb = SKINCOLOR_TEXT;
+
+ s = me.getPlayerList(me, i, PLAYERPARM_NAME);
+ score = me.getPlayerList(me, i, PLAYERPARM_SCORE);
+
+ if(substring(score, strlen(score) - 10, 10) == ":spectator")
+ {
+ score = _("spectator");
+ }
+ else
+ {
+ if((t = strstrofs(score, ":", 0)) >= 0)
+ score = substring(score, 0, t);
+ if((t = strstrofs(score, ",", 0)) >= 0)
+ score = substring(score, 0, t);
+
+ if(stof(score) == -666)
+ score = _("spectator");
+ }
+
+ s = draw_TextShortenToWidth(s, me.columnNameSize, 1, me.realFontSize);
+ draw_Text(me.realUpperMargin2 * eY + (me.columnNameOrigin + 0.00 * (me.columnNameSize - draw_TextWidth(s, 1, me.realFontSize))) * eX, s, me.realFontSize, '1 1 1', 1, 1);
+
+ score = draw_TextShortenToWidth(score, me.columnScoreSize, 0, me.realFontSize);
+ draw_Text(me.realUpperMargin2 * eY + (me.columnScoreOrigin + 1.00 * (me.columnScoreSize - draw_TextWidth(score, 1, me.realFontSize))) * eX, score, me.realFontSize, rgb, 1, 0);
+}
+
+#endif
--- /dev/null
+#ifdef INTERFACE
+CLASS(XonoticServerList) EXTENDS(XonoticListBox)
+ METHOD(XonoticServerList, configureXonoticServerList, void(entity))
+ ATTRIB(XonoticServerList, rowsPerItem, float, 1)
+ METHOD(XonoticServerList, draw, void(entity))
+ METHOD(XonoticServerList, drawListBoxItem, void(entity, float, vector, float))
+ METHOD(XonoticServerList, doubleClickListBoxItem, void(entity, float, vector))
+ METHOD(XonoticServerList, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(XonoticServerList, keyDown, float(entity, float, float, float))
+ METHOD(XonoticServerList, toggleFavorite, void(entity, string))
+
+ ATTRIB(XonoticServerList, iconsSizeFactor, float, 0.85)
+
+ ATTRIB(XonoticServerList, realFontSize, vector, '0 0 0')
+ ATTRIB(XonoticServerList, realUpperMargin, float, 0)
+ ATTRIB(XonoticServerList, columnIconsOrigin, float, 0)
+ ATTRIB(XonoticServerList, columnIconsSize, float, 0)
+ ATTRIB(XonoticServerList, columnPingOrigin, float, 0)
+ ATTRIB(XonoticServerList, columnPingSize, float, 0)
+ ATTRIB(XonoticServerList, columnNameOrigin, float, 0)
+ ATTRIB(XonoticServerList, columnNameSize, float, 0)
+ ATTRIB(XonoticServerList, columnMapOrigin, float, 0)
+ ATTRIB(XonoticServerList, columnMapSize, float, 0)
+ ATTRIB(XonoticServerList, columnTypeOrigin, float, 0)
+ ATTRIB(XonoticServerList, columnTypeSize, float, 0)
+ ATTRIB(XonoticServerList, columnPlayersOrigin, float, 0)
+ ATTRIB(XonoticServerList, columnPlayersSize, float, 0)
+
+ ATTRIB(XonoticServerList, selectedServer, string, string_null) // to restore selected server when needed
+ METHOD(XonoticServerList, setSelected, void(entity, float))
+ METHOD(XonoticServerList, setSortOrder, void(entity, float, float))
+ ATTRIB(XonoticServerList, filterShowEmpty, float, 1)
+ ATTRIB(XonoticServerList, filterShowFull, float, 1)
+ ATTRIB(XonoticServerList, filterString, string, string_null)
+ ATTRIB(XonoticServerList, controlledTextbox, entity, NULL)
+ ATTRIB(XonoticServerList, ipAddressBox, entity, NULL)
+ ATTRIB(XonoticServerList, favoriteButton, entity, NULL)
+ ATTRIB(XonoticServerList, nextRefreshTime, float, 0)
+ METHOD(XonoticServerList, refreshServerList, void(entity, float)) // refresh mode: REFRESHSERVERLIST_*
+ ATTRIB(XonoticServerList, needsRefresh, float, 1)
+ METHOD(XonoticServerList, focusEnter, void(entity))
+ METHOD(XonoticServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)))
+ ATTRIB(XonoticServerList, sortButton1, entity, NULL)
+ ATTRIB(XonoticServerList, sortButton2, entity, NULL)
+ ATTRIB(XonoticServerList, sortButton3, entity, NULL)
+ ATTRIB(XonoticServerList, sortButton4, entity, NULL)
+ ATTRIB(XonoticServerList, sortButton5, entity, NULL)
+ ATTRIB(XonoticServerList, connectButton, entity, NULL)
+ ATTRIB(XonoticServerList, infoButton, entity, NULL)
+ ATTRIB(XonoticServerList, currentSortOrder, float, 0)
+ ATTRIB(XonoticServerList, currentSortField, float, -1)
+
+ ATTRIB(XonoticServerList, ipAddressBoxFocused, float, -1)
+
+ ATTRIB(XonoticServerList, seenIPv4, float, 0)
+ ATTRIB(XonoticServerList, seenIPv6, float, 0)
+ ATTRIB(XonoticServerList, categoriesHeight, float, 1.25)
+
+ METHOD(XonoticServerList, getTotalHeight, float(entity))
+ METHOD(XonoticServerList, getItemAtPos, float(entity, float))
+ METHOD(XonoticServerList, getItemStart, float(entity, float))
+ METHOD(XonoticServerList, getItemHeight, float(entity, float))
+ENDCLASS(XonoticServerList)
+entity makeXonoticServerList();
+
+#ifndef IMPLEMENTATION
+float autocvar_menu_slist_categories;
+float autocvar_menu_slist_categories_onlyifmultiple;
+float autocvar_menu_slist_purethreshold;
+float autocvar_menu_slist_modimpurity;
+float autocvar_menu_slist_recommendations;
+float autocvar_menu_slist_recommendations_maxping;
+float autocvar_menu_slist_recommendations_minfreeslots;
+float autocvar_menu_slist_recommendations_minhumans;
+float autocvar_menu_slist_recommendations_purethreshold;
+
+// server cache fields
+#define SLIST_FIELDS \
+ SLIST_FIELD(CNAME, "cname") \
+ SLIST_FIELD(PING, "ping") \
+ SLIST_FIELD(GAME, "game") \
+ SLIST_FIELD(MOD, "mod") \
+ SLIST_FIELD(MAP, "map") \
+ SLIST_FIELD(NAME, "name") \
+ SLIST_FIELD(MAXPLAYERS, "maxplayers") \
+ SLIST_FIELD(NUMPLAYERS, "numplayers") \
+ SLIST_FIELD(NUMHUMANS, "numhumans") \
+ SLIST_FIELD(NUMBOTS, "numbots") \
+ SLIST_FIELD(PROTOCOL, "protocol") \
+ SLIST_FIELD(FREESLOTS, "freeslots") \
+ SLIST_FIELD(PLAYERS, "players") \
+ SLIST_FIELD(QCSTATUS, "qcstatus") \
+ SLIST_FIELD(CATEGORY, "category") \
+ SLIST_FIELD(ISFAVORITE, "isfavorite")
+
+#define SLIST_FIELD(suffix,name) float SLIST_FIELD_##suffix;
+SLIST_FIELDS
+#undef SLIST_FIELD
+
+const float REFRESHSERVERLIST_RESORT = 0; // sort the server list again to update for changes to e.g. favorite status, categories
+const float REFRESHSERVERLIST_REFILTER = 1; // ..., also update filter and sort criteria
+const float REFRESHSERVERLIST_ASK = 2; // ..., also suggest querying servers now
+const float REFRESHSERVERLIST_RESET = 3; // ..., also clear the list first
+
+// function declarations
+float IsServerInList(string list, string srv);
+#define IsFavorite(srv) IsServerInList(cvar_string("net_slist_favorites"), srv)
+#define IsPromoted(srv) IsServerInList(_Nex_ExtResponseSystem_PromotedServers, srv)
+#define IsRecommended(srv) IsServerInList(_Nex_ExtResponseSystem_RecommendedServers, srv)
+
+entity RetrieveCategoryEnt(float catnum);
+
+float CheckCategoryOverride(float cat);
+float CheckCategoryForEntry(float entry);
+float m_gethostcachecategory(float entry) { return CheckCategoryOverride(CheckCategoryForEntry(entry)); }
+
+void RegisterSLCategories();
+
+void ServerList_Connect_Click(entity btn, entity me);
+void ServerList_Categories_Click(entity box, entity me);
+void ServerList_ShowEmpty_Click(entity box, entity me);
+void ServerList_ShowFull_Click(entity box, entity me);
+void ServerList_Filter_Change(entity box, entity me);
+void ServerList_Favorite_Click(entity btn, entity me);
+void ServerList_Info_Click(entity btn, entity me);
+void ServerList_Update_favoriteButton(entity btn, entity me);
+
+// fields for category entities
+const float MAX_CATEGORIES = 9;
+const float CATEGORY_FIRST = 1;
+entity categories[MAX_CATEGORIES];
+float category_ent_count;
+.string cat_name;
+.string cat_string;
+.string cat_enoverride_string;
+.string cat_dioverride_string;
+.float cat_enoverride;
+.float cat_dioverride;
+
+// fields for drawing categories
+float category_name[MAX_CATEGORIES];
+float category_item[MAX_CATEGORIES];
+float category_draw_count;
+
+#define SLIST_CATEGORIES \
+ SLIST_CATEGORY(CAT_FAVORITED, "", "", ZCTX(_("SLCAT^Favorites"))) \
+ SLIST_CATEGORY(CAT_RECOMMENDED, "", "", ZCTX(_("SLCAT^Recommended"))) \
+ SLIST_CATEGORY(CAT_NORMAL, "", "CAT_SERVERS", ZCTX(_("SLCAT^Normal Servers"))) \
+ SLIST_CATEGORY(CAT_SERVERS, "CAT_NORMAL", "CAT_SERVERS", ZCTX(_("SLCAT^Servers"))) \
+ SLIST_CATEGORY(CAT_XPM, "CAT_NORMAL", "CAT_SERVERS", ZCTX(_("SLCAT^Competitive Mode"))) \
+ SLIST_CATEGORY(CAT_MODIFIED, "", "CAT_SERVERS", ZCTX(_("SLCAT^Modified Servers"))) \
+ SLIST_CATEGORY(CAT_OVERKILL, "", "CAT_SERVERS", ZCTX(_("SLCAT^Overkill Mode"))) \
+ SLIST_CATEGORY(CAT_INSTAGIB, "", "CAT_SERVERS", ZCTX(_("SLCAT^InstaGib Mode"))) \
+ SLIST_CATEGORY(CAT_DEFRAG, "", "CAT_SERVERS", ZCTX(_("SLCAT^Defrag Mode")))
+
+#define SLIST_CATEGORY_AUTOCVAR(name) autocvar_menu_slist_categories_##name##_override
+#define SLIST_CATEGORY(name,enoverride,dioverride,str) \
+ float name; \
+ string SLIST_CATEGORY_AUTOCVAR(name) = enoverride;
+SLIST_CATEGORIES
+#undef SLIST_CATEGORY
+
+#endif
+#endif
+#ifdef IMPLEMENTATION
+
+void RegisterSLCategories()
+{
+ entity cat;
+ #define SLIST_CATEGORY(name,enoverride,dioverride,str) \
+ SET_FIELD_COUNT(name, CATEGORY_FIRST, category_ent_count) \
+ CHECK_MAX_COUNT(name, MAX_CATEGORIES, category_ent_count, "SLIST_CATEGORY") \
+ cat = spawn(); \
+ categories[name - 1] = cat; \
+ cat.classname = "slist_category"; \
+ cat.cat_name = strzone(#name); \
+ cat.cat_enoverride_string = strzone(SLIST_CATEGORY_AUTOCVAR(name)); \
+ cat.cat_dioverride_string = strzone(dioverride); \
+ cat.cat_string = strzone(str);
+ SLIST_CATEGORIES
+ #undef SLIST_CATEGORY
+
+ float i, x, catnum;
+ string s;
+
+ #define PROCESS_OVERRIDE(override_string,override_field) \
+ for(i = 0; i < category_ent_count; ++i) \
+ { \
+ s = categories[i].override_string; \
+ if((s != "") && (s != categories[i].cat_name)) \
+ { \
+ catnum = 0; \
+ for(x = 0; x < category_ent_count; ++x) \
+ { if(categories[x].cat_name == s) { \
+ catnum = (x+1); \
+ break; \
+ } } \
+ if(catnum) \
+ { \
+ strunzone(categories[i].override_string); \
+ categories[i].override_field = catnum; \
+ continue; \
+ } \
+ else \
+ { \
+ printf( \
+ "RegisterSLCategories(): Improper override '%s' for category '%s'!\n", \
+ s, \
+ categories[i].cat_name \
+ ); \
+ } \
+ } \
+ strunzone(categories[i].override_string); \
+ categories[i].override_field = 0; \
+ }
+ PROCESS_OVERRIDE(cat_enoverride_string, cat_enoverride)
+ PROCESS_OVERRIDE(cat_dioverride_string, cat_dioverride)
+ #undef PROCESS_OVERRIDE
+}
+
+// Supporting Functions
+entity RetrieveCategoryEnt(float catnum)
+{
+ if((catnum > 0) && (catnum <= category_ent_count))
+ {
+ return categories[catnum - 1];
+ }
+ else
+ {
+ error(sprintf("RetrieveCategoryEnt(%d): Improper category number!\n", catnum));
+ return world;
+ }
+}
+
+float IsServerInList(string list, string srv)
+{
+ string p;
+ float i, n;
+ if(srv == "")
+ return false;
+ srv = netaddress_resolve(srv, 26000);
+ if(srv == "")
+ return false;
+ p = crypto_getidfp(srv);
+ n = tokenize_console(list);
+ for(i = 0; i < n; ++i)
+ {
+ if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
+ {
+ if(p)
+ if(argv(i) == p)
+ return true;
+ }
+ else
+ {
+ if(srv == netaddress_resolve(argv(i), 26000))
+ return true;
+ }
+ }
+ return false;
+}
+
+float CheckCategoryOverride(float cat)
+{
+ entity catent = RetrieveCategoryEnt(cat);
+ if(catent)
+ {
+ float override = (autocvar_menu_slist_categories ? catent.cat_enoverride : catent.cat_dioverride);
+ if(override) { return override; }
+ else { return cat; }
+ }
+ else
+ {
+ error(sprintf("CheckCategoryOverride(%d): Improper category number!\n", cat));
+ return cat;
+ }
+}
+
+float CheckCategoryForEntry(float entry)
+{
+ string s, k, v, modtype = "";
+ float j, m, impure = 0, freeslots = 0, sflags = 0;
+ s = gethostcachestring(SLIST_FIELD_QCSTATUS, entry);
+ m = tokenizebyseparator(s, ":");
+
+ for(j = 2; j < m; ++j)
+ {
+ if(argv(j) == "") { break; }
+ k = substring(argv(j), 0, 1);
+ v = substring(argv(j), 1, -1);
+ switch(k)
+ {
+ case "P": { impure = stof(v); break; }
+ case "S": { freeslots = stof(v); break; }
+ case "F": { sflags = stof(v); break; }
+ case "M": { modtype = strtolower(v); break; }
+ }
+ }
+
+ if(modtype != "xonotic") { impure += autocvar_menu_slist_modimpurity; }
+
+ // check if this server is favorited
+ if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, entry)) { return CAT_FAVORITED; }
+
+ // now check if it's recommended
+ if(autocvar_menu_slist_recommendations)
+ {
+ string cname = gethostcachestring(SLIST_FIELD_CNAME, entry);
+
+ if(IsPromoted(cname)) { return CAT_RECOMMENDED; }
+ else
+ {
+ float recommended = 0;
+ if(autocvar_menu_slist_recommendations & 1)
+ {
+ if(IsRecommended(cname)) { ++recommended; }
+ else { --recommended; }
+ }
+ if(autocvar_menu_slist_recommendations & 2)
+ {
+ if(
+ ///// check for minimum free slots
+ (freeslots >= autocvar_menu_slist_recommendations_minfreeslots)
+
+ && // check for purity requirement
+ (
+ (autocvar_menu_slist_recommendations_purethreshold < 0)
+ ||
+ (impure <= autocvar_menu_slist_recommendations_purethreshold)
+ )
+
+ && // check for minimum amount of humans
+ (
+ gethostcachenumber(SLIST_FIELD_NUMHUMANS, entry)
+ >=
+ autocvar_menu_slist_recommendations_minhumans
+ )
+
+ && // check for maximum latency
+ (
+ gethostcachenumber(SLIST_FIELD_PING, entry)
+ <=
+ autocvar_menu_slist_recommendations_maxping
+ )
+ )
+ { ++recommended; }
+ else
+ { --recommended; }
+ }
+ if(recommended > 0) { return CAT_RECOMMENDED; }
+ }
+ }
+
+ // if not favorited or recommended, check modname
+ if(modtype != "xonotic")
+ {
+ switch(modtype)
+ {
+ // old servers which don't report their mod name are considered modified now
+ case "": { return CAT_MODIFIED; }
+
+ case "xpm": { return CAT_XPM; }
+ case "minstagib":
+ case "instagib": { return CAT_INSTAGIB; }
+ case "overkill": { return CAT_OVERKILL; }
+ //case "nix": { return CAT_NIX; }
+ //case "newtoys": { return CAT_NEWTOYS; }
+
+ // "cts" is allowed as compat, xdf is replacement
+ case "cts":
+ case "xdf": { return CAT_DEFRAG; }
+
+ default: { dprintf("Found strange mod type: %s\n", modtype); return CAT_MODIFIED; }
+ }
+ }
+
+ // must be normal or impure server
+ return ((impure > autocvar_menu_slist_purethreshold) ? CAT_MODIFIED : CAT_NORMAL);
+}
+
+void XonoticServerList_toggleFavorite(entity me, string srv)
+{
+ string s, s0, s1, s2, srv_resolved, p;
+ float i, n, f;
+ srv_resolved = netaddress_resolve(srv, 26000);
+ p = crypto_getidfp(srv_resolved);
+ s = cvar_string("net_slist_favorites");
+ n = tokenize_console(s);
+ f = 0;
+ for(i = 0; i < n; ++i)
+ {
+ if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
+ {
+ if(p)
+ if(argv(i) != p)
+ continue;
+ }
+ else
+ {
+ if(srv_resolved != netaddress_resolve(argv(i), 26000))
+ continue;
+ }
+ s0 = s1 = s2 = "";
+ if(i > 0)
+ s0 = substring(s, 0, argv_end_index(i - 1));
+ if(i < n-1)
+ s2 = substring(s, argv_start_index(i + 1), -1);
+ if(s0 != "" && s2 != "")
+ s1 = " ";
+ cvar_set("net_slist_favorites", strcat(s0, s1, s2));
+ s = cvar_string("net_slist_favorites");
+ n = tokenize_console(s);
+ f = 1;
+ --i;
+ }
+
+ if(!f)
+ {
+ s1 = "";
+ if(s != "")
+ s1 = " ";
+ if(p)
+ cvar_set("net_slist_favorites", strcat(s, s1, p));
+ else
+ cvar_set("net_slist_favorites", strcat(s, s1, srv));
+ }
+
+ me.refreshServerList(me, REFRESHSERVERLIST_RESORT);
+}
+
+void ServerList_Update_favoriteButton(entity btn, entity me)
+{
+ me.favoriteButton.setText(me.favoriteButton,
+ (IsFavorite(me.ipAddressBox.text) ?
+ _("Remove") : _("Favorite")
+ )
+ );
+}
+
+entity makeXonoticServerList()
+{
+ entity me;
+ me = spawnXonoticServerList();
+ me.configureXonoticServerList(me);
+ return me;
+}
+void XonoticServerList_configureXonoticServerList(entity me)
+{
+ me.configureXonoticListBox(me);
+
+ // update field ID's
+ #define SLIST_FIELD(suffix,name) SLIST_FIELD_##suffix = gethostcacheindexforkey(name);
+ SLIST_FIELDS
+ #undef SLIST_FIELD
+
+ // clear list
+ me.nItems = 0;
+}
+void XonoticServerList_setSelected(entity me, float i)
+{
+ float save;
+ save = me.selectedItem;
+ SUPER(XonoticServerList).setSelected(me, i);
+ /*
+ if(me.selectedItem == save)
+ return;
+ */
+ if(me.nItems == 0)
+ return;
+ if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems)
+ return; // sorry, it would be wrong
+
+ if(me.selectedServer)
+ strunzone(me.selectedServer);
+ me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
+
+ me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
+ me.ipAddressBox.cursorPos = strlen(me.selectedServer);
+ me.ipAddressBoxFocused = -1;
+}
+void XonoticServerList_refreshServerList(entity me, float mode)
+{
+ //print("refresh of type ", ftos(mode), "\n");
+
+ if(mode >= REFRESHSERVERLIST_REFILTER)
+ {
+ float m, i, n;
+ float listflags = 0;
+ string s, typestr, modstr;
+
+ s = me.filterString;
+
+ m = strstrofs(s, ":", 0);
+ if(m >= 0)
+ {
+ typestr = substring(s, 0, m);
+ s = substring(s, m + 1, strlen(s) - m - 1);
+ while(substring(s, 0, 1) == " ")
+ s = substring(s, 1, strlen(s) - 1);
+ }
+ else
+ typestr = "";
+
+ modstr = cvar_string("menu_slist_modfilter");
+
+ m = SLIST_MASK_AND - 1;
+ resethostcachemasks();
+
+ // ping: reject negative ping (no idea why this happens in the first place, engine bug)
+ sethostcachemasknumber(++m, SLIST_FIELD_PING, 0, SLIST_TEST_GREATEREQUAL);
+
+ // show full button
+ if(!me.filterShowFull)
+ {
+ sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL); // legacy
+ sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, ":S0:", SLIST_TEST_NOTCONTAIN); // g_maxplayers support
+ }
+
+ // show empty button
+ if(!me.filterShowEmpty)
+ sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
+
+ // gametype filtering
+ if(typestr != "")
+ sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
+
+ // mod filtering
+ if(modstr != "")
+ {
+ if(substring(modstr, 0, 1) == "!")
+ sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(substring(modstr, 1, strlen(modstr) - 1)), SLIST_TEST_NOTEQUAL);
+ else
+ sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(modstr), SLIST_TEST_EQUAL);
+ }
+
+ // server banning
+ n = tokenizebyseparator(_Nex_ExtResponseSystem_BannedServers, " ");
+ for(i = 0; i < n; ++i)
+ if(argv(i) != "")
+ sethostcachemaskstring(++m, SLIST_FIELD_CNAME, argv(i), SLIST_TEST_NOTSTARTSWITH);
+
+ m = SLIST_MASK_OR - 1;
+ if(s != "")
+ {
+ sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
+ sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
+ sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
+ sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
+ }
+
+ // sorting flags
+ //listflags |= SLSF_FAVORITES;
+ listflags |= SLSF_CATEGORIES;
+ if(me.currentSortOrder < 0) { listflags |= SLSF_DESCENDING; }
+ sethostcachesort(me.currentSortField, listflags);
+ }
+
+ resorthostcache();
+ if(mode >= REFRESHSERVERLIST_ASK)
+ refreshhostcache(mode >= REFRESHSERVERLIST_RESET);
+}
+void XonoticServerList_focusEnter(entity me)
+{
++ SUPER(XonoticServerList).focusEnter(me);
+ if(time < me.nextRefreshTime)
+ {
+ //print("sorry, no refresh yet\n");
+ return;
+ }
+ me.nextRefreshTime = time + 10;
+ me.refreshServerList(me, REFRESHSERVERLIST_ASK);
+}
+
+void XonoticServerList_draw(entity me)
+{
+ float i, found, owned;
+
+ if(_Nex_ExtResponseSystem_BannedServersNeedsRefresh)
+ {
+ if(!me.needsRefresh)
+ me.needsRefresh = 2;
+ _Nex_ExtResponseSystem_BannedServersNeedsRefresh = 0;
+ }
+
+ if(_Nex_ExtResponseSystem_PromotedServersNeedsRefresh)
+ {
+ if(!me.needsRefresh)
+ me.needsRefresh = 3;
+ _Nex_ExtResponseSystem_PromotedServersNeedsRefresh = 0;
+ }
+
+ if(_Nex_ExtResponseSystem_RecommendedServersNeedsRefresh)
+ {
+ if(!me.needsRefresh)
+ me.needsRefresh = 3;
+ _Nex_ExtResponseSystem_RecommendedServersNeedsRefresh = 0;
+ }
+
+ if(me.currentSortField == -1)
+ {
+ me.setSortOrder(me, SLIST_FIELD_PING, +1);
+ me.refreshServerList(me, REFRESHSERVERLIST_RESET);
+ }
+ else if(me.needsRefresh == 1)
+ {
+ me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
+ }
+ else if(me.needsRefresh == 2)
+ {
+ me.needsRefresh = 0;
+ me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
+ }
+ else if(me.needsRefresh == 3)
+ {
+ me.needsRefresh = 0;
+ me.refreshServerList(me, REFRESHSERVERLIST_RESORT);
+ }
+
+ owned = ((me.selectedServer == me.ipAddressBox.text) && (me.ipAddressBox.text != ""));
+
+ for(i = 0; i < category_draw_count; ++i) { category_name[i] = -1; category_item[i] = -1; }
+ category_draw_count = 0;
+
+ if(autocvar_menu_slist_categories >= 0) // if less than 0, don't even draw a category heading for favorites
+ {
+ float itemcount = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
+ me.nItems = itemcount;
+
+ //float visible = floor(me.scrollPos / me.itemHeight);
+ // ^ unfortunately no such optimization can be made-- we must process through the
+ // entire list, otherwise there is no way to know which item is first in its category.
+
+ // binary search method suggested by div
+ float x;
+ float begin = 0;
+ for(x = 1; x <= category_ent_count; ++x) {
+ float first = begin;
+ float last = (itemcount - 1);
+ if (first > last) {
+ // List is empty.
+ break;
+ }
+ float catf = gethostcachenumber(SLIST_FIELD_CATEGORY, first);
+ float catl = gethostcachenumber(SLIST_FIELD_CATEGORY, last);
+ if (catf > x) {
+ // The first one is already > x.
+ // Therefore, category x does not exist.
+ // Higher numbered categories do exist though.
+ } else if (catl < x) {
+ // The last one is < x.
+ // Thus this category - and any following -
+ // don't exist.
+ break;
+ } else if (catf == x) {
+ // Starts at first. This breaks the loop
+ // invariant in the binary search and thus has
+ // to be handled separately.
+ if(gethostcachenumber(SLIST_FIELD_CATEGORY, first) != x)
+ error("Category mismatch I");
+ if(first > 0)
+ if(gethostcachenumber(SLIST_FIELD_CATEGORY, first - 1) == x)
+ error("Category mismatch II");
+ category_name[category_draw_count] = x;
+ category_item[category_draw_count] = first;
+ ++category_draw_count;
+ begin = first + 1;
+ } else {
+ // At this point, catf <= x < catl, thus
+ // catf < catl, thus first < last.
+ // INVARIANTS:
+ // last - first >= 1
+ // catf == gethostcachenumber(SLIST_FIELD_CATEGORY(first)
+ // catl == gethostcachenumber(SLIST_FIELD_CATEGORY(last)
+ // catf < x
+ // catl >= x
+ while (last - first > 1) {
+ float middle = floor((first + last) / 2);
+ // By loop condition, middle != first && middle != last.
+ float cat = gethostcachenumber(SLIST_FIELD_CATEGORY, middle);
+ if (cat >= x) {
+ last = middle;
+ catl = cat;
+ } else {
+ first = middle;
+ catf = cat;
+ }
+ }
+ if (catl == x) {
+ if(gethostcachenumber(SLIST_FIELD_CATEGORY, last) != x)
+ error("Category mismatch III");
+ if(last > 0)
+ if(gethostcachenumber(SLIST_FIELD_CATEGORY, last - 1) == x)
+ error("Category mismatch IV");
+ category_name[category_draw_count] = x;
+ category_item[category_draw_count] = last;
+ ++category_draw_count;
+ begin = last + 1; // already scanned through these, skip 'em
+ }
+ else
+ begin = last; // already scanned through these, skip 'em
+ }
+ }
+ if(autocvar_menu_slist_categories_onlyifmultiple && (category_draw_count == 1))
+ {
+ category_name[0] = -1;
+ category_item[0] = -1;
+ category_draw_count = 0;
+ me.nItems = itemcount;
+ }
+ }
+ else { me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT); }
+
+ me.connectButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
+ me.infoButton.disabled = ((me.nItems == 0) || !owned);
+ me.favoriteButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
+
+ found = 0;
+ if(me.selectedServer)
+ {
+ for(i = 0; i < me.nItems; ++i)
+ {
+ if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
+ {
+ me.selectedItem = i;
+ found = 1;
+ break;
+ }
+ }
+ }
+ if(!found)
+ {
+ if(me.nItems > 0)
+ {
+ if(me.selectedItem >= me.nItems)
+ me.selectedItem = me.nItems - 1;
+ if(me.selectedServer)
+ strunzone(me.selectedServer);
+ me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
+ }
+ }
+
+ if(owned)
+ {
+ if(me.selectedServer != me.ipAddressBox.text)
+ {
+ me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
+ me.ipAddressBox.cursorPos = strlen(me.selectedServer);
+ me.ipAddressBoxFocused = -1;
+ }
+ }
+
+ if(me.ipAddressBoxFocused != me.ipAddressBox.focused)
+ {
+ if(me.ipAddressBox.focused || me.ipAddressBoxFocused < 0)
+ ServerList_Update_favoriteButton(NULL, me);
+ me.ipAddressBoxFocused = me.ipAddressBox.focused;
+ }
+
+ SUPER(XonoticServerList).draw(me);
+}
+void ServerList_PingSort_Click(entity btn, entity me)
+{
+ me.setSortOrder(me, SLIST_FIELD_PING, +1);
+}
+void ServerList_NameSort_Click(entity btn, entity me)
+{
+ me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
+}
+void ServerList_MapSort_Click(entity btn, entity me)
+{
+ me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
+}
+void ServerList_PlayerSort_Click(entity btn, entity me)
+{
+ me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
+}
+void ServerList_TypeSort_Click(entity btn, entity me)
+{
+ string s, t;
+ float i, m;
+ s = me.filterString;
+ m = strstrofs(s, ":", 0);
+ if(m >= 0)
+ {
+ s = substring(s, 0, m);
+ while(substring(s, m+1, 1) == " ") // skip spaces
+ ++m;
+ }
+ else
+ s = "";
+
+ for(i = 1; ; i *= 2) // 20 modes ought to be enough for anyone
+ {
+ t = MapInfo_Type_ToString(i);
+ if(i > 1)
+ if(t == "") // it repeats (default case)
+ {
+ // no type was found
+ // choose the first one
+ s = MapInfo_Type_ToString(1);
+ break;
+ }
+ if(s == t)
+ {
+ // the type was found
+ // choose the next one
+ s = MapInfo_Type_ToString(i * 2);
+ if(s == "")
+ s = MapInfo_Type_ToString(1);
+ break;
+ }
+ }
+
+ if(s != "")
+ s = strcat(s, ":");
+ s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
+
+ me.controlledTextbox.setText(me.controlledTextbox, s);
+ me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
+ me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
+ //ServerList_Filter_Change(me.controlledTextbox, me);
+}
+void ServerList_Filter_Change(entity box, entity me)
+{
+ if(me.filterString)
+ strunzone(me.filterString);
+ if(box.text != "")
+ me.filterString = strzone(box.text);
+ else
+ me.filterString = string_null;
+ me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
+
+ me.ipAddressBox.setText(me.ipAddressBox, "");
+ me.ipAddressBox.cursorPos = 0;
+ me.ipAddressBoxFocused = -1;
+}
+void ServerList_Categories_Click(entity box, entity me)
+{
+ box.setChecked(box, autocvar_menu_slist_categories = !autocvar_menu_slist_categories);
+ me.refreshServerList(me, REFRESHSERVERLIST_RESORT);
+
+ me.ipAddressBox.setText(me.ipAddressBox, "");
+ me.ipAddressBox.cursorPos = 0;
+ me.ipAddressBoxFocused = -1;
+}
+void ServerList_ShowEmpty_Click(entity box, entity me)
+{
+ box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
+ me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
+
+ me.ipAddressBox.setText(me.ipAddressBox, "");
+ me.ipAddressBox.cursorPos = 0;
+ me.ipAddressBoxFocused = -1;
+}
+void ServerList_ShowFull_Click(entity box, entity me)
+{
+ box.setChecked(box, me.filterShowFull = !me.filterShowFull);
+ me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
+
+ me.ipAddressBox.setText(me.ipAddressBox, "");
+ me.ipAddressBox.cursorPos = 0;
+ me.ipAddressBoxFocused = -1;
+}
+void XonoticServerList_setSortOrder(entity me, float fld, float direction)
+{
+ if(me.currentSortField == fld)
+ direction = -me.currentSortOrder;
+ me.currentSortOrder = direction;
+ me.currentSortField = fld;
+ me.sortButton1.forcePressed = (fld == SLIST_FIELD_PING);
+ me.sortButton2.forcePressed = (fld == SLIST_FIELD_NAME);
+ me.sortButton3.forcePressed = (fld == SLIST_FIELD_MAP);
+ me.sortButton4.forcePressed = 0;
+ me.sortButton5.forcePressed = (fld == SLIST_FIELD_NUMHUMANS);
+ me.selectedItem = 0;
+ if(me.selectedServer)
+ strunzone(me.selectedServer);
+ me.selectedServer = string_null;
+ me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
+}
+void XonoticServerList_positionSortButton(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
+{
+ vector originInLBSpace, sizeInLBSpace;
+ originInLBSpace = eY * (-me.itemHeight);
+ sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
+
+ vector originInDialogSpace, sizeInDialogSpace;
+ originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
+ sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
+
+ btn.Container_origin_x = originInDialogSpace.x + sizeInDialogSpace.x * theOrigin;
+ btn.Container_size_x = sizeInDialogSpace.x * theSize;
+ btn.setText(btn, theTitle);
+ btn.onClick = theFunc;
+ btn.onClickEntity = me;
+ btn.resized = 1;
+}
+void XonoticServerList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ SUPER(XonoticServerList).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);
+
+ me.columnIconsOrigin = 0;
+ me.columnIconsSize = me.realFontSize.x * 4 * me.iconsSizeFactor;
+ me.columnPingSize = me.realFontSize.x * 3;
+ me.columnMapSize = me.realFontSize.x * 10;
+ me.columnTypeSize = me.realFontSize.x * 4;
+ me.columnPlayersSize = me.realFontSize.x * 5;
+ me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnIconsSize - me.columnTypeSize - 5 * me.realFontSize.x;
+ me.columnPingOrigin = me.columnIconsOrigin + me.columnIconsSize + me.realFontSize.x;
+ me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize.x;
+ me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize.x;
+ me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize.x;
+ me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize.x;
+
+ me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, _("Ping"), ServerList_PingSort_Click);
+ me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, _("Host name"), ServerList_NameSort_Click);
+ me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, _("Map"), ServerList_MapSort_Click);
+ me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, _("Type"), ServerList_TypeSort_Click);
+ me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, _("Players"), ServerList_PlayerSort_Click);
+
+ float f;
+ f = me.currentSortField;
+ if(f >= 0)
+ {
+ me.currentSortField = -1;
+ me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
+ }
+}
+void ServerList_Connect_Click(entity btn, entity me)
+{
+ localcmd(sprintf("connect %s\n",
+ ((me.ipAddressBox.text != "") ?
+ me.ipAddressBox.text : me.selectedServer
+ )
+ ));
+}
+void ServerList_Favorite_Click(entity btn, entity me)
+{
+ string ipstr;
+ ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
+ if(ipstr != "")
+ {
++ m_play_click_sound(MENU_SOUND_SELECT);
+ me.toggleFavorite(me, me.ipAddressBox.text);
+ me.ipAddressBoxFocused = -1;
+ }
+}
+void ServerList_Info_Click(entity btn, entity me)
+{
+ if (me.nItems != 0)
+ main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
+
+ vector org = boxToGlobal(eY * (me.selectedItem * me.itemHeight - me.scrollPos), me.origin, me.size);
+ vector sz = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), me.size);
+ DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
+}
+void XonoticServerList_doubleClickListBoxItem(entity me, float i, vector where)
+{
+ ServerList_Connect_Click(NULL, me);
+}
+void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float isSelected)
+{
+ // layout: Ping, Server name, Map name, NP, TP, MP
+ float p, q;
+ float isv4, isv6;
+ vector theColor;
+ float theAlpha;
+ float m, pure, freeslots, j, sflags;
+ string s, typestr, versionstr, k, v, modname;
+
+ //printf("time: %f, i: %d, item: %d, nitems: %d\n", time, i, item, me.nItems);
+
+ vector oldscale = draw_scale;
+ vector oldshift = draw_shift;
+#define SET_YRANGE(start,end) \
+ draw_scale = boxToGlobalSize(eX * 1 + eY * (end - start), oldscale); \
+ draw_shift = boxToGlobal(eY * start, oldshift, oldscale);
+
+ for (j = 0; j < category_draw_count; ++j) {
+ // Matches exactly the headings with increased height.
+ if (i == category_item[j])
+ break;
+ }
+
+ if (j < category_draw_count)
+ {
+ entity catent = RetrieveCategoryEnt(category_name[j]);
+ if(catent)
+ {
+ SET_YRANGE(
+ (me.categoriesHeight - 1) / (me.categoriesHeight + 1),
+ me.categoriesHeight / (me.categoriesHeight + 1)
+ );
+ draw_Text(
+ eY * me.realUpperMargin
+ +
+#if 0
+ eX * (me.columnNameOrigin + (me.columnNameSize - draw_TextWidth(catent.cat_string, 0, me.realFontSize)) * 0.5),
+ catent.cat_string,
+#else
+ eX * (me.columnNameOrigin),
+ strcat(catent.cat_string, ":"),
+#endif
+ me.realFontSize,
+ SKINCOLOR_SERVERLIST_CATEGORY,
+ SKINALPHA_SERVERLIST_CATEGORY,
+ 0
+ );
+ SET_YRANGE(me.categoriesHeight / (me.categoriesHeight + 1), 1);
+ }
+ }
+
+ if(isSelected)
+ draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
+
+ s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
+ m = tokenizebyseparator(s, ":");
+ typestr = "";
+ if(m >= 2)
+ {
+ typestr = argv(0);
+ versionstr = argv(1);
+ }
+ freeslots = -1;
+ sflags = -1;
+ modname = "";
+ pure = 0;
+ for(j = 2; j < m; ++j)
+ {
+ if(argv(j) == "")
+ break;
+ k = substring(argv(j), 0, 1);
+ v = substring(argv(j), 1, -1);
+ if(k == "P")
+ pure = stof(v);
+ else if(k == "S")
+ freeslots = stof(v);
+ else if(k == "F")
+ sflags = stof(v);
+ else if(k == "M")
+ modname = v;
+ }
+
+#ifdef COMPAT_NO_MOD_IS_XONOTIC
+ if(modname == "")
+ modname = "Xonotic";
+#endif
+
+ /*
+ SLIST_FIELD_MOD = gethostcacheindexforkey("mod");
+ s = gethostcachestring(SLIST_FIELD_MOD, i);
+ if(s != "data")
+ if(modname == "Xonotic")
+ modname = s;
+ */
+
+ // list the mods here on which the pure server check actually works
+ if(modname != "Xonotic")
+ if(modname != "InstaGib" || modname != "MinstaGib")
+ if(modname != "CTS")
+ if(modname != "NIX")
+ if(modname != "NewToys")
+ pure = 0;
+
+ if(gethostcachenumber(SLIST_FIELD_FREESLOTS, i) <= 0)
+ theAlpha = SKINALPHA_SERVERLIST_FULL;
+ else if(freeslots == 0)
+ theAlpha = SKINALPHA_SERVERLIST_FULL; // g_maxplayers support
+ else if (!gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
+ theAlpha = SKINALPHA_SERVERLIST_EMPTY;
+ else
+ theAlpha = 1;
+
+ p = gethostcachenumber(SLIST_FIELD_PING, i);
+ const float PING_LOW = 75;
+ const float PING_MED = 200;
+ const float PING_HIGH = 500;
+ if(p < PING_LOW)
+ theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
+ else if(p < PING_MED)
+ theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
+ else if(p < PING_HIGH)
+ {
+ theColor = SKINCOLOR_SERVERLIST_HIGHPING;
+ theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
+ }
+ else
+ {
+ theColor = eX;
+ theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
+ }
+
+ if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
+ {
+ theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
+ theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
+ }
+
+ s = gethostcachestring(SLIST_FIELD_CNAME, i);
+
+ isv4 = isv6 = 0;
+ if(substring(s, 0, 1) == "[")
+ {
+ isv6 = 1;
+ me.seenIPv6 += 1;
+ }
+ else if(strstrofs("0123456789", substring(s, 0, 1), 0) >= 0)
+ {
+ isv4 = 1;
+ me.seenIPv4 += 1;
+ }
+
+ q = stof(substring(crypto_getencryptlevel(s), 0, 1));
+ if((q <= 0 && cvar("crypto_aeslevel") >= 3) || (q >= 3 && cvar("crypto_aeslevel") <= 0))
+ {
+ theColor = SKINCOLOR_SERVERLIST_IMPOSSIBLE;
+ theAlpha = SKINALPHA_SERVERLIST_IMPOSSIBLE;
+ }
+
+ if(q == 1)
+ {
+ if(cvar("crypto_aeslevel") >= 2)
+ q |= 4;
+ }
+ if(q == 2)
+ {
+ if(cvar("crypto_aeslevel") >= 1)
+ q |= 4;
+ }
+ if(q == 3)
+ q = 5;
+ else if(q >= 3)
+ q -= 2;
+ // possible status:
+ // 0: crypto off
+ // 1: AES possible
+ // 2: AES recommended but not available
+ // 3: AES possible and will be used
+ // 4: AES recommended and will be used
+ // 5: AES required
+
+ // --------------
+ // RENDER ICONS
+ // --------------
+ vector iconSize = '0 0 0';
+ iconSize_y = me.realFontSize.y * me.iconsSizeFactor;
+ iconSize_x = me.realFontSize.x * me.iconsSizeFactor;
+
+ vector iconPos = '0 0 0';
+ iconPos_x = (me.columnIconsSize - 3 * iconSize.x) * 0.5;
+ iconPos_y = (1 - iconSize.y) * 0.5;
+
+ string n;
+
+ if (!(me.seenIPv4 && me.seenIPv6))
+ {
+ iconPos.x += iconSize.x * 0.5;
+ }
+ else if(me.seenIPv4 && me.seenIPv6)
+ {
+ n = string_null;
+ if(isv6)
+ draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_ipv6"), 0); // PRECACHE_PIC_MIPMAP
+ else if(isv4)
+ draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_ipv4"), 0); // PRECACHE_PIC_MIPMAP
+ if(n)
+ draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
+ iconPos.x += iconSize.x;
+ }
+
+ if(q > 0)
+ {
+ draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_aeslevel", ftos(q)), 0); // PRECACHE_PIC_MIPMAP
+ draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
+ }
+ iconPos.x += iconSize.x;
+
+ if(modname == "Xonotic")
+ {
+ if(pure == 0)
+ {
+ draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_pure1"), PRECACHE_PIC_MIPMAP);
+ draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
+ }
+ }
+ else
+ {
+ draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_mod_", modname), PRECACHE_PIC_MIPMAP);
+ if(draw_PictureSize(n) == '0 0 0')
+ draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_mod_"), PRECACHE_PIC_MIPMAP);
+ if(pure == 0)
+ draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
+ else
+ draw_Picture(iconPos, n, iconSize, '1 1 1', SKINALPHA_SERVERLIST_ICON_NONPURE);
+ }
+ iconPos.x += iconSize.x;
+
+ if(sflags >= 0 && (sflags & SERVERFLAG_PLAYERSTATS))
+ {
+ draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_stats1"), 0); // PRECACHE_PIC_MIPMAP
+ draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
+ }
+ iconPos.x += iconSize.x;
+
+ // --------------
+ // RENDER TEXT
+ // --------------
+
+ // ping
+ s = ftos(p);
+ draw_Text(me.realUpperMargin * eY + (me.columnPingOrigin + me.columnPingSize - draw_TextWidth(s, 0, me.realFontSize)) * eX, s, me.realFontSize, theColor, theAlpha, 0);
+
+ // server name
+ s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize, 0, me.realFontSize);
+ draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
+
+ // server map
+ s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize, 0, me.realFontSize);
+ draw_Text(me.realUpperMargin * eY + (me.columnMapOrigin + (me.columnMapSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
+
+ // server gametype
+ s = draw_TextShortenToWidth(typestr, me.columnTypeSize, 0, me.realFontSize);
+ draw_Text(me.realUpperMargin * eY + (me.columnTypeOrigin + (me.columnTypeSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
+
+ // server playercount
+ s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
+ draw_Text(me.realUpperMargin * eY + (me.columnPlayersOrigin + (me.columnPlayersSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
+}
+
+float XonoticServerList_keyDown(entity me, float scan, float ascii, float shift)
+{
+ vector org, sz;
+
+ org = boxToGlobal(eY * (me.selectedItem * me.itemHeight - me.scrollPos), me.origin, me.size);
+ sz = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), me.size);
+
+ if(scan == K_ENTER || scan == K_KP_ENTER)
+ {
+ ServerList_Connect_Click(NULL, me);
+ return 1;
+ }
+ else if(scan == K_MOUSE2 || scan == K_SPACE)
+ {
+ if(me.nItems != 0)
+ {
++ m_play_click_sound(MENU_SOUND_OPEN);
+ main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
+ DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
+ return 1;
+ }
+ return 0;
+ }
+ else if(scan == K_INS || scan == K_MOUSE3 || scan == K_KP_INS)
+ {
+ if(me.nItems != 0)
+ {
+ me.toggleFavorite(me, me.selectedServer);
+ me.ipAddressBoxFocused = -1;
+ return 1;
+ }
+ return 0;
+ }
+ else if(SUPER(XonoticServerList).keyDown(me, scan, ascii, shift))
+ return 1;
+ else if(!me.controlledTextbox)
+ return 0;
+ else
+ return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);
+}
+
+float XonoticServerList_getTotalHeight(entity me) {
+ float num_normal_rows = me.nItems;
+ float num_headers = category_draw_count;
+ return me.itemHeight * (num_normal_rows + me.categoriesHeight * num_headers);
+}
+float XonoticServerList_getItemAtPos(entity me, float pos) {
+ pos = pos / me.itemHeight;
+ float i;
+ for (i = category_draw_count - 1; i >= 0; --i) {
+ float itemidx = category_item[i];
+ float itempos = i * me.categoriesHeight + category_item[i];
+ if (pos >= itempos + me.categoriesHeight + 1)
+ return itemidx + 1 + floor(pos - (itempos + me.categoriesHeight + 1));
+ if (pos >= itempos)
+ return itemidx;
+ }
+ // No category matches? Note that category 0 is... 0. Therefore no headings exist at all.
+ return floor(pos);
+}
+float XonoticServerList_getItemStart(entity me, float item) {
+ float i;
+ for (i = category_draw_count - 1; i >= 0; --i) {
+ float itemidx = category_item[i];
+ float itempos = i * me.categoriesHeight + category_item[i];
+ if (item >= itemidx + 1)
+ return (itempos + me.categoriesHeight + 1 + item - (itemidx + 1)) * me.itemHeight;
+ if (item >= itemidx)
+ return itempos * me.itemHeight;
+ }
+ // No category matches? Note that category 0 is... 0. Therefore no headings exist at all.
+ return item * me.itemHeight;
+}
+float XonoticServerList_getItemHeight(entity me, float item) {
+ float i;
+ for (i = 0; i < category_draw_count; ++i) {
+ // Matches exactly the headings with increased height.
+ if (item == category_item[i])
+ return me.itemHeight * (me.categoriesHeight + 1);
+ }
+ return me.itemHeight;
+}
+
+#endif
--- /dev/null
- if(scan == K_ENTER || scan == K_KP_ENTER) {
+#ifdef INTERFACE
+CLASS(XonoticSkinList) EXTENDS(XonoticListBox)
+ METHOD(XonoticSkinList, configureXonoticSkinList, void(entity))
+ ATTRIB(XonoticSkinList, rowsPerItem, float, 4)
+ METHOD(XonoticSkinList, resizeNotify, void(entity, vector, vector, vector, vector))
+ METHOD(XonoticSkinList, drawListBoxItem, void(entity, float, vector, float))
+ METHOD(XonoticSkinList, getSkins, void(entity))
+ METHOD(XonoticSkinList, setSkin, void(entity))
+ METHOD(XonoticSkinList, loadCvars, void(entity))
+ METHOD(XonoticSkinList, saveCvars, void(entity))
+ METHOD(XonoticSkinList, skinParameter, string(entity, float, float))
+ METHOD(XonoticSkinList, doubleClickListBoxItem, void(entity, float, vector))
+ METHOD(XonoticSkinList, keyDown, float(entity, float, float, float))
+ METHOD(XonoticSkinList, destroy, void(entity))
+
+ ATTRIB(XonoticSkinList, skinlist, float, -1)
+ ATTRIB(XonoticSkinList, realFontSize, vector, '0 0 0')
+ ATTRIB(XonoticSkinList, columnPreviewOrigin, float, 0)
+ ATTRIB(XonoticSkinList, columnPreviewSize, float, 0)
+ ATTRIB(XonoticSkinList, columnNameOrigin, float, 0)
+ ATTRIB(XonoticSkinList, columnNameSize, float, 0)
+ ATTRIB(XonoticSkinList, realUpperMargin1, float, 0)
+ ATTRIB(XonoticSkinList, realUpperMargin2, float, 0)
+ ATTRIB(XonoticSkinList, origin, vector, '0 0 0')
+ ATTRIB(XonoticSkinList, itemAbsSize, vector, '0 0 0')
+
+ ATTRIB(XonoticSkinList, name, string, "skinselector")
+ENDCLASS(XonoticSkinList)
+
+entity makeXonoticSkinList();
+void SetSkin_Click(entity btn, entity me);
+#endif
+
+#ifdef IMPLEMENTATION
+
+const float SKINPARM_NAME = 0;
+const float SKINPARM_TITLE = 1;
+const float SKINPARM_AUTHOR = 2;
+const float SKINPARM_PREVIEW = 3;
+const float SKINPARM_COUNT = 4;
+
+entity makeXonoticSkinList()
+{
+ entity me;
+ me = spawnXonoticSkinList();
+ me.configureXonoticSkinList(me);
+ return me;
+}
+
+void XonoticSkinList_configureXonoticSkinList(entity me)
+{
+ me.configureXonoticListBox(me);
+ me.getSkins(me);
+ me.loadCvars(me);
+}
+
+void XonoticSkinList_loadCvars(entity me)
+{
+ string s;
+ float i, n;
+ s = cvar_string("menu_skin");
+ n = me.nItems;
+ for(i = 0; i < n; ++i)
+ {
+ if(me.skinParameter(me, i, SKINPARM_NAME) == s)
+ {
+ me.selectedItem = i;
+ break;
+ }
+ }
+}
+
+void XonoticSkinList_saveCvars(entity me)
+{
+ cvar_set("menu_skin", me.skinParameter(me, me.selectedItem, SKINPARM_NAME));
+}
+
+string XonoticSkinList_skinParameter(entity me, float i, float key)
+{
+ return bufstr_get(me.skinlist, i * SKINPARM_COUNT + key);
+}
+
+void XonoticSkinList_getSkins(entity me)
+{
+ float glob, buf, i, n, fh;
+ string s;
+
+ buf = buf_create();
+ glob = search_begin("gfx/menu/*/skinvalues.txt", true, true);
+ if(glob < 0)
+ {
+ me.skinlist = buf;
+ me.nItems = 0;
+ return;
+ }
+
+ n = search_getsize(glob);
+ for(i = 0; i < n; ++i)
+ {
+ s = search_getfilename(glob, i);
+ bufstr_set(buf, i * SKINPARM_COUNT + SKINPARM_NAME, substring(s, 9, strlen(s) - 24)); // the * part
+ bufstr_set(buf, i * SKINPARM_COUNT + SKINPARM_TITLE, _("<TITLE>"));
+ bufstr_set(buf, i * SKINPARM_COUNT + SKINPARM_AUTHOR, _("<AUTHOR>"));
+ if(draw_PictureSize(strcat("/gfx/menu/", substring(s, 9, strlen(s) - 24), "/skinpreview")) == '0 0 0')
+ bufstr_set(buf, i * SKINPARM_COUNT + SKINPARM_PREVIEW, "nopreview_menuskin");
+ else
+ bufstr_set(buf, i * SKINPARM_COUNT + SKINPARM_PREVIEW, strcat("/gfx/menu/", substring(s, 9, strlen(s) - 24), "/skinpreview"));
+ fh = fopen(language_filename(s), FILE_READ);
+ if(fh < 0)
+ {
+ print("Warning: can't open skinvalues.txt file\n");
+ continue;
+ }
+ while((s = fgets(fh)))
+ {
+ // these two are handled by skinlist.qc
+ if(substring(s, 0, 6) == "title ")
+ bufstr_set(buf, i * SKINPARM_COUNT + SKINPARM_TITLE, substring(s, 6, strlen(s) - 6));
+ else if(substring(s, 0, 7) == "author ")
+ bufstr_set(buf, i * SKINPARM_COUNT + SKINPARM_AUTHOR, substring(s, 7, strlen(s) - 7));
+ }
+ fclose(fh);
+ }
+
+ search_end(glob);
+
+ me.skinlist = buf;
+ me.nItems = n;
+}
+
+void XonoticSkinList_destroy(entity me)
+{
+ buf_del(me.skinlist);
+}
+
+void XonoticSkinList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+{
+ me.itemAbsSize = '0 0 0';
+ SUPER(XonoticSkinList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
+
+ me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight));
+ me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth)));
+ me.realUpperMargin1 = 0.5 * (1 - 2.5 * me.realFontSize.y);
+ me.realUpperMargin2 = me.realUpperMargin1 + 1.5 * me.realFontSize.y;
+
+ me.columnPreviewOrigin = 0;
+ me.columnPreviewSize = me.itemAbsSize.y / me.itemAbsSize.x * 4 / 3;
+ me.columnNameOrigin = me.columnPreviewOrigin + me.columnPreviewSize + me.realFontSize.x;
+ me.columnNameSize = 1 - me.columnPreviewSize - 2 * me.realFontSize.x;
+}
+
+void XonoticSkinList_drawListBoxItem(entity me, float i, vector absSize, float isSelected)
+{
+ string s;
+
+ if(isSelected)
+ draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
+
+ s = me.skinParameter(me, i, SKINPARM_PREVIEW);
+ draw_Picture(me.columnPreviewOrigin * eX, s, me.columnPreviewSize * eX + eY, '1 1 1', 1);
+
+ s = me.skinParameter(me, i, SKINPARM_TITLE);
+ s = draw_TextShortenToWidth(s, me.columnNameSize, 0, me.realFontSize);
+ draw_Text(me.realUpperMargin1 * eY + (me.columnNameOrigin + 0.00 * (me.columnNameSize - draw_TextWidth(s, 0, me.realFontSize))) * eX, s, me.realFontSize, SKINCOLOR_SKINLIST_TITLE, SKINALPHA_TEXT, 0);
+
+ s = me.skinParameter(me, i, SKINPARM_AUTHOR);
+ s = draw_TextShortenToWidth(s, me.columnNameSize, 0, me.realFontSize);
+ draw_Text(me.realUpperMargin2 * eY + (me.columnNameOrigin + 1.00 * (me.columnNameSize - draw_TextWidth(s, 0, me.realFontSize))) * eX, s, me.realFontSize, SKINCOLOR_SKINLIST_AUTHOR, SKINALPHA_TEXT, 0);
+}
+
+void XonoticSkinList_setSkin(entity me)
+{
+ me.saveCvars(me);
+ localcmd("\nmenu_restart\nmenu_cmd skinselect\n");
+}
+
+void SetSkin_Click(entity btn, entity me)
+{
+ me.setSkin(me);
+}
+
+void XonoticSkinList_doubleClickListBoxItem(entity me, float i, vector where)
+{
++ m_play_click_sound(MENU_SOUND_EXECUTE);
+ me.setSkin(me);
+}
+
+float XonoticSkinList_keyDown(entity me, float scan, float ascii, float shift)
+{
++ if(scan == K_ENTER || scan == K_KP_ENTER)
++ {
++ m_play_click_sound(MENU_SOUND_EXECUTE);
+ me.setSkin(me);
+ return 1;
+ }
+ else
+ return SUPER(XonoticSkinList).keyDown(me, scan, ascii, shift);
+}
+#endif
+#include "waypointsprites.qh"
+
+#include "cl_impulse.qh"
+#include "cl_player.qh"
+#include "ent_cs.qh"
+#include "g_subs.qh"
+#include "ipban.qh"
+#include "miscfunctions.qh"
+#include "portals.qh"
+#include "teamplay.qh"
+#include "playerdemo.qh"
+#include "secret.qh"
+
+#include "bot/bot.qh"
+#include "bot/navigation.qh"
+
+#include "weapons/hitplot.qh"
+#include "weapons/weaponsystem.qh"
+
+#include "../common/net_notice.qh"
+#include "../common/physics.qh"
+
+#include "../common/monsters/sv_monsters.qh"
+
+#include "../warpzonelib/server.qh"
+
+float c1, c2, c3, c4;
+
void send_CSQC_teamnagger() {
WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
}
-float ClientData_Send(entity to, float sf)
+float ClientData_Send(entity to, int sf)
{
if(to != self.owner)
{
error("wtf");
- return FALSE;
+ return false;
}
entity e;
if(sf & 8)
{
- WriteAngle(MSG_ENTITY, e.v_angle_x);
- WriteAngle(MSG_ENTITY, e.v_angle_y);
+ WriteAngle(MSG_ENTITY, e.v_angle.x);
+ WriteAngle(MSG_ENTITY, e.v_angle.y);
}
- return TRUE;
+ return true;
}
void ClientData_Attach()
{
- Net_LinkEntity(self.clientdata = spawn(), FALSE, 0, ClientData_Send);
+ Net_LinkEntity(self.clientdata = spawn(), false, 0, ClientData_Send);
self.clientdata.drawonlytoclient = self;
self.clientdata.owner = self;
}
if(IS_PLAYER(self)) { pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); }
- spot = SelectSpawnPoint (TRUE);
+ spot = SelectSpawnPoint (true);
if(!spot)
error("No spawnpoints for observers?!?\n");
RemoveGrapplingHook(self); // Wazat's Grappling Hook
if(autocvar_g_chat_nospectators == 1 || (cvar("g_warmup") && !(warmup_stage || gameover) && autocvar_g_chat_nospectators == 2))
Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_CHAT_NOSPECTATORS);
- if(self.just_joined == FALSE) {
+ if(self.just_joined == false) {
LogTeamchange(self.playerid, -1, 4);
} else
- self.just_joined = FALSE;
+ self.just_joined = false;
}
PlayerScore_Clear(self); // clear scores when needed
self.spectatortime = time;
self.classname = "observer";
- self.iscreature = FALSE;
+ self.iscreature = false;
self.teleportable = TELEPORT_SIMPLE;
- self.damagedbycontents = FALSE;
+ self.damagedbycontents = false;
self.health = -666;
self.takedamage = DAMAGE_NO;
self.solid = SOLID_NOT;
self.deadflag = DEAD_NO;
self.angles = spot.angles;
self.angles_z = 0;
- self.fixangle = TRUE;
- self.crouch = FALSE;
+ self.fixangle = true;
+ self.crouch = false;
self.revival_time = 0;
setorigin (self, (spot.origin + PL_VIEW_OFS)); // offset it so that the spectator spawns higher off the ground, looks better this way
defaultmodel = "";
defaultskin = 0;
- chmdl = FALSE;
+ chmdl = false;
if(autocvar_sv_defaultcharacter == 1)
{
m2 = self.maxs;
setplayermodel (self, defaultmodel);
setsize (self, m1, m2);
- chmdl = TRUE;
+ chmdl = true;
}
oldskin = self.skin;
m2 = self.maxs;
setplayermodel (self, self.playermodel);
setsize (self, m1, m2);
- chmdl = TRUE;
+ chmdl = true;
}
oldskin = self.skin;
accuracy_resend(self);
if(self.team < 0)
- JoinBestTeam(self, FALSE, TRUE);
+ JoinBestTeam(self, false, true);
- spot = SelectSpawnPoint (FALSE);
+ spot = SelectSpawnPoint (false);
if(!spot)
{
Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_JOIN_NOSPAWNS);
vehicles_exit(VHEF_RELESE);
self.classname = "player";
- self.wasplayer = TRUE;
- self.iscreature = TRUE;
+ self.wasplayer = true;
+ self.iscreature = true;
self.teleportable = TELEPORT_NORMAL;
- self.damagedbycontents = TRUE;
+ self.damagedbycontents = true;
self.movetype = MOVETYPE_WALK;
self.solid = SOLID_SLIDEBOX;
self.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID;
self.angles_z = 0; // never spawn tilted even if the spot says to
if(IS_BOT_CLIENT(self))
self.v_angle = self.angles;
- self.fixangle = TRUE; // turn this way immediately
+ self.fixangle = true; // turn this way immediately
self.velocity = '0 0 0';
self.avelocity = '0 0 0';
self.punchangle = '0 0 0';
entity spawnevent = spawn();
spawnevent.owner = self;
- Net_LinkEntity(spawnevent, FALSE, 0.5, SpawnEvent_Send);
+ Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send);
// Cut off any still running player sounds.
stopsound(self, CH_PLAYER_SINGLE);
FixPlayermodel();
self.drawonlytoclient = world;
- self.crouch = FALSE;
+ self.crouch = false;
self.view_ofs = PL_VIEW_OFS;
setsize (self, PL_MIN, PL_MAX);
self.spawnorigin = spot.origin;
- setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24));
+ setorigin (self, spot.origin + '0 0 1' * (1 - self.mins.z - 24));
// don't reset back to last position, even if new position is stuck in solid
self.oldorigin = self.origin;
self.prevorigin = self.origin;
self.event_damage = PlayerDamage;
- self.bot_attack = TRUE;
- self.monster_attack = TRUE;
-
+ self.bot_attack = true;
+ self.monster_attack = true;
+
self.spider_slowness = 0;
self.BUTTON_ATCK = self.BUTTON_JUMP = self.BUTTON_ATCK2 = 0;
self.colormod = '1 1 1' * autocvar_g_player_brightness;
self.exteriorweaponentity.alpha = default_weapon_alpha;
- self.speedrunning = FALSE;
+ self.speedrunning = false;
//stuffcmd(self, "chase_active 0");
//stuffcmd(self, "set viewsize $tmpviewsize \n");
WEP_ACTION(j, WR_RESETPLAYER);
// all weapons must be fully loaded when we spawn
- entity e;
- e = get_weaponinfo(j);
+ entity e = get_weaponinfo(j);
if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
self.(weapon_load[j]) = e.reloading_ammo;
}
WriteByte(MSG_ENTITY, WEP_CVAR_SEC(hagar, load_max)); // hagar max loadable rockets // WEAPONTODO
WriteCoord(MSG_ENTITY, autocvar_g_trueaim_minrange);
WriteByte(MSG_ENTITY, WEP_CVAR(porto, secondary)); // WEAPONTODO
- return TRUE;
+ return true;
}
void ClientInit_CheckUpdate()
e = spawn();
e.classname = "clientinit";
e.think = ClientInit_CheckUpdate;
- Net_LinkEntity(e, FALSE, 0, ClientInit_SendEntity);
+ Net_LinkEntity(e, false, 0, ClientInit_SendEntity);
o = self;
self = e;
{
if(self.killindicator_teamchange == -1)
{
- JoinBestTeam( self, FALSE, TRUE );
+ JoinBestTeam( self, false, true );
}
else if(self.killindicator_teamchange == -2)
{
return 0;
}
+ #ifdef DP_EXT_PRECONNECT
+ /*
+ =============
+ ClientPreConnect
+
+ Called once (not at each match start) when a client begins a connection to the server
+ =============
+ */
+ void ClientPreConnect (void)
+ {
+ if(autocvar_sv_eventlog)
+ {
+ GameLogEcho(sprintf(":connect:%d:%d:%s",
+ self.playerid,
+ num_for_edict(self),
+ ((IS_REAL_CLIENT(self)) ? self.netaddress : "bot")
+ ));
+ }
+ }
+ #endif
+
/*
=============
ClientConnect
if(self.team_forced > 0)
self.team_forced = 0;
- JoinBestTeam(self, FALSE, FALSE); // if the team number is valid, keep it
+ JoinBestTeam(self, false, false); // if the team number is valid, keep it
if((autocvar_sv_spectate == 1) || autocvar_g_campaign || self.team_forced < 0) {
self.classname = "observer";
LogTeamchange(self.playerid, self.team, 1);
- self.just_joined = TRUE; // stop spamming the eventlog with additional lines when the client connects
+ self.just_joined = true; // stop spamming the eventlog with additional lines when the client connects
self.netname_previous = strzone(self.netname);
self.chatbubbleentity.nextthink = time;
setmodel(self.chatbubbleentity, "models/misc/chatbubble.spr"); // precision set below
//setorigin(self.chatbubbleentity, self.origin + '0 0 15' + self.maxs_z * '0 0 1');
- setorigin(self.chatbubbleentity, '0 0 15' + self.maxs_z * '0 0 1');
+ setorigin(self.chatbubbleentity, '0 0 15' + self.maxs.z * '0 0 1');
setattachment(self.chatbubbleentity, self, ""); // sticks to moving player better, also conserves bandwidth
self.chatbubbleentity.mdl = self.chatbubbleentity.model;
self.chatbubbleentity.model = "";
void GetPressedKeys(void) {
MUTATOR_CALLHOOK(GetPressedKeys);
- if (self.movement_x > 0) // get if movement keys are pressed
- { // forward key pressed
- self.pressedkeys |= KEY_FORWARD;
- self.pressedkeys &= ~KEY_BACKWARD;
- }
- else if (self.movement_x < 0)
- { // backward key pressed
- self.pressedkeys |= KEY_BACKWARD;
- self.pressedkeys &= ~KEY_FORWARD;
- }
- else
- { // no x input
- self.pressedkeys &= ~KEY_FORWARD;
- self.pressedkeys &= ~KEY_BACKWARD;
- }
-
- if (self.movement_y > 0)
- { // right key pressed
- self.pressedkeys |= KEY_RIGHT;
- self.pressedkeys &= ~KEY_LEFT;
- }
- else if (self.movement_y < 0)
- { // left key pressed
- self.pressedkeys |= KEY_LEFT;
- self.pressedkeys &= ~KEY_RIGHT;
- }
- else
- { // no y input
- self.pressedkeys &= ~KEY_RIGHT;
- self.pressedkeys &= ~KEY_LEFT;
- }
-
- if (self.BUTTON_JUMP) // get if jump and crouch keys are pressed
- self.pressedkeys |= KEY_JUMP;
- else
- self.pressedkeys &= ~KEY_JUMP;
- if (self.BUTTON_CROUCH)
- self.pressedkeys |= KEY_CROUCH;
- else
- self.pressedkeys &= ~KEY_CROUCH;
-
- if (self.BUTTON_ATCK)
- self.pressedkeys |= KEY_ATCK;
- else
- self.pressedkeys &= ~KEY_ATCK;
- if (self.BUTTON_ATCK2)
- self.pressedkeys |= KEY_ATCK2;
- else
- self.pressedkeys &= ~KEY_ATCK2;
+ #define X(var,bit,flag) (flag ? var |= bit : var &= ~bit)
+ X(self.pressedkeys, KEY_FORWARD, PHYS_INPUT_MOVEVALUES(self)_x > 0);
+ X(self.pressedkeys, KEY_BACKWARD, PHYS_INPUT_MOVEVALUES(self)_x < 0);
+ X(self.pressedkeys, KEY_RIGHT, PHYS_INPUT_MOVEVALUES(self)_y > 0);
+ X(self.pressedkeys, KEY_LEFT, PHYS_INPUT_MOVEVALUES(self)_y < 0);
+
+ X(self.pressedkeys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(self));
+ X(self.pressedkeys, KEY_CROUCH, PHYS_INPUT_BUTTON_CROUCH(self));
+ X(self.pressedkeys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(self));
+ X(self.pressedkeys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(self));
+ #undef X
}
/*
self.frozen = spectatee.frozen;
self.revive_progress = spectatee.revive_progress;
if(!self.BUTTON_USE)
- self.fixangle = TRUE;
+ self.fixangle = true;
setorigin(self, spectatee.origin);
setsize(self, spectatee.mins, spectatee.maxs);
SetZoomState(spectatee.zoomstate);
self.hud = spectatee.hud;
if(spectatee.vehicle)
{
- self.fixangle = FALSE;
+ self.fixangle = false;
//self.velocity = spectatee.vehicle.velocity;
self.vehicle_health = spectatee.vehicle_health;
self.vehicle_shield = spectatee.vehicle_shield;
msg_entity = self;
WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
- WriteAngle(MSG_ONE, spectatee.v_angle_x);
- WriteAngle(MSG_ONE, spectatee.v_angle_y);
- WriteAngle(MSG_ONE, spectatee.v_angle_z);
+ WriteAngle(MSG_ONE, spectatee.v_angle.x);
+ WriteAngle(MSG_ONE, spectatee.v_angle.y);
+ WriteAngle(MSG_ONE, spectatee.v_angle.z);
//WriteByte (MSG_ONE, SVC_SETVIEW);
// WriteEntity(MSG_ONE, self);
float SpectateSet()
{
if(self.enemy.classname != "player")
- return FALSE;
+ return false;
/*if(self.enemy.vehicle)
{
if(!SpectateUpdate())
PutObserverInServer();
//}
- return TRUE;
+ return true;
}
void SetSpectator(entity player, entity spectatee)
// NOTE: chain order is from the highest to the lower entnum (unlike find)
other = findchain(classname, "player");
if (!other) // no player
- return FALSE;
+ return false;
entity first = other;
// skip players until current spectated player
while(other.team != self.team)
other = other.chain;
if(other == self.enemy)
- return TRUE;
+ return true;
}
}
else
nades_RemoveBonus(self);
if(autocvar_g_campaign || autocvar_g_balance_teams)
- { JoinBestTeam(self, FALSE, TRUE); }
+ { JoinBestTeam(self, false, true); }
if(autocvar_g_campaign)
{ campaign_bots_may_start = 1; }
/**
* Determines whether the player is allowed to join. This depends on cvar
- * g_maxplayers, if it isn't used this function always return TRUE, otherwise
+ * g_maxplayers, if it isn't used this function always return true, otherwise
* it checks whether the number of currently playing players exceeds g_maxplayers.
* @return int number of free slots for players, 0 if none
*/
case 192: // charmap space
if (!autocvar_utf8_enable)
break;
- return FALSE;
+ return false;
case 160: // space in unicode fonts
case 0xE000 + 192: // utf8 charmap space
if (autocvar_utf8_enable)
break;
default:
- return FALSE;
+ return false;
}
}
- return TRUE;
+ return true;
}
/*
*/
.float usekeypressed;
void() nexball_setstatus;
-.float items_added;
+.int items_added;
void PlayerPreThink (void)
{
WarpZone_PlayerPhysics_FixVAngle();
{
self.revive_progress = bound(0, self.revive_progress - frametime * self.revive_speed, 1);
self.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * self.revive_progress );
-
+
if(self.health < 1)
{
if(self.vehicle)
// FIXME turn this into CSQC stuff
self.v_angle = self.lastV_angle;
self.angles = self.lastV_angle;
- self.fixangle = TRUE;
+ self.fixangle = true;
}
if(frametime)
if(self.vortex_charge > WEP_CVAR(vortex, charge_animlimit))
{
- self.weaponentity_glowmod_x = self.weaponentity_glowmod_x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
- self.weaponentity_glowmod_y = self.weaponentity_glowmod_y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
- self.weaponentity_glowmod_z = self.weaponentity_glowmod_z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
+ self.weaponentity_glowmod_x = self.weaponentity_glowmod.x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
+ self.weaponentity_glowmod_y = self.weaponentity_glowmod.y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
+ self.weaponentity_glowmod_z = self.weaponentity_glowmod.z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
}
}
else
- self.weaponentity_glowmod = colormapPaletteColor(self.clientcolors & 0x0F, TRUE) * 2;
+ self.weaponentity_glowmod = colormapPaletteColor(self.clientcolors & 0x0F, true) * 2;
player_powerups();
}
do_crouch = 0;
// WEAPONTODO: THIS SHIT NEEDS TO GO EVENTUALLY
- // It cannot be predicted by the engine!
+ // It cannot be predicted by the engine!
if((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink)
do_crouch = 0;
{
if (!self.crouch)
{
- self.crouch = TRUE;
+ self.crouch = true;
self.view_ofs = PL_CROUCH_VIEW_OFS;
setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX);
- // setanim(self, self.anim_duck, FALSE, TRUE, TRUE); // this anim is BROKEN anyway
+ // setanim(self, self.anim_duck, false, true, true); // this anim is BROKEN anyway
}
}
else
{
if (self.crouch)
{
- tracebox(self.origin, PL_MIN, PL_MAX, self.origin, FALSE, self);
+ tracebox(self.origin, PL_MIN, PL_MAX, self.origin, false, self);
if (!trace_startsolid)
{
- self.crouch = FALSE;
+ self.crouch = false;
self.view_ofs = PL_VIEW_OFS;
setsize (self, PL_MIN, PL_MAX);
}
player_regen();
- // WEAPONTODO: Add a weapon request for this
+ // WEAPONTODO: Add a weapon request for this
// rot vortex charge to the charge limit
if(WEP_CVAR(vortex, charge_rot_rate) && self.vortex_charge > WEP_CVAR(vortex, charge_limit) && self.vortex_charge_rottime < time)
self.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), self.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+ #include "../../dpdefs/dpextensions.qh"
+ #include "../../common/util.qh"
+ #include "../../common/command/shared_defs.qh"
+ #include "../autocvars.qh"
+ #include "common.qh"
+ #include "banning.qh"
+ #include "../ipban.qh"
+#endif
// =====================================================
// Banning and kicking command code, written by Samual
// Last updated: December 29th, 2011
if(argc >= 2)
{
entity client = GetIndexedEntity(argc, 1);
- float accepted = VerifyClientEntity(client, true, false);
+ float accepted = VerifyKickableEntity(client);
float reason_arg, bantime, masksize;
string reason;
if(argc >= 2)
{
entity client = GetFilteredEntity(argv(1));
- float accepted = VerifyClientEntity(client, TRUE, FALSE);
+ float accepted = VerifyClientEntity(client, true, false);
if(accepted > 0)
{
- client.muted = TRUE;
+ client.muted = true;
return;
}
else
if(argc >= 2)
{
entity client = GetFilteredEntity(argv(1));
- float accepted = VerifyClientEntity(client, TRUE, FALSE);
+ float accepted = VerifyClientEntity(client, true, false);
if(accepted > 0)
{
- client.muted = FALSE;
+ client.muted = false;
return;
}
else
#define BAN_COMMAND(name,function,description) \
{ if(strtolower(description) != "") { print(" ^2", name, "^7: ", description, "\n"); } }
- BAN_COMMANDS(0, 0, "")
+ BAN_COMMANDS(0, 0, "");
#undef BAN_COMMAND
return;
float BanCommand_macro_command(float argc, string command)
{
#define BAN_COMMAND(name,function,description) \
- { if(name == strtolower(argv(0))) { function; return TRUE; } }
+ { if(name == strtolower(argv(0))) { function; return true; } }
- BAN_COMMANDS(CMD_REQUEST_COMMAND, argc, command)
+ BAN_COMMANDS(CMD_REQUEST_COMMAND, argc, command);
#undef BAN_COMMAND
- return FALSE;
+ return false;
}
float BanCommand_macro_usage(float argc)
{
#define BAN_COMMAND(name,function,description) \
- { if(name == strtolower(argv(1))) { function; return TRUE; } }
+ { if(name == strtolower(argv(1))) { function; return true; } }
- BAN_COMMANDS(CMD_REQUEST_USAGE, argc, "")
+ BAN_COMMANDS(CMD_REQUEST_USAGE, argc, "");
#undef BAN_COMMAND
- return FALSE;
+ return false;
}
void BanCommand_macro_write_aliases(float fh)
#define BAN_COMMAND(name,function,description) \
{ if(strtolower(description) != "") { CMD_Write_Alias("qc_cmd_sv", name, description); } }
- BAN_COMMANDS(0, 0, "")
+ BAN_COMMANDS(0, 0, "");
#undef BAN_COMMAND
return;
if(BanCommand_macro_command(argc, command)) // continue as usual and scan for normal commands
{
- return TRUE; // handled by one of the above GenericCommand_* functions
+ return true; // handled by one of the above GenericCommand_* functions
}
- return FALSE;
+ return false;
}
+#include "common.qh"
+
+#include "../../common/counting.qh"
+
+
// ====================================================
// Shared code for server commands, written by Samual
// Last updated: December 27th, 2011
return admin_name(); //((autocvar_sv_adminnick != "") ? autocvar_sv_adminnick : autocvar_hostname);
}
+ // verify that the client provided is acceptable for kicking
+ float VerifyKickableEntity(entity client)
+ {
+ if (!IS_REAL_CLIENT(client))
+ return CLIENT_NOT_REAL;
+ return CLIENT_ACCEPTABLE;
+ }
+
// verify that the client provided is acceptable for use
float VerifyClientEntity(entity client, float must_be_real, float must_be_bots)
{
float VerifyClientNumber(float tmp_number)
{
if((tmp_number < 1) || (tmp_number > maxclients))
- return FALSE;
+ return false;
else
- return TRUE;
+ return true;
}
entity GetIndexedEntity(float argc, float start_index)
// unlock the view for players so they can move around again
FOR_EACH_REALPLAYER(tmp_player)
- tmp_player.fixangle = FALSE;
+ tmp_player.fixangle = false;
timeout_handler_reset();
}
{
case CMD_REQUEST_COMMAND:
{
- float i;
-
- for(i = 0; i < 10; ++i)
+ for(int i = 0; i < 10; ++i)
if(records_reply[i] != "")
print_to(caller, records_reply[i]);
print_to(caller, strcat("realtime = ", ftos(gettime(GETTIME_REALTIME))));
print_to(caller, strcat("hires = ", ftos(gettime(GETTIME_HIRES))));
print_to(caller, strcat("uptime = ", ftos(gettime(GETTIME_UPTIME))));
- print_to(caller, strcat("localtime = ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y")));
- print_to(caller, strcat("gmtime = ", strftime(FALSE, "%a %b %e %H:%M:%S %Z %Y")));
+ print_to(caller, strcat("localtime = ", strftime(true, "%a %b %e %H:%M:%S %Z %Y")));
+ print_to(caller, strcat("gmtime = ", strftime(false, "%a %b %e %H:%M:%S %Z %Y")));
return;
}
}
}
*/
-
-
-// ==================================
-// Macro system for common commands
-// ==================================
-
-// Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
-#define COMMON_COMMANDS(request,caller,arguments,command) \
- COMMON_COMMAND("cvar_changes", CommonCommand_cvar_changes(request, caller), "Prints a list of all changed server cvars") \
- COMMON_COMMAND("cvar_purechanges", CommonCommand_cvar_purechanges(request, caller), "Prints a list of all changed gameplay cvars") \
- COMMON_COMMAND("info", CommonCommand_info(request, caller, arguments), "Request for unique server information set up by admin") \
- COMMON_COMMAND("ladder", CommonCommand_ladder(request, caller), "Get information about top players if supported") \
- COMMON_COMMAND("lsmaps", CommonCommand_lsmaps(request, caller), "List maps which can be used with the current game mode") \
- COMMON_COMMAND("printmaplist", CommonCommand_printmaplist(request, caller), "Display full server maplist reply") \
- COMMON_COMMAND("rankings", CommonCommand_rankings(request, caller), "Print information about rankings") \
- COMMON_COMMAND("records", CommonCommand_records(request, caller), "List top 10 records for the current map") \
- COMMON_COMMAND("teamstatus", CommonCommand_teamstatus(request, caller), "Show information about player and team scores") \
- COMMON_COMMAND("time", CommonCommand_time(request, caller), "Print different formats/readouts of time") \
- COMMON_COMMAND("timein", CommonCommand_timein(request, caller), "Resume the game from being paused with a timeout") \
- COMMON_COMMAND("timeout", CommonCommand_timeout(request, caller), "Call a timeout which pauses the game for certain amount of time unless unpaused") \
- COMMON_COMMAND("vote", VoteCommand(request, caller, arguments, command), "Request an action to be voted upon by players") \
- COMMON_COMMAND("who", CommonCommand_who(request, caller, arguments), "Display detailed client information about all players") \
- /* nothing */
-
-void CommonCommand_macro_help(entity caller)
-{
- #define COMMON_COMMAND(name,function,description) \
- { print_to(caller, strcat(" ^2", name, "^7: ", description)); }
-
- COMMON_COMMANDS(0, caller, 0, "")
- #undef COMMON_COMMAND
-
- return;
-}
-
-float CommonCommand_macro_command(float argc, entity caller, string command)
-{
- #define COMMON_COMMAND(name,function,description) \
- { if(name == strtolower(argv(0))) { function; return TRUE; } }
-
- COMMON_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, command)
- #undef COMMON_COMMAND
-
- return FALSE;
-}
-
-float CommonCommand_macro_usage(float argc, entity caller)
-{
- #define COMMON_COMMAND(name,function,description) \
- { if(name == strtolower(argv(1))) { function; return TRUE; } }
-
- COMMON_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "")
- #undef COMMON_COMMAND
-
- return FALSE;
-}
-
-void CommonCommand_macro_write_aliases(float fh)
-{
- #define COMMON_COMMAND(name,function,description) \
- { CMD_Write_Alias("qc_cmd_svcmd", name, description); }
-
- COMMON_COMMANDS(0, world, 0, "")
- #undef COMMON_COMMAND
-
- return;
-}
+#ifndef COMMAND_COMMON_H
+#define COMMAND_COMMON_H
+
+#include "vote.qh"
+
+#include "../../common/command/generic.qh"
+#include "../../common/command/shared_defs.qh"
+
// ============================================================
// Shared declarations for server commands, written by Samual
// Last updated: December 30th, 2011
// ============================================================
// client verification results
-#define CLIENT_ACCEPTABLE 1
-#define CLIENT_DOESNT_EXIST -1
-#define CLIENT_NOT_REAL -2
-#define CLIENT_NOT_BOT -3
+const float CLIENT_ACCEPTABLE = 1;
+const float CLIENT_DOESNT_EXIST = -1;
+const float CLIENT_NOT_REAL = -2;
+const float CLIENT_NOT_BOT = -3;
// definitions for timeouts
-#define TIMEOUT_INACTIVE 0
-#define TIMEOUT_LEADTIME 1
-#define TIMEOUT_ACTIVE 2
+const float TIMEOUT_INACTIVE = 0;
+const float TIMEOUT_LEADTIME = 1;
+const float TIMEOUT_ACTIVE = 2;
// timeout which pauses the game by setting the slowmo value extremely low.
-#define TIMEOUT_SLOWMO_VALUE 0.0001
+const float TIMEOUT_SLOWMO_VALUE = 0.0001;
// global timeout information declarations
entity timeout_caller; // contains the entity of the player who started the last timeout
void CommonCommand_macro_write_aliases(float fh);
// keep track of the next token to use for argc
-float next_token;
+float next_token;
+
+// select the proper prefix for usage and other messages
+string GetCommandPrefix(entity caller);
+
+// if client return player nickname, or if server return admin nickname
+string GetCallerName(entity caller);
+
++// verify that the client provided is acceptable for kicking
++float VerifyKickableEntity(entity client);
++
+// verify that the client provided is acceptable for use
+float VerifyClientEntity(entity client, float must_be_real, float must_be_bots);
+
+// if the client is not acceptable, return a string to be used for error messages
+string GetClientErrorString(float clienterror, string original_input);
+
+// is this entity number even in the possible range of entities?
+float VerifyClientNumber(float tmp_number);
+
+entity GetIndexedEntity(float argc, float start_index);
+
+// find a player which matches the input string, and return their entity
+entity GetFilteredEntity(string input);
+
+// same thing, but instead return their edict number
+float GetFilteredNumber(string input);
+
+// switch between sprint and print depending on whether the receiver is the server or a player
+void print_to(entity to, string input);
+
+// ==========================================
+// Supporting functions for common commands
+// ==========================================
+
+// used by CommonCommand_timeout() and CommonCommand_timein() to handle game pausing and messaging and such.
+void timeout_handler_reset();
+
+void timeout_handler_think();
+
+// ===================================================
+// Common commands used in both sv_cmd.qc and cmd.qc
+// ===================================================
+
+void CommonCommand_cvar_changes(float request, entity caller);
+
+void CommonCommand_cvar_purechanges(float request, entity caller);
+
+void CommonCommand_info(float request, entity caller, float argc);
+
+void CommonCommand_ladder(float request, entity caller);
+
+void CommonCommand_lsmaps(float request, entity caller);
+
+void CommonCommand_printmaplist(float request, entity caller);
+
+void CommonCommand_rankings(float request, entity caller);
+
+void CommonCommand_records(float request, entity caller);
+
+void CommonCommand_teamstatus(float request, entity caller);
+
+void CommonCommand_time(float request, entity caller);
+
+void CommonCommand_timein(float request, entity caller);
+
+void CommonCommand_timeout(float request, entity caller);
+
+void CommonCommand_who(float request, entity caller, float argc);
+
+
+// ==================================
+// Macro system for common commands
+// ==================================
+
+// Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
+#define COMMON_COMMANDS(request,caller,arguments,command) \
+ COMMON_COMMAND("cvar_changes", CommonCommand_cvar_changes(request, caller), "Prints a list of all changed server cvars") \
+ COMMON_COMMAND("cvar_purechanges", CommonCommand_cvar_purechanges(request, caller), "Prints a list of all changed gameplay cvars") \
+ COMMON_COMMAND("info", CommonCommand_info(request, caller, arguments), "Request for unique server information set up by admin") \
+ COMMON_COMMAND("ladder", CommonCommand_ladder(request, caller), "Get information about top players if supported") \
+ COMMON_COMMAND("lsmaps", CommonCommand_lsmaps(request, caller), "List maps which can be used with the current game mode") \
+ COMMON_COMMAND("printmaplist", CommonCommand_printmaplist(request, caller), "Display full server maplist reply") \
+ COMMON_COMMAND("rankings", CommonCommand_rankings(request, caller), "Print information about rankings") \
+ COMMON_COMMAND("records", CommonCommand_records(request, caller), "List top 10 records for the current map") \
+ COMMON_COMMAND("teamstatus", CommonCommand_teamstatus(request, caller), "Show information about player and team scores") \
+ COMMON_COMMAND("time", CommonCommand_time(request, caller), "Print different formats/readouts of time") \
+ COMMON_COMMAND("timein", CommonCommand_timein(request, caller), "Resume the game from being paused with a timeout") \
+ COMMON_COMMAND("timeout", CommonCommand_timeout(request, caller), "Call a timeout which pauses the game for certain amount of time unless unpaused") \
+ COMMON_COMMAND("vote", VoteCommand(request, caller, arguments, command), "Request an action to be voted upon by players") \
+ COMMON_COMMAND("who", CommonCommand_who(request, caller, arguments), "Display detailed client information about all players") \
+ /* nothing */
+
+void CommonCommand_macro_help(entity caller)
+{
+ #define COMMON_COMMAND(name,function,description) \
+ { print_to(caller, strcat(" ^2", name, "^7: ", description)); }
+
+ COMMON_COMMANDS(0, caller, 0, "");
+ #undef COMMON_COMMAND
+
+ return;
+}
+
+float CommonCommand_macro_command(float argc, entity caller, string command)
+{
+ #define COMMON_COMMAND(name,function,description) \
+ { if(name == strtolower(argv(0))) { function; return true; } }
+
+ COMMON_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, command);
+ #undef COMMON_COMMAND
+
+ return false;
+}
+
+float CommonCommand_macro_usage(float argc, entity caller)
+{
+ #define COMMON_COMMAND(name,function,description) \
+ { if(name == strtolower(argv(1))) { function; return true; } }
+
+ COMMON_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "");
+ #undef COMMON_COMMAND
+
+ return false;
+}
+
+void CommonCommand_macro_write_aliases(float fh)
+{
+ #define COMMON_COMMAND(name,function,description) \
+ { CMD_Write_Alias("qc_cmd_svcmd", name, description); }
+
+ COMMON_COMMANDS(0, world, 0, "");
+ #undef COMMON_COMMAND
+
+ return;
+}
+
+
+#endif
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+ #include "../../dpdefs/progsdefs.qh"
+ #include "../../dpdefs/dpextensions.qh"
+ #include "../../common/constants.qh"
+ #include "../../common/util.qh"
+ #include "../../common/command/shared_defs.qh"
+ #include "../autocvars.qh"
+ #include "../constants.qh"
+ #include "../defs.qh"
+ #include "../../common/notifications.qh"
+ #include "../mutators/mutators_include.qh"
+ #include "../../common/mapinfo.qh"
+ #include "common.qh"
+ #include "vote.qh"
+ #include "../../common/playerstats.qh"
+ #include "../scores.qh"
+ #include "../race.qh"
+ #include "../round_handler.qh"
+#endif
+
// =============================================
// Server side voting code, reworked by Samual
// Last updated: December 27th, 2011
// Nagger for players to know status of voting
float Nagger_SendEntity(entity to, float sendflags)
{
- float nags, i, f, b;
+ int nags, i, f, b;
entity e;
WriteByte(MSG_ENTITY, ENT_CLIENT_NAGGER);
}
}
- return TRUE;
+ return true;
}
void Nagger_Init()
{
- Net_LinkEntity(nagger = spawn(), FALSE, 0, Nagger_SendEntity);
+ Net_LinkEntity(nagger = spawn(), false, 0, Nagger_SendEntity);
}
void Nagger_VoteChanged()
if(vote_endtime > 0) // a vote was called
if(time > vote_endtime) // time is up
{
- VoteCount(FALSE);
+ VoteCount(false);
}
return;
void ReadyRestart_think()
{
restart_mapalreadyrestarted = 1;
- reset_map(TRUE);
+ reset_map(true);
Score_ClearAll();
remove(self);
if(autocvar_sv_timeout) { FOR_EACH_REALPLAYER(tmp_player) { tmp_player.allowed_timeouts = autocvar_sv_timeout_number; } }
//reset map immediately if this cvar is not set
- if (!autocvar_sv_ready_restart_after_countdown) { reset_map(TRUE); }
+ if (!autocvar_sv_ready_restart_after_countdown) { reset_map(true); }
if(autocvar_sv_eventlog) { GameLogEcho(":restart"); }
}
|| ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY)
|| (from_server && assignment == VC_ASGNMNT_SERVERONLY)))
{
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
string VoteCommand_extractcommand(string input, float startpos, float argc)
|| (strstrofs(vote_command, "\n", 0) >= 0)
|| (strstrofs(vote_command, "\r", 0) >= 0)
|| (strstrofs(vote_command, "$", 0) >= 0))
- return FALSE;
+ return false;
- return TRUE;
+ return true;
}
float VoteCommand_checkinlist(string vote_command, string list)
string l = strcat(" ", list, " ");
if(strstrofs(l, strcat(" ", vote_command, " "), 0) >= 0)
- return TRUE;
+ return true;
- return FALSE;
+ return false;
}
string ValidateMap(string validated_map, entity caller)
float checkmate;
if(cmdrestriction == "")
- return TRUE;
+ return true;
++startpos; // skip command name
minargs = stof(cmdrestriction);
if(argc - startpos < minargs)
- return FALSE;
+ return false;
p = strstrofs(cmdrestriction, ";", 0); // find first semicolon
- for(;;)
+ for (;;)
{
// we know that at any time, startpos <= argc - minargs
// so this means: argc-minargs >= startpos >= argc, thus
break;
// otherwise fail
- return FALSE;
+ return false;
}
// cut to next semicolon
checkmate = strlen(arg);
for(check = 0; check < checkmate; ++check)
if(strstrofs(charlist, substring(arg, check, 1), 0) < 0)
- return FALSE; // not allowed character
+ return false; // not allowed character
// all characters are allowed. FINE.
}
p = q;
}
- return TRUE;
+ return true;
}
float VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, float argc)
&&
(strlen(substring(vote_command, argv_start_index(startpos), strlen(vote_command) - argv_start_index(startpos))) > autocvar_sv_vote_limit)
)
- return FALSE;
+ return false;
if (!VoteCommand_checkinlist(first_command, vote_list))
- return FALSE;
+ return false;
if (!VoteCommand_checkargs(startpos, argc))
- return FALSE;
+ return false;
switch(first_command) // now go through and parse the proper commands to adjust as needed.
{
case "kickban": // catch all kick/kickban commands
{
entity victim = GetIndexedEntity(argc, (startpos + 1));
- float accepted = VerifyClientEntity(victim, TRUE, FALSE);
+ float accepted = VerifyClientEntity(victim, true, false);
if(accepted > 0)
{
vote_parsed_command = strcat(first_command, " # ", ftos(num_for_edict(victim)), " ", command_arguments);
vote_parsed_display = strcat("^1", vote_command, " (^7", victim.netname, "^1): ", reason);
}
- else { print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n")); return FALSE; }
+ else { print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n")); return false; }
break;
}
case "gotomap": // re-direct all map selection commands to gotomap
{
vote_command = ValidateMap(argv(startpos + 1), caller);
- if (!vote_command) { return FALSE; }
+ if (!vote_command) { return false; }
vote_parsed_command = strcat("gotomap ", vote_command);
vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
}
}
- return TRUE;
+ return true;
}
print_to(caller, "^1You abstained from your vote.");
caller.vote_selection = VOTE_SELECT_ABSTAIN;
msg_entity = caller;
- if(!autocvar_sv_vote_singlecount) { VoteCount(FALSE); }
+ if(!autocvar_sv_vote_singlecount) { VoteCount(false); }
}
return;
else if(!autocvar_sv_vote_gamestart && time < game_starttime) { print_to(caller, "^1Vote calling is not allowed before the match has started."); }
else if(vote_called) { print_to(caller, "^1There is already a vote called."); }
else if(!spectators_allowed && (caller && !IS_PLAYER(caller))) { print_to(caller, "^1Only players can call a vote."); }
+ else if(caller && !IS_CLIENT(caller)) { print_to(caller, "^1Only connected clients can vote."); }
else if(timeout_status) { print_to(caller, "^1You can not call a vote while a timeout is active."); }
else if(caller && (time < caller.vote_waittime)) { print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote.")); }
else if (!VoteCommand_checknasty(vote_command)) { print_to(caller, "^1Syntax error in command, see 'vhelp' for more info."); }
bprint("\{1}^2* ^3", GetCallerName(vote_caller), "^2 calls a vote for ", vote_called_display, "\n");
if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display)); }
Nagger_VoteChanged();
- VoteCount(TRUE); // needed if you are the only one
+ VoteCount(true); // needed if you are the only one
}
return;
else // everything went okay, proceed with giving this player master privilages
{
- caller.vote_master = TRUE;
+ caller.vote_master = true;
print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller)));
bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n");
if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid))); }
bprint("\{1}^2* ^3", GetCallerName(vote_caller), "^2 calls a vote to become ^3master^2.\n");
if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display)); }
Nagger_VoteChanged();
- VoteCount(TRUE); // needed if you are the only one
+ VoteCount(true); // needed if you are the only one
}
return;
print_to(caller, "^1You rejected the vote.");
caller.vote_selection = VOTE_SELECT_REJECT;
msg_entity = caller;
- if(!autocvar_sv_vote_singlecount) { VoteCount(FALSE); }
+ if(!autocvar_sv_vote_singlecount) { VoteCount(false); }
}
return;
print_to(caller, "^1You accepted the vote.");
caller.vote_selection = VOTE_SELECT_ACCEPT;
msg_entity = caller;
- if(!autocvar_sv_vote_singlecount) { VoteCount(FALSE); }
+ if(!autocvar_sv_vote_singlecount) { VoteCount(false); }
}
return;
#define VOTE_COMMAND(name,function,description,assignment) \
{ if(Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat(" ^2", name, "^7: ", description)); } }
- VOTE_COMMANDS(0, caller, 0, "")
+ VOTE_COMMANDS(0, caller, 0, "");
#undef VOTE_COMMAND
print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote COMMAND...^7, where possible commands are listed above.\n"));
#define VOTE_COMMAND(name,function,description,assignment) \
{ if(Votecommand_check_assignment(caller, assignment)) { if(name == strtolower(argv(2))) { function; return; } } }
- VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "")
+ VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "");
#undef VOTE_COMMAND
}
float VoteCommand_macro_command(entity caller, float argc, string vote_command)
{
#define VOTE_COMMAND(name,function,description,assignment) \
- { if(Votecommand_check_assignment(caller, assignment)) { if(name == strtolower(argv(1))) { function; return TRUE; } } }
+ { if(Votecommand_check_assignment(caller, assignment)) { if(name == strtolower(argv(1))) { function; return true; } } }
- VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command)
+ VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command);
#undef VOTE_COMMAND
- return FALSE;
+ return false;
}
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+ #include "../dpdefs/progsdefs.qh"
+ #include "../dpdefs/dpextensions.qh"
+ #include "../common/constants.qh"
+ #include "../common/util.qh"
+ #include "autocvars.qh"
+ #include "defs.qh"
+ #include "command/banning.qh"
+ #include "ipban.qh"
+#endif
+
/*
* Protocol of online ban list:
*
remove(self);
}
-#define BAN_MAX 256
+const float BAN_MAX = 256;
float ban_loaded;
string ban_ip[BAN_MAX];
float ban_expire[BAN_MAX];
float Ban_Delete(float i)
{
if(i < 0)
- return FALSE;
+ return false;
if(i >= ban_count)
- return FALSE;
+ return false;
if(ban_expire[i] == 0)
- return FALSE;
+ return false;
if(ban_expire[i] > 0)
{
OnlineBanList_SendUnban(ban_ip[i]);
ban_expire[i] = 0;
ban_ip[i] = "";
Ban_SaveBans();
- return TRUE;
+ return true;
}
void Ban_LoadBans()
for(i = 0; i < ban_count; ++i)
Ban_Delete(i);
ban_count = 0;
- ban_loaded = TRUE;
+ ban_loaded = true;
n = tokenize_console(autocvar_g_banned_list);
if(stof(argv(0)) == 1)
{
goto ipv6;
i2 = strstrofs(s, ".", i1 + 1);
if(i2 < 0)
- return FALSE;
+ return false;
i3 = strstrofs(s, ".", i2 + 1);
if(i3 < 0)
- return FALSE;
+ return false;
i4 = strstrofs(s, ".", i3 + 1);
if(i4 >= 0)
s = substring(s, 0, i4);
ban_ip2 = substring(s, 0, i2); // 16
ban_ip3 = substring(s, 0, i3); // 24
ban_ip4 = strcat1(s); // 32
- return TRUE;
+ return true;
:ipv6
i1 = strstrofs(s, ":", 0);
if(i1 < 0)
- return FALSE;
+ return false;
i1 = strstrofs(s, ":", i1 + 1);
if(i1 < 0)
- return FALSE;
+ return false;
i2 = strstrofs(s, ":", i1 + 1);
if(i2 < 0)
- return FALSE;
+ return false;
i3 = strstrofs(s, ":", i2 + 1);
if(i3 < 0)
- return FALSE;
+ return false;
ban_ip1 = strcat(substring(s, 0, i1), "::/32"); // 32
ban_ip2 = strcat(substring(s, 0, i2), "::/48"); // 48
else
ban_ip3 = strcat(substring(s, 0, i2), ":0::/56");
- return TRUE;
+ return true;
}
float Ban_IsClientBanned(entity client, float idx)
if(!ban_loaded)
Ban_LoadBans();
if(!Ban_GetClientIP(client))
- return FALSE;
+ return false;
if(idx < 0)
{
b = 0;
b = idx;
e = idx + 1;
}
- ipbanned = FALSE;
+ ipbanned = false;
for(i = b; i < e; ++i)
{
string s;
if(time > ban_expire[i])
continue;
s = ban_ip[i];
- if(ban_ip1 == s) ipbanned = TRUE;
- if(ban_ip2 == s) ipbanned = TRUE;
- if(ban_ip3 == s) ipbanned = TRUE;
- if(ban_ip4 == s) ipbanned = TRUE;
- if(ban_idfp == s) return TRUE;
+ if(ban_ip1 == s) ipbanned = true;
+ if(ban_ip2 == s) ipbanned = true;
+ if(ban_ip3 == s) ipbanned = true;
+ if(ban_ip4 == s) ipbanned = true;
+ if(ban_idfp == s) return true;
}
if(ipbanned)
{
if(!autocvar_g_banned_list_idmode)
- return TRUE;
+ return true;
if (!ban_idfp)
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
float Ban_MaybeEnforceBan(entity client)
s = strcat("^1NOTE:^7 banned client ", client.netaddress, " just tried to enter\n");
dropclient(client);
bprint(s);
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
.float ban_checked;
float Ban_MaybeEnforceBanOnce(entity client)
{
if(client.ban_checked)
- return FALSE;
- client.ban_checked = TRUE;
+ return false;
+ client.ban_checked = true;
- return Ban_MaybeEnforceBan(self);
+ return Ban_MaybeEnforceBan(client);
}
string Ban_Enforce(float i, string reason)
// Enforce our new ban
s = "";
- FOR_EACH_REALCLIENT(e)
+ FOR_EACH_CLIENTSLOT(e)
+ if (IS_REAL_CLIENT(e))
if(Ban_IsClientBanned(e, i))
{
if(reason != "")
if(substring(reason, 0, 1) != "~") // like IRC: unauthenticated banner
OnlineBanList_SendBan(ip, bantime, reason);
- return FALSE;
+ return false;
}
// do we have a free slot?
if(ban_expire[i] > time + bantime)
{
print(ip, " could not get banned due to no free ban slot\n");
- return FALSE;
+ return false;
}
// okay, insert our new victim as i
Ban_Delete(i);
if(substring(reason, 0, 1) != "~") // like IRC: unauthenticated banner
OnlineBanList_SendBan(ip, bantime, reason);
- return TRUE;
+ return true;
}
void Ban_KickBanClient(entity client, float bantime, float masksize, string reason)
--- /dev/null
+#ifndef MISCFUNCTIONS_H
+#define MISCFUNCTIONS_H
+
+#include "t_items.qh"
+
+#include "mutators/base.qh"
+#include "mutators/gamemode_race.qh"
+
+#include "../common/constants.qh"
+#include "../common/mapinfo.qh"
+
+#ifdef RELEASE
+#define cvar_string_normal builtin_cvar_string
+#define cvar_normal builtin_cvar
+#else
+string cvar_string_normal(string n)
+{
+ if (!(cvar_type(n) & 1))
+ backtrace(strcat("Attempt to access undefined cvar: ", n));
+ return builtin_cvar_string(n);
+}
+
+float cvar_normal(string n)
+{
+ return stof(cvar_string_normal(n));
+}
+#endif
+#define cvar_set_normal builtin_cvar_set
+
+.vector dropped_origin;
+.void(void) uncustomizeentityforclient;
+.float uncustomizeentityforclient_set;
+.float nottargeted;
+
+
+float DistributeEvenly_amount;
+float DistributeEvenly_totalweight;
+var void remove(entity e);
+void objerror(string s);
+void droptofloor();
+void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
+void() spawnpoint_use;
+void() SUB_Remove;
+
+void attach_sameorigin(entity e, entity to, string tag);
+
+void crosshair_trace(entity pl);
+
+void crosshair_trace_plusvisibletriggers(entity pl);
+
+void detach_sameorigin(entity e);
+
+void follow_sameorigin(entity e, entity to);
+
+string formatmessage(string msg);
+
+void GameLogEcho(string s);
+
+void GameLogInit();
+
+void GameLogClose();
+
+void GetCvars(float f);
+
+string GetMapname();
+
+float isPushable(entity e);
+
+float LostMovetypeFollow(entity ent);
+
+float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance);
+
+string NearestLocation(vector p);
+
+void play2(entity e, string filename);
+
+string playername(entity p);
+
+void precache();
+
+void remove_safely(entity e);
+
+void remove_unsafely(entity e);
+
+void SetMovetypeFollow(entity ent, entity e);
+
+vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn);
+
+void soundto(float dest, entity e, float chan, string samp, float vol, float atten);
+
+void stopsound(entity e, float chan);
+
+float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma);
+
+void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
+
+void WarpZone_crosshair_trace(entity pl);
+
+void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
+
+
+#define IFTARGETED if(!self.nottargeted && self.targetname != "")
+
+#define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
+#define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
+
+#define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
+
+const string STR_PLAYER = "player";
+const string STR_SPECTATOR = "spectator";
+const string STR_OBSERVER = "observer";
+
+#define IS_PLAYER(v) (v.classname == STR_PLAYER)
+#define IS_SPEC(v) (v.classname == STR_SPECTATOR)
+#define IS_OBSERVER(v) (v.classname == STR_OBSERVER)
+#define IS_CLIENT(v) (v.flags & FL_CLIENT)
+#define IS_BOT_CLIENT(v) (clienttype(v) == CLIENTTYPE_BOT)
+#define IS_REAL_CLIENT(v) (clienttype(v) == CLIENTTYPE_REAL)
+#define IS_NOT_A_CLIENT(v) (clienttype(v) == CLIENTTYPE_NOTACLIENT)
+
+#define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
+#define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(IS_CLIENT(v))
+#define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(IS_REAL_CLIENT(v))
+
+#define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(IS_PLAYER(v))
+#define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if (!IS_PLAYER(v)) // Samual: shouldn't this be IS_SPEC(v)? and rather create a separate macro to include observers too
+#define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(IS_PLAYER(v))
+
+#define FOR_EACH_MONSTER(v) for(v = world; (v = findflags(v, flags, FL_MONSTER)) != world; )
+
+#define CENTER_OR_VIEWOFS(ent) (ent.origin + (IS_PLAYER(ent) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5)))
+
+// copies a string to a tempstring (so one can strunzone it)
+string strcat1(string s) = #115; // FRIK_FILE
+
+float logfile_open;
+float logfile;
+
+#define strstr strstrofs
+/*
+// NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
+// IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
+// STRINGS AND TAKE QUITE LONG. haystack and needle MUST
+// BE CONSTANT OR strzoneD!
+float strstr(string haystack, string needle, float offset)
+{
+ float len, endpos;
+ string found;
+ len = strlen(needle);
+ endpos = strlen(haystack) - len;
+ while(offset <= endpos)
+ {
+ found = substring(haystack, offset, len);
+ if(found == needle)
+ return offset;
+ offset = offset + 1;
+ }
+ return -1;
+}
+*/
+
+const float NUM_NEAREST_ENTITIES = 4;
+entity nearest_entity[NUM_NEAREST_ENTITIES];
+float nearest_length[NUM_NEAREST_ENTITIES];
+
+
+//#NO AUTOCVARS START
+
+float g_pickup_shells;
+float g_pickup_shells_max;
+float g_pickup_nails;
+float g_pickup_nails_max;
+float g_pickup_rockets;
+float g_pickup_rockets_max;
+float g_pickup_cells;
+float g_pickup_cells_max;
+float g_pickup_plasma;
+float g_pickup_plasma_max;
+float g_pickup_fuel;
+float g_pickup_fuel_jetpack;
+float g_pickup_fuel_max;
+float g_pickup_armorsmall;
+float g_pickup_armorsmall_max;
+float g_pickup_armorsmall_anyway;
+float g_pickup_armormedium;
+float g_pickup_armormedium_max;
+float g_pickup_armormedium_anyway;
+float g_pickup_armorbig;
+float g_pickup_armorbig_max;
+float g_pickup_armorbig_anyway;
+float g_pickup_armorlarge;
+float g_pickup_armorlarge_max;
+float g_pickup_armorlarge_anyway;
+float g_pickup_healthsmall;
+float g_pickup_healthsmall_max;
+float g_pickup_healthsmall_anyway;
+float g_pickup_healthmedium;
+float g_pickup_healthmedium_max;
+float g_pickup_healthmedium_anyway;
+float g_pickup_healthlarge;
+float g_pickup_healthlarge_max;
+float g_pickup_healthlarge_anyway;
+float g_pickup_healthmega;
+float g_pickup_healthmega_max;
+float g_pickup_healthmega_anyway;
+float g_pickup_ammo_anyway;
+float g_pickup_weapons_anyway;
+float g_weaponarena;
+WepSet g_weaponarena_weapons;
+float g_weaponarena_random;
+float g_weaponarena_random_with_blaster;
+string g_weaponarena_list;
+float g_weaponspeedfactor;
+float g_weaponratefactor;
+float g_weapondamagefactor;
+float g_weaponforcefactor;
+float g_weaponspreadfactor;
+
+WepSet start_weapons;
+WepSet start_weapons_default;
+WepSet start_weapons_defaultmask;
+int start_items;
+float start_ammo_shells;
+float start_ammo_nails;
+float start_ammo_rockets;
+float start_ammo_cells;
+float start_ammo_plasma;
+float start_ammo_fuel;
+float start_health;
+float start_armorvalue;
+WepSet warmup_start_weapons;
+WepSet warmup_start_weapons_default;
+WepSet warmup_start_weapons_defaultmask;
+#define WARMUP_START_WEAPONS ((g_warmup_allguns == 1) ? (warmup_start_weapons & (weaponsInMap | start_weapons)) : warmup_start_weapons)
+float warmup_start_ammo_shells;
+float warmup_start_ammo_nails;
+float warmup_start_ammo_rockets;
+float warmup_start_ammo_cells;
+float warmup_start_ammo_plasma;
+float warmup_start_ammo_fuel;
+float warmup_start_health;
+float warmup_start_armorvalue;
+float g_weapon_stay;
+
+float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
+{
+ int i = weaponinfo.weapon;
+ int d = 0;
+
+ if (!i)
+ return 0;
+
+ if (g_lms || g_ca || allguns)
+ {
+ if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
+ d = true;
+ else
+ d = false;
+ }
+ else if (g_cts)
+ d = (i == WEP_SHOTGUN);
+ else if (g_nexball)
+ d = 0; // weapon is set a few lines later
+ else
+ d = !(!weaponinfo.weaponstart);
+
+ if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
+ d |= (i == WEP_HOOK);
+ if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
+ d = 0;
+
+ float t = weaponinfo.weaponstartoverride;
+
+ //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
+
+ // bit order in t:
+ // 1: want or not
+ // 2: is default?
+ // 4: is set by default?
+ if(t < 0)
+ t = 4 | (3 * d);
+ else
+ t |= (2 * d);
+
+ return t;
+}
+
+void readplayerstartcvars()
+{
+ entity e;
+ float i, j, t;
+ string s;
+
+ // initialize starting values for players
+ start_weapons = '0 0 0';
+ start_weapons_default = '0 0 0';
+ start_weapons_defaultmask = '0 0 0';
+ start_items = 0;
+ start_ammo_shells = 0;
+ start_ammo_nails = 0;
+ start_ammo_rockets = 0;
+ start_ammo_cells = 0;
+ start_ammo_plasma = 0;
+ start_health = cvar("g_balance_health_start");
+ start_armorvalue = cvar("g_balance_armor_start");
+
+ g_weaponarena = 0;
+ g_weaponarena_weapons = '0 0 0';
+
+ s = cvar_string("g_weaponarena");
+ if (s == "0" || s == "")
+ {
+ if(g_ca)
+ s = "most";
+ }
+
+ if (s == "0" || s == "")
+ {
+ // no arena
+ }
+ else if (s == "off")
+ {
+ // forcibly turn off weaponarena
+ }
+ else if (s == "all" || s == "1")
+ {
+ g_weaponarena = 1;
+ g_weaponarena_list = "All Weapons";
+ for (j = WEP_FIRST; j <= WEP_LAST; ++j)
+ {
+ e = get_weaponinfo(j);
+ if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
+ g_weaponarena_weapons |= WepSet_FromWeapon(j);
+ }
+ }
+ else if (s == "most")
+ {
+ g_weaponarena = 1;
+ g_weaponarena_list = "Most Weapons";
+ for (j = WEP_FIRST; j <= WEP_LAST; ++j)
+ {
+ e = get_weaponinfo(j);
+ if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
+ if (e.spawnflags & WEP_FLAG_NORMAL)
+ g_weaponarena_weapons |= WepSet_FromWeapon(j);
+ }
+ }
+ else if (s == "none")
+ {
+ g_weaponarena = 1;
+ g_weaponarena_list = "No Weapons";
+ }
+ else
+ {
+ g_weaponarena = 1;
+ t = tokenize_console(s);
+ g_weaponarena_list = "";
+ for (i = 0; i < t; ++i)
+ {
+ s = argv(i);
+ for (j = WEP_FIRST; j <= WEP_LAST; ++j)
+ {
+ e = get_weaponinfo(j);
+ if (e.netname == s)
+ {
+ g_weaponarena_weapons |= WepSet_FromWeapon(j);
+ g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
+ break;
+ }
+ }
+ if (j > WEP_LAST)
+ {
+ print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
+ }
+ }
+ g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
+ }
+
+ if(g_weaponarena)
+ g_weaponarena_random = cvar("g_weaponarena_random");
+ else
+ g_weaponarena_random = 0;
+ g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
+
+ if (g_weaponarena)
+ {
+ g_weapon_stay = 0; // incompatible
+ start_weapons = g_weaponarena_weapons;
+ start_items |= IT_UNLIMITED_AMMO;
+ }
+ else
+ {
+ for (i = WEP_FIRST; i <= WEP_LAST; ++i)
+ {
+ e = get_weaponinfo(i);
+ int w = want_weapon(e, false);
+ if(w & 1)
+ start_weapons |= WepSet_FromWeapon(i);
+ if(w & 2)
+ start_weapons_default |= WepSet_FromWeapon(i);
+ if(w & 4)
+ start_weapons_defaultmask |= WepSet_FromWeapon(i);
+ }
+ }
+
+ if(!cvar("g_use_ammunition"))
+ start_items |= IT_UNLIMITED_AMMO;
+
+ if(start_items & IT_UNLIMITED_WEAPON_AMMO)
+ {
+ start_ammo_shells = 999;
+ start_ammo_nails = 999;
+ start_ammo_rockets = 999;
+ start_ammo_cells = 999;
+ start_ammo_plasma = 999;
+ start_ammo_fuel = 999;
+ }
+ else
+ {
+ start_ammo_shells = cvar("g_start_ammo_shells");
+ start_ammo_nails = cvar("g_start_ammo_nails");
+ start_ammo_rockets = cvar("g_start_ammo_rockets");
+ start_ammo_cells = cvar("g_start_ammo_cells");
+ start_ammo_plasma = cvar("g_start_ammo_plasma");
+ start_ammo_fuel = cvar("g_start_ammo_fuel");
+ }
+
+ if (warmup_stage)
+ {
+ warmup_start_ammo_shells = start_ammo_shells;
+ warmup_start_ammo_nails = start_ammo_nails;
+ warmup_start_ammo_rockets = start_ammo_rockets;
+ warmup_start_ammo_cells = start_ammo_cells;
+ warmup_start_ammo_plasma = start_ammo_plasma;
+ warmup_start_ammo_fuel = start_ammo_fuel;
+ warmup_start_health = start_health;
+ warmup_start_armorvalue = start_armorvalue;
+ warmup_start_weapons = start_weapons;
+ warmup_start_weapons_default = start_weapons_default;
+ warmup_start_weapons_defaultmask = start_weapons_defaultmask;
+
+ if (!g_weaponarena && !g_ca)
+ {
+ warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
+ warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
+ warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
+ warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
+ warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
+ warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
+ warmup_start_health = cvar("g_warmup_start_health");
+ warmup_start_armorvalue = cvar("g_warmup_start_armor");
+ warmup_start_weapons = '0 0 0';
+ warmup_start_weapons_default = '0 0 0';
+ warmup_start_weapons_defaultmask = '0 0 0';
+ for (i = WEP_FIRST; i <= WEP_LAST; ++i)
+ {
+ e = get_weaponinfo(i);
+ int w = want_weapon(e, g_warmup_allguns);
+ if(w & 1)
+ warmup_start_weapons |= WepSet_FromWeapon(i);
+ if(w & 2)
+ warmup_start_weapons_default |= WepSet_FromWeapon(i);
+ if(w & 4)
+ warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
+ }
+ }
+ }
+
+ if (g_jetpack)
+ start_items |= IT_JETPACK;
+
+ MUTATOR_CALLHOOK(SetStartItems);
+
+ if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
+ {
+ start_items |= IT_FUEL_REGEN;
+ start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
+ warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
+ }
+
+ WepSet precache_weapons = start_weapons;
+ if (g_warmup_allguns != 1)
+ precache_weapons |= warmup_start_weapons;
+ for (i = WEP_FIRST; i <= WEP_LAST; ++i)
+ {
+ e = get_weaponinfo(i);
+ if(precache_weapons & WepSet_FromWeapon(i))
+ WEP_ACTION(i, WR_INIT);
+ }
+
+ start_ammo_shells = max(0, start_ammo_shells);
+ start_ammo_nails = max(0, start_ammo_nails);
+ start_ammo_rockets = max(0, start_ammo_rockets);
+ start_ammo_cells = max(0, start_ammo_cells);
+ start_ammo_plasma = max(0, start_ammo_plasma);
+ start_ammo_fuel = max(0, start_ammo_fuel);
+
+ warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
+ warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
+ warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
+ warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
+ warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
+ warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
+}
+
+float g_bugrigs;
+float g_bugrigs_planar_movement;
+float g_bugrigs_planar_movement_car_jumping;
+float g_bugrigs_reverse_spinning;
+float g_bugrigs_reverse_speeding;
+float g_bugrigs_reverse_stopping;
+float g_bugrigs_air_steering;
+float g_bugrigs_angle_smoothing;
+float g_bugrigs_friction_floor;
+float g_bugrigs_friction_brake;
+float g_bugrigs_friction_air;
+float g_bugrigs_accel;
+float g_bugrigs_speed_ref;
+float g_bugrigs_speed_pow;
+float g_bugrigs_steer;
+
+float sv_autotaunt;
+float sv_taunt;
+
+string GetGametype(); // g_world.qc
+void mutators_add(); // mutators.qc
+void readlevelcvars(void)
+{
+ // load mutators
+ mutators_add();
+
+ if(cvar("sv_allow_fullbright"))
+ serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
+
+ g_bugrigs = cvar("g_bugrigs");
+ g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
+ g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
+ g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
+ g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
+ g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
+ g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
+ g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
+ g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
+ g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
+ g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
+ g_bugrigs_accel = cvar("g_bugrigs_accel");
+ g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
+ g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
+ g_bugrigs_steer = cvar("g_bugrigs_steer");
+
+ g_instagib = cvar("g_instagib");
+
+ sv_clones = cvar("sv_clones");
+ sv_foginterval = cvar("sv_foginterval");
+ g_cloaked = cvar("g_cloaked");
+ g_footsteps = cvar("g_footsteps");
+ g_grappling_hook = cvar("g_grappling_hook");
+ g_jetpack = cvar("g_jetpack");
+ sv_maxidle = cvar("sv_maxidle");
+ sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
+ sv_autotaunt = cvar("sv_autotaunt");
+ sv_taunt = cvar("sv_taunt");
+
+ warmup_stage = cvar("g_warmup");
+ g_warmup_limit = cvar("g_warmup_limit");
+ g_warmup_allguns = cvar("g_warmup_allguns");
+ g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
+
+ if ((g_race && g_race_qualifying == 2) || g_assault || cvar("g_campaign"))
+ warmup_stage = 0; // these modes cannot work together, sorry
+
+ g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
+ g_pickup_respawntime_superweapon = cvar("g_pickup_respawntime_superweapon");
+ g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
+ g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
+ g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
+ g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
+ g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
+ g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
+ g_pickup_respawntimejitter_superweapon = cvar("g_pickup_respawntimejitter_superweapon");
+ g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
+ g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
+ g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
+ g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
+ g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
+
+ g_weaponspeedfactor = cvar("g_weaponspeedfactor");
+ g_weaponratefactor = cvar("g_weaponratefactor");
+ g_weapondamagefactor = cvar("g_weapondamagefactor");
+ g_weaponforcefactor = cvar("g_weaponforcefactor");
+ g_weaponspreadfactor = cvar("g_weaponspreadfactor");
+
+ g_pickup_shells = cvar("g_pickup_shells");
+ g_pickup_shells_max = cvar("g_pickup_shells_max");
+ g_pickup_nails = cvar("g_pickup_nails");
+ g_pickup_nails_max = cvar("g_pickup_nails_max");
+ g_pickup_rockets = cvar("g_pickup_rockets");
+ g_pickup_rockets_max = cvar("g_pickup_rockets_max");
+ g_pickup_cells = cvar("g_pickup_cells");
+ g_pickup_cells_max = cvar("g_pickup_cells_max");
+ g_pickup_plasma = cvar("g_pickup_plasma");
+ g_pickup_plasma_max = cvar("g_pickup_plasma_max");
+ g_pickup_fuel = cvar("g_pickup_fuel");
+ g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
+ g_pickup_fuel_max = cvar("g_pickup_fuel_max");
+ g_pickup_armorsmall = cvar("g_pickup_armorsmall");
+ g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
+ g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
+ g_pickup_armormedium = cvar("g_pickup_armormedium");
+ g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
+ g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
+ g_pickup_armorbig = cvar("g_pickup_armorbig");
+ g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
+ g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
+ g_pickup_armorlarge = cvar("g_pickup_armorlarge");
+ g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
+ g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
+ g_pickup_healthsmall = cvar("g_pickup_healthsmall");
+ g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
+ g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
+ g_pickup_healthmedium = cvar("g_pickup_healthmedium");
+ g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
+ g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
+ g_pickup_healthlarge = cvar("g_pickup_healthlarge");
+ g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
+ g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
+ g_pickup_healthmega = cvar("g_pickup_healthmega");
+ g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
+ g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
+
+ g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
+ g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
+
+ g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
+ if(!g_weapon_stay)
+ g_weapon_stay = cvar("g_weapon_stay");
+
+ if (!warmup_stage)
+ game_starttime = time + cvar("g_start_delay");
+
++ for(int i = WEP_FIRST; i <= WEP_LAST; ++i)
++ WEP_ACTION(i, WR_INIT);
++
+ readplayerstartcvars();
+}
+
+//#NO AUTOCVARS END
+
+
+// Sound functions
+//string precache_sound (string s) = #19;
+// hack
+float precache_sound_index (string s) = #19;
+
+const float SND_VOLUME = 1;
+const float SND_ATTENUATION = 2;
+const float SND_LARGEENTITY = 8;
+const float SND_LARGESOUND = 16;
+
+// WARNING: this kills the trace globals
+#define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
+#define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
+
+const float INITPRIO_FIRST = 0;
+const float INITPRIO_GAMETYPE = 0;
+const float INITPRIO_GAMETYPE_FALLBACK = 1;
+const float INITPRIO_FINDTARGET = 10;
+const float INITPRIO_DROPTOFLOOR = 20;
+const float INITPRIO_SETLOCATION = 90;
+const float INITPRIO_LINKDOORS = 91;
+const float INITPRIO_LAST = 99;
+
+.void(void) initialize_entity;
+.float initialize_entity_order;
+.entity initialize_entity_next;
+entity initialize_entity_first;
+
+
+
+
+
+float sound_allowed(float dest, entity e);
+void InitializeEntity(entity e, void(void) func, float order);
+void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer);
+void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc);
+
+#endif
+#if defined(CSQC)
+ #include "../dpdefs/csprogsdefs.qh"
+ #include "../common/util.qh"
+ #include "../common/buffs.qh"
+ #include "../common/weapons/weapons.qh"
+ #include "../client/autocvars.qh"
+ #include "../client/movetypes.qh"
+ #include "../client/main.qh"
+ #include "../csqcmodellib/common.qh"
+ #include "../csqcmodellib/cl_model.qh"
+ #include "t_items.qh"
+#elif defined(MENUQC)
+#elif defined(SVQC)
+ #include "../dpdefs/progsdefs.qh"
+ #include "../dpdefs/dpextensions.qh"
+ #include "../warpzonelib/util_server.qh"
+ #include "../common/constants.qh"
+ #include "../common/util.qh"
+ #include "../common/monsters/monsters.qh"
+ #include "../common/weapons/weapons.qh"
+ #include "weapons/weaponsystem.qh"
+ #include "t_items.qh"
+ #include "autocvars.qh"
+ #include "constants.qh"
+ #include "defs.qh"
+ #include "../common/notifications.qh"
+ #include "../common/deathtypes.qh"
+ #include "mutators/mutators_include.qh"
+#endif
+
#ifdef CSQC
void ItemDraw()
{
void ItemRead(float _IsNew)
{
- float sf = ReadByte();
+ int sf = ReadByte();
if(sf & ISF_LOCATION)
{
//WriteByte(MSG_ENTITY, self.cnt);
if(sf & ISF_LOCATION)
{
- WriteCoord(MSG_ENTITY, self.origin_x);
- WriteCoord(MSG_ENTITY, self.origin_y);
- WriteCoord(MSG_ENTITY, self.origin_z);
+ WriteCoord(MSG_ENTITY, self.origin.x);
+ WriteCoord(MSG_ENTITY, self.origin.y);
+ WriteCoord(MSG_ENTITY, self.origin.z);
}
if(sf & ISF_ANGLES)
{
- WriteCoord(MSG_ENTITY, self.angles_x);
- WriteCoord(MSG_ENTITY, self.angles_y);
- WriteCoord(MSG_ENTITY, self.angles_z);
+ WriteCoord(MSG_ENTITY, self.angles.x);
+ WriteCoord(MSG_ENTITY, self.angles.y);
+ WriteCoord(MSG_ENTITY, self.angles.z);
}
if(sf & ISF_SIZE)
{
- WriteCoord(MSG_ENTITY, self.mins_x);
- WriteCoord(MSG_ENTITY, self.mins_y);
- WriteCoord(MSG_ENTITY, self.mins_z);
- WriteCoord(MSG_ENTITY, self.maxs_x);
- WriteCoord(MSG_ENTITY, self.maxs_y);
- WriteCoord(MSG_ENTITY, self.maxs_z);
+ WriteCoord(MSG_ENTITY, self.mins.x);
+ WriteCoord(MSG_ENTITY, self.mins.y);
+ WriteCoord(MSG_ENTITY, self.mins.z);
+ WriteCoord(MSG_ENTITY, self.maxs.x);
+ WriteCoord(MSG_ENTITY, self.maxs.y);
+ WriteCoord(MSG_ENTITY, self.maxs.z);
}
if(sf & ISF_STATUS)
if(sf & ISF_DROP)
{
- WriteCoord(MSG_ENTITY, self.velocity_x);
- WriteCoord(MSG_ENTITY, self.velocity_y);
- WriteCoord(MSG_ENTITY, self.velocity_z);
+ WriteCoord(MSG_ENTITY, self.velocity.x);
+ WriteCoord(MSG_ENTITY, self.velocity.y);
+ WriteCoord(MSG_ENTITY, self.velocity.z);
}
- return TRUE;
+ return true;
}
void ItemUpdate(entity item)
if(self.flags & FL_POWERUP)
{
if(autocvar_g_powerups > 0)
- return TRUE;
+ return true;
if(autocvar_g_powerups == 0)
- return FALSE;
+ return false;
}
else
{
if(autocvar_g_pickup_items > 0)
- return TRUE;
+ return true;
if(autocvar_g_pickup_items == 0)
- return FALSE;
+ return false;
if(g_weaponarena)
if(self.weapons || (self.items & IT_AMMO)) // no item or ammo pickups in weaponarena
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
/*
float Item_Customize()
{
if(self.spawnshieldtime)
- return TRUE;
+ return true;
if(self.weapons & ~other.weapons)
{
self.colormod = '0 0 0';
self.glowmod = self.colormod;
self.alpha = 0.5 + 0.5 * g_ghost_items; // halfway more alpha
- return TRUE;
+ return true;
}
else
{
self.colormod = stov(autocvar_g_ghost_items_color);
self.glowmod = self.colormod;
self.alpha = g_ghost_items;
- return TRUE;
+ return true;
}
else
- return FALSE;
+ return false;
}
}
*/
self.think = Item_Think;
self.nextthink = time;
-
+
//pointparticles(particleeffectnum("item_respawn"), self.origin + self.mins_z * '0 0 1' + '0 0 48', '0 0 0', 1);
pointparticles(particleeffectnum("item_respawn"), self.origin + 0.5 * (self.mins + self.maxs), '0 0 0', 1);
}
}
if(name)
{
- WaypointSprite_Spawn(name, 0, 0, self, '0 0 64', world, 0, self, waypointsprite_attached, TRUE, RADARICON_POWERUP, rgb);
+ WaypointSprite_Spawn(name, 0, 0, self, '0 0 64', world, 0, self, waypointsprite_attached, true, RADARICON_POWERUP, rgb);
if(self.waypointsprite_attached)
WaypointSprite_UpdateBuildFinished(self.waypointsprite_attached, time + ITEM_RESPAWN_TICKS);
}
float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax, float mode)
{
if (!item.ammotype)
- return FALSE;
+ return false;
if (item.spawnshieldtime)
{
}
}
- return FALSE;
+ return false;
:YEAH
switch(mode)
default:
break;
}
- return TRUE;
+ return true;
}
float Item_GiveTo(entity item, entity player)
float i;
// if nothing happens to player, just return without taking the item
- pickedup = FALSE;
- _switchweapon = FALSE;
+ pickedup = false;
+ _switchweapon = false;
// in case the player has autoswitch enabled do the following:
// if the player is using their best weapon before items are given, they
// probably want to switch to an even better weapon after items are given
if (player.autoswitch)
if (player.switchweapon == w_getbestweapon(player))
- _switchweapon = TRUE;
+ _switchweapon = true;
if (!(player.weapons & WepSet_FromWeapon(player.switchweapon)))
- _switchweapon = TRUE;
+ _switchweapon = true;
pickedup |= Item_GiveAmmoTo(item, player, ammo_fuel, g_pickup_fuel_max, ITEM_MODE_FUEL);
pickedup |= Item_GiveAmmoTo(item, player, ammo_shells, g_pickup_shells_max, ITEM_MODE_NONE);
if (it || (item.spawnshieldtime && item.pickup_anyway))
{
- pickedup = TRUE;
+ pickedup = true;
for(i = WEP_FIRST; i <= WEP_LAST; ++i)
if(it & WepSet_FromWeapon(i))
{
if((it = (item.items - (item.items & player.items)) & IT_PICKUPMASK))
{
- pickedup = TRUE;
+ pickedup = true;
player.items |= it;
Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_ITEM_WEAPON_GOT, item.netname);
}
if (item.strength_finished)
{
- pickedup = TRUE;
+ pickedup = true;
player.strength_finished = max(player.strength_finished, time) + item.strength_finished;
}
if (item.invincible_finished)
{
- pickedup = TRUE;
+ pickedup = true;
player.invincible_finished = max(player.invincible_finished, time) + item.invincible_finished;
}
if (item.superweapons_finished)
{
- pickedup = TRUE;
+ pickedup = true;
player.superweapons_finished = max(player.superweapons_finished, time) + item.superweapons_finished;
}
// always eat teamed entities
if(item.team)
- pickedup = TRUE;
+ pickedup = true;
if (!pickedup)
return 0;
float commodity_pickupevalfunc(entity player, entity item)
{
float c, i;
- float need_shells = FALSE, need_nails = FALSE, need_rockets = FALSE, need_cells = FALSE, need_plasma = FALSE, need_fuel = FALSE;
+ float need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false;
entity wi;
c = 0;
continue;
if(wi.items & IT_SHELLS)
- need_shells = TRUE;
+ need_shells = true;
else if(wi.items & IT_NAILS)
- need_nails = TRUE;
+ need_nails = true;
else if(wi.items & IT_ROCKETS)
- need_rockets = TRUE;
+ need_rockets = true;
else if(wi.items & IT_CELLS)
- need_cells = TRUE;
+ need_cells = true;
else if(wi.items & IT_PLASMA)
- need_plasma = TRUE;
+ need_plasma = true;
else if(wi.items & IT_FUEL)
- need_fuel = TRUE;
+ need_fuel = true;
}
// TODO: figure out if the player even has the weapon this ammo is for?
void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue)
{
- startitem_failed = FALSE;
+ startitem_failed = false;
if(self.model == "")
self.model = itemmodel;
if(MUTATOR_CALLHOOK(FilterItem)) // error means we do not want the item
{
- startitem_failed = TRUE;
+ startitem_failed = true;
remove(self);
return;
}
traceline(self.origin, self.origin, MOVE_NORMAL, self);
if (trace_dpstartcontents & DPCONTENTS_NODROP)
{
- startitem_failed = TRUE;
+ startitem_failed = true;
remove(self);
return;
}
{
if(!have_pickup_item())
{
- startitem_failed = TRUE;
+ startitem_failed = true;
remove (self);
return;
}
else
setsize (self, '-16 -16 0', '16 16 32');
- // note droptofloor returns FALSE if stuck/or would fall too far
+ // note droptofloor returns false if stuck/or would fall too far
droptofloor();
waypoint_spawnforitem(self);
}
{
// target_give not yet supported; maybe later
print("removed targeted ", self.classname, "\n");
- startitem_failed = TRUE;
+ startitem_failed = true;
remove (self);
return;
}
error("Mapper sucks.");
}
}
- self.is_item = TRUE;
+ self.is_item = true;
}
weaponsInMap |= WepSet_FromWeapon(weaponid);
self.target = "###item###"; // for finding the nearest item using find()
}
- self.bot_pickup = TRUE;
+ self.bot_pickup = true;
self.bot_pickupevalfunc = pickupevalfunc;
self.bot_pickupbasevalue = pickupbasevalue;
self.mdl = self.model;
else
Item_Reset();
- Net_LinkEntity(self, FALSE, 0, ItemSend);
-
+ Net_LinkEntity(self, false, 0, ItemSend);
+
self.SendFlags |= ISF_SIZE;
if(self.angles)
self.SendFlags |= ISF_ANGLES;
// call this hook after everything else has been done
if(MUTATOR_CALLHOOK(Item_Spawn))
{
- startitem_failed = TRUE;
+ startitem_failed = true;
remove(self);
return;
}
if(autocvar_sv_q3acompat_machineshotgunswap)
if(self.classname != "droppedweapon")
{
- weaponswapping = TRUE;
+ weaponswapping = true;
spawnfunc_item_shells();
- weaponswapping = FALSE;
+ weaponswapping = false;
return;
}
if(autocvar_sv_q3acompat_machineshotgunswap)
if(self.classname != "droppedweapon")
{
- weaponswapping = TRUE;
+ weaponswapping = true;
spawnfunc_item_bullets();
- weaponswapping = FALSE;
+ weaponswapping = false;
return;
}
{
float n, i, j;
entity e;
+ string s;
self.use = target_items_use;
if(!self.strength_finished)
for(j = WEP_FIRST; j <= WEP_LAST; ++j)
{
e = get_weaponinfo(j);
- if(argv(i) == e.netname)
+ s = W_UndeprecateName(argv(i));
+ if(s == e.netname)
{
self.weapons |= WepSet_FromWeapon(j);
if(self.spawnflags == 0 || self.spawnflags == 2)
else
{
error("invalid spawnflags");
-#ifdef GMQCC
- itemprefix = string_null;
- valueprefix = string_null;
-#endif
+ itemprefix = valueprefix = string_null;
}
self.netname = "";
got = 0;
- _switchweapon = FALSE;
+ _switchweapon = false;
if (e.autoswitch)
if (e.switchweapon == w_getbestweapon(e))
- _switchweapon = TRUE;
+ _switchweapon = true;
e.strength_finished = max(0, e.strength_finished - time);
e.invincible_finished = max(0, e.invincible_finished - time);
e.superweapons_finished += time;
if (!(e.weapons & WepSet_FromWeapon(e.switchweapon)))
- _switchweapon = TRUE;
+ _switchweapon = true;
if(_switchweapon)
W_SwitchWeapon_Force(e, w_getbestweapon(e));