From: TimePath <andrew.hardaker1995@gmail.com>
Date: Sun, 10 May 2015 03:56:21 +0000 (+1000)
Subject: Merge remote-tracking branch 'origin/master' into terencehill/listbox_item_highlight
X-Git-Tag: xonotic-v0.8.1~55^2~1
X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=e090603a32c8cba598f2c54e355cdb5b32b0d986;p=xonotic%2Fxonotic-data.pk3dir.git

Merge remote-tracking branch 'origin/master' into terencehill/listbox_item_highlight
---

e090603a32c8cba598f2c54e355cdb5b32b0d986
diff --cc qcsrc/menu/classes.inc
index 000000000,68b90d586..5b0e417f9
mode 000000,100644..100644
--- a/qcsrc/menu/classes.inc
+++ b/qcsrc/menu/classes.inc
@@@ -1,0 -1,132 +1,134 @@@
+ #include "anim/animation.qc"
+ #include "anim/animhost.qc"
+ #include "anim/easing.qc"
+ #include "anim/keyframe.qc"
+ 
+ #include "item.qc"
+ #include "item/borderimage.qc"
+ #include "item/button.qc"
+ #include "item/checkbox.qc"
+ #include "item/container.qc"
+ #include "item/dialog.qc"
+ #include "item/image.qc"
+ #include "item/inputbox.qc"
+ #include "item/inputcontainer.qc"
+ #include "item/label.qc"
+ #include "item/listbox.qc"
+ #include "item/modalcontroller.qc"
+ #include "item/nexposee.qc"
+ #include "item/radiobutton.qc"
+ #include "item/slider.qc"
+ #include "item/tab.qc"
+ #include "item/textslider.qc"
+ 
+ #include "xonotic/bigbutton.qc"
+ #include "xonotic/bigcommandbutton.qc"
+ #include "xonotic/button.qc"
+ #include "xonotic/campaign.qc"
+ #include "xonotic/charmap.qc"
+ #include "xonotic/checkbox.qc"
+ #include "xonotic/checkbox_slider_invalid.qc"
+ #include "xonotic/checkbox_string.qc"
+ #include "xonotic/colorbutton.qc"
+ #include "xonotic/colorpicker.qc"
+ #include "xonotic/colorpicker_string.qc"
+ #include "xonotic/commandbutton.qc"
+ #include "xonotic/credits.qc"
 -#include "xonotic/crosshairbutton.qc"
++#include "xonotic/crosshairpicker.qc"
++#include "xonotic/crosshairpreview.qc"
+ #include "xonotic/cvarlist.qc"
+ #include "xonotic/demolist.qc"
+ #include "xonotic/dialog.qc"
+ #include "xonotic/dialog_credits.qc"
+ #include "xonotic/dialog_firstrun.qc"
+ #include "xonotic/dialog_hudpanel_ammo.qc"
+ #include "xonotic/dialog_hudpanel_buffs.qc"
+ #include "xonotic/dialog_hudpanel_centerprint.qc"
+ #include "xonotic/dialog_hudpanel_chat.qc"
+ #include "xonotic/dialog_hudpanel_engineinfo.qc"
+ #include "xonotic/dialog_hudpanel_healtharmor.qc"
+ #include "xonotic/dialog_hudpanel_infomessages.qc"
+ #include "xonotic/dialog_hudpanel_modicons.qc"
+ #include "xonotic/dialog_hudpanel_notification.qc"
+ #include "xonotic/dialog_hudpanel_physics.qc"
+ #include "xonotic/dialog_hudpanel_powerups.qc"
+ #include "xonotic/dialog_hudpanel_pressedkeys.qc"
+ #include "xonotic/dialog_hudpanel_racetimer.qc"
+ #include "xonotic/dialog_hudpanel_radar.qc"
+ #include "xonotic/dialog_hudpanel_score.qc"
+ #include "xonotic/dialog_hudpanel_timer.qc"
+ #include "xonotic/dialog_hudpanel_vote.qc"
+ #include "xonotic/dialog_hudpanel_weapons.qc"
+ #include "xonotic/dialog_hudsetup_exit.qc"
+ #include "xonotic/dialog_monstertools.qc"
+ #include "xonotic/dialog_multiplayer.qc"
+ #include "xonotic/dialog_multiplayer_create.qc"
+ #include "xonotic/dialog_multiplayer_create_mapinfo.qc"
+ #include "xonotic/dialog_multiplayer_create_mutators.qc"
+ #include "xonotic/dialog_multiplayer_join.qc"
+ #include "xonotic/dialog_multiplayer_join_serverinfo.qc"
+ #include "xonotic/dialog_multiplayer_media.qc"
+ #include "xonotic/dialog_multiplayer_media_demo.qc"
+ #include "xonotic/dialog_multiplayer_media_demo_startconfirm.qc"
+ #include "xonotic/dialog_multiplayer_media_demo_timeconfirm.qc"
+ #include "xonotic/dialog_multiplayer_media_musicplayer.qc"
+ #include "xonotic/dialog_multiplayer_media_screenshot.qc"
+ #include "xonotic/dialog_multiplayer_media_screenshot_viewer.qc"
+ #include "xonotic/dialog_multiplayer_profile.qc"
+ #include "xonotic/dialog_quit.qc"
+ #include "xonotic/dialog_sandboxtools.qc"
+ #include "xonotic/dialog_settings.qc"
+ #include "xonotic/dialog_settings_audio.qc"
+ #include "xonotic/dialog_settings_effects.qc"
+ #include "xonotic/dialog_settings_game.qc"
+ #include "xonotic/dialog_settings_game_crosshair.qc"
+ #include "xonotic/dialog_settings_game_hudconfirm.qc"
+ #include "xonotic/dialog_settings_game_hud.qc"
+ #include "xonotic/dialog_settings_game_messages.qc"
+ #include "xonotic/dialog_settings_game_model.qc"
+ #include "xonotic/dialog_settings_game_view.qc"
+ #include "xonotic/dialog_settings_game_weapons.qc"
+ #include "xonotic/dialog_settings_input.qc"
+ #include "xonotic/dialog_settings_input_userbind.qc"
+ #include "xonotic/dialog_settings_misc.qc"
+ #include "xonotic/dialog_settings_misc_cvars.qc"
+ #include "xonotic/dialog_settings_misc_reset.qc"
+ #include "xonotic/dialog_settings_user.qc"
+ #include "xonotic/dialog_settings_user_languagewarning.qc"
+ #include "xonotic/dialog_settings_video.qc"
+ #include "xonotic/dialog_singleplayer.qc"
+ #include "xonotic/dialog_singleplayer_winner.qc"
+ #include "xonotic/dialog_teamselect.qc"
+ #include "xonotic/gametypelist.qc"
+ #include "xonotic/image.qc"
+ #include "xonotic/inputbox.qc"
+ #include "xonotic/keybinder.qc"
+ #include "xonotic/languagelist.qc"
+ #include "xonotic/listbox.qc"
+ #include "xonotic/mainwindow.qc"
+ #include "xonotic/maplist.qc"
+ #include "xonotic/nexposee.qc"
++#include "xonotic/picker.qc"
+ #include "xonotic/playerlist.qc"
+ #include "xonotic/playermodel.qc"
+ #include "xonotic/playlist.qc"
+ #include "xonotic/radiobutton.qc"
+ #include "xonotic/rootdialog.qc"
+ #include "xonotic/screenshotimage.qc"
+ #include "xonotic/screenshotlist.qc"
+ #include "xonotic/serverlist.qc"
+ #include "xonotic/skinlist.qc"
+ #include "xonotic/slider.qc"
+ #include "xonotic/slider_decibels.qc"
+ #include "xonotic/slider_particles.qc"
+ #include "xonotic/slider_picmip.qc"
+ #include "xonotic/slider_resolution.qc"
+ #include "xonotic/slider_sbfadetime.qc"
+ #include "xonotic/soundlist.qc"
+ #include "xonotic/statslist.qc"
+ #include "xonotic/tabcontroller.qc"
+ #include "xonotic/tab.qc"
+ #include "xonotic/textlabel.qc"
+ #include "xonotic/textslider.qc"
+ #include "xonotic/weaponarenacheckbox.qc"
+ #include "xonotic/weaponslist.qc"
diff --cc qcsrc/menu/xonotic/charmap.qc
index 68c0e566f,d6bc52461..747d1f03b
--- a/qcsrc/menu/xonotic/charmap.qc
+++ b/qcsrc/menu/xonotic/charmap.qc
@@@ -1,19 -1,25 +1,21 @@@
- #ifdef INTERFACE
- CLASS(XonoticCharmap) EXTENDS(XonoticPicker)
+ #ifndef CHARMAP_H
+ #define CHARMAP_H
 -#include "../item.qc"
 -CLASS(XonoticCharmap, Item)
++#include "picker.qc"
++CLASS(XonoticCharmap, XonoticPicker)
  	METHOD(XonoticCharmap, configureXonoticCharmap, void(entity, entity))
 -	METHOD(XonoticCharmap, mousePress, float(entity, vector))
 -	METHOD(XonoticCharmap, mouseRelease, float(entity, vector))
 -	METHOD(XonoticCharmap, mouseMove, float(entity, vector))
 -	METHOD(XonoticCharmap, mouseDrag, float(entity, vector))
 -	METHOD(XonoticCharmap, keyDown, float(entity, float, float, float))
  	METHOD(XonoticCharmap, focusLeave, void(entity))
  	METHOD(XonoticCharmap, resizeNotify, void(entity, vector, vector, vector, vector))
 -	METHOD(XonoticCharmap, draw, void(entity))
 -	ATTRIB(XonoticCharmap, focusable, float, 1)
 -
 -	METHOD(XonoticCharmap, moveFocus, void(entity, vector, vector))
 -	METHOD(XonoticCharmap, enterChar, void(entity))
 +	METHOD(XonoticCharmap, keyDown, float(entity, float, float, float))
  	ATTRIB(XonoticCharmap, inputBox, entity, NULL)
  	ATTRIB(XonoticCharmap, realFontSize, vector, '0 0 0')
 -	ATTRIB(XonoticCharmap, realCellSize, vector, '0 0 0')
 -	ATTRIB(XonoticCharmap, focusedCell, vector, '-1 -1 0')
 -	ATTRIB(XonoticCharmap, previouslyFocusedCell, vector, '-1 -1 0')
 +
 +	ATTRIB(XonoticCharmap, rows, float, 10)
 +	ATTRIB(XonoticCharmap, columns, float, 14)
 +
 +	METHOD(XonoticCharmap, cellSelect, void(entity, vector))
 +	METHOD(XonoticCharmap, cellIsValid, bool(entity, vector))
 +	METHOD(XonoticCharmap, cellDraw, void(entity, vector, vector))
 +	METHOD(XonoticCharmap, charOffset, vector)
  ENDCLASS(XonoticCharmap)
  entity makeXonoticCharmap(entity controlledInputBox);
  #endif
diff --cc qcsrc/menu/xonotic/crosshairpicker.qc
index 2f2c35507,000000000..8922e7fbf
mode 100644,000000..100644
--- a/qcsrc/menu/xonotic/crosshairpicker.qc
+++ b/qcsrc/menu/xonotic/crosshairpicker.qc
@@@ -1,77 -1,0 +1,79 @@@
- #ifdef INTERFACE
- CLASS(XonoticCrosshairPicker) EXTENDS(XonoticPicker)
++#ifndef CROSSHAIRPICKER_H
++#define CROSSHAIRPICKER_H
++#include "picker.qc"
++CLASS(XonoticCrosshairPicker, XonoticPicker)
 +	METHOD(XonoticCrosshairPicker, configureXonoticCrosshairPicker, void(entity))
 +
 +	ATTRIB(XonoticCrosshairPicker, rows, float, 3)
 +	ATTRIB(XonoticCrosshairPicker, columns, float, 12)
 +
 +	METHOD(XonoticCrosshairPicker, cellSelect, void(entity, vector))
 +	METHOD(XonoticCrosshairPicker, cellIsValid, bool(entity, vector))
 +	METHOD(XonoticCrosshairPicker, cellDraw, void(entity, vector, vector))
 +ENDCLASS(XonoticCrosshairPicker)
 +entity makeXonoticCrosshairPicker();
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +string crosshairpicker_cellToCrosshair(entity me, vector cell)
 +{
 +	if(cell.x < 0 || cell.x >= me.columns || cell.y < 0 || cell.y >= me.rows)
 +		return "";
 +	return ftos(31 + cell.y * me.columns + cell.x);
 +}
 +
 +vector crosshairpicker_crosshairToCell(entity me, string crosshair_str)
 +{
 +	float crosshair = stof(crosshair_str) - 31;
 +	if(crosshair - floor(crosshair) > 0)
 +		return '-1 -1 0';
 +	return mod(crosshair, me.columns) * eX + floor(crosshair / me.columns) * eY;
 +}
 +
 +entity makeXonoticCrosshairPicker()
 +{
 +	entity me;
- 	me = spawnXonoticCrosshairPicker();
++	me = NEW(XonoticCrosshairPicker);
 +	me.configureXonoticCrosshairPicker(me);
 +	return me;
 +}
 +
 +void XonoticCrosshairPicker_configureXonoticCrosshairPicker(entity me)
 +{
 +	me.configureXonoticPicker(me);
 +	SUPER(XonoticCrosshairPicker).cellSelect(me, crosshairpicker_crosshairToCell(me, cvar_string("crosshair")));
 +}
 +
 +void XonoticCrosshairPicker_cellSelect(entity me, vector cell)
 +{
 +	cvar_set("crosshair", crosshairpicker_cellToCrosshair(me, me.focusedCell));
 +	SUPER(XonoticCrosshairPicker).cellSelect(me, me.focusedCell);
 +}
 +
 +bool XonoticCrosshairPicker_cellIsValid(entity me, vector cell)
 +{
 +	if(crosshairpicker_cellToCrosshair(me, cell) == "")
 +		return false;
 +	return true;
 +}
 +
 +void XonoticCrosshairPicker_cellDraw(entity me, vector cell, vector cellPos)
 +{
 +	vector sz;
 +	string cross = strcat("/gfx/crosshair", crosshairpicker_cellToCrosshair(me, cell));
 +	sz = draw_PictureSize(cross);
 +	sz = globalToBoxSize(sz, me.size);
 +
 +	float ar = sz.x / sz.y;
 +	sz.x = me.realCellSize.x;
 +	sz.y = sz.x / ar;
 +	sz = sz * 0.95;
 +
 +	vector crosshairPos = cellPos + 0.5 * me.realCellSize;
 +	draw_Picture(crosshairPos - 0.5 * sz, cross, sz, SKINCOLOR_CROSSHAIRPICKER_CROSSHAIR, SKINALPHA_CROSSHAIRPICKER_CROSSHAIR);
 +
 +	if(cvar("crosshair_dot"))
 +		draw_Picture(crosshairPos - 0.5 * sz * cvar("crosshair_dot_size"), "/gfx/crosshairdot", sz * cvar("crosshair_dot_size"), SKINCOLOR_CROSSHAIRPICKER_CROSSHAIR, SKINALPHA_CROSSHAIRPICKER_CROSSHAIR);
 +}
 +#endif
diff --cc qcsrc/menu/xonotic/crosshairpreview.qc
index b271f4dab,000000000..f8de85798
mode 100644,000000..100644
--- a/qcsrc/menu/xonotic/crosshairpreview.qc
+++ b/qcsrc/menu/xonotic/crosshairpreview.qc
@@@ -1,60 -1,0 +1,62 @@@
- #ifdef INTERFACE
- CLASS(XonoticCrosshairPreview) EXTENDS(Item)
++#ifndef CROSSHAIRPREVIEW_H
++#define CROSSHAIRPREVIEW_H
++#include "../item.qc"
++CLASS(XonoticCrosshairPreview, Item)
 +	METHOD(XonoticCrosshairPreview, configureXonoticCrosshairPreview, void(entity))
 +	METHOD(XonoticCrosshairPreview, draw, void(entity))
 +	ATTRIB(XonoticCrosshairPreview, src, string, string_null)
 +	ATTRIB(XonoticCrosshairPreview, src2, string, string_null)
 +	ATTRIB(XonoticCrosshairPreview, disabled, float, 0)
 +	ATTRIB(XonoticCrosshairPreview, disabledAlpha, float, SKINALPHA_DISABLED)
 +ENDCLASS(XonoticCrosshairPreview)
 +entity makeXonoticCrosshairPreview();
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +entity makeXonoticCrosshairPreview()
 +{
 +	entity me;
- 	me = spawnXonoticCrosshairPreview();
++	me = NEW(XonoticCrosshairPreview);
 +	me.configureXonoticCrosshairPreview(me);
 +	return me;
 +}
 +
 +void XonoticCrosshairPreview_configureXonoticCrosshairPreview(entity me)
 +{
 +	me.src = strzone(strcat("/gfx/crosshair", cvar_string("crosshair")));
 +	me.src2 = "/gfx/crosshairdot";
 +}
 +
 +void XonoticCrosshairPreview_draw(entity me)
 +{
 +	float save;
 +	save = draw_alpha;
 +	if(me.disabled)
 +		draw_alpha *= me.disabledAlpha;
 +
 +	vector sz, rgb;
 +	float a;
 +	rgb = stov(cvar_string("crosshair_color"));
 +	a = cvar("crosshair_alpha");
 +	if(me.src)
 +		strunzone(me.src);
 +	me.src = strzone(strcat("/gfx/crosshair", cvar_string("crosshair")));
 +
 +	sz = draw_PictureSize(me.src);
 +	sz = globalToBoxSize(sz, me.size);
 +	sz = sz * cvar("crosshair_size");
 +
 +	draw_Picture('0.5 0.5 0' - 0.5 * sz, me.src, sz, rgb, a);
 +	if(cvar("crosshair_dot"))
 +	{
 +		if(cvar("crosshair_dot_color_custom") && (cvar_string("crosshair_dot_color") != "0"))
 +			rgb = stov(cvar_string("crosshair_dot_color"));
 +
 +		draw_Picture('0.5 0.5 0' - 0.5 * sz * cvar("crosshair_dot_size"), me.src2, sz * cvar("crosshair_dot_size"), rgb, a * cvar("crosshair_dot_alpha"));
 +	}
 +
 +	draw_alpha = save;
 +
 +	SUPER(XonoticCrosshairPreview).draw(me);
 +}
 +#endif
diff --cc qcsrc/menu/xonotic/cvarlist.qc
index 172eb58a6,44520c1f2..9a95ed163
--- a/qcsrc/menu/xonotic/cvarlist.qc
+++ b/qcsrc/menu/xonotic/cvarlist.qc
@@@ -1,8 -1,10 +1,10 @@@
- #ifdef INTERFACE
- CLASS(XonoticCvarList) EXTENDS(XonoticListBox)
+ #ifndef CVARLIST_H
+ #define CVARLIST_H
+ #include "listbox.qc"
+ CLASS(XonoticCvarList, XonoticListBox)
  	METHOD(XonoticCvarList, configureXonoticCvarList, void(entity))
  	ATTRIB(XonoticCvarList, rowsPerItem, float, 1)
 -	METHOD(XonoticCvarList, drawListBoxItem, void(entity, float, vector, float))
 +	METHOD(XonoticCvarList, drawListBoxItem, void(entity, int, vector, bool, bool))
  	METHOD(XonoticCvarList, resizeNotify, void(entity, vector, vector, vector, vector))
  	METHOD(XonoticCvarList, keyDown, float(entity, float, float, float))
  
diff --cc qcsrc/menu/xonotic/gametypelist.qc
index c0a351b31,d8dbc8838..26845f094
--- a/qcsrc/menu/xonotic/gametypelist.qc
+++ b/qcsrc/menu/xonotic/gametypelist.qc
@@@ -1,8 -1,10 +1,10 @@@
- #ifdef INTERFACE
- CLASS(XonoticGametypeList) EXTENDS(XonoticListBox)
+ #ifndef GAMETYPELIST_H
+ #define GAMETYPELIST_H
+ #include "listbox.qc"
+ CLASS(XonoticGametypeList, XonoticListBox)
  	METHOD(XonoticGametypeList, configureXonoticGametypeList, void(entity))
  	ATTRIB(XonoticGametypeList, rowsPerItem, float, 2)
 -	METHOD(XonoticGametypeList, drawListBoxItem, void(entity, float, vector, float))
 +	METHOD(XonoticGametypeList, drawListBoxItem, void(entity, int, vector, bool, bool))
  	METHOD(XonoticGametypeList, resizeNotify, void(entity, vector, vector, vector, vector))
  	METHOD(XonoticGametypeList, setSelected, void(entity, float))
  	METHOD(XonoticGametypeList, loadCvars, void(entity))
diff --cc qcsrc/menu/xonotic/keybinder.qc
index 02f41efd2,82d3db4e5..8953b1db9
--- a/qcsrc/menu/xonotic/keybinder.qc
+++ b/qcsrc/menu/xonotic/keybinder.qc
@@@ -1,8 -1,10 +1,10 @@@
- #ifdef INTERFACE
- CLASS(XonoticKeyBinder) EXTENDS(XonoticListBox)
+ #ifndef KEYBINDER_H
+ #define KEYBINDER_H
+ #include "listbox.qc"
+ CLASS(XonoticKeyBinder, XonoticListBox)
  	METHOD(XonoticKeyBinder, configureXonoticKeyBinder, void(entity))
  	ATTRIB(XonoticKeyBinder, rowsPerItem, int, 1)
 -	METHOD(XonoticKeyBinder, drawListBoxItem, void(entity, float, vector, float))
 +	METHOD(XonoticKeyBinder, drawListBoxItem, void(entity, int, vector, bool, bool))
  	METHOD(XonoticKeyBinder, doubleClickListBoxItem, void(entity, float, vector))
  	METHOD(XonoticKeyBinder, resizeNotify, void(entity, vector, vector, vector, vector))
  	METHOD(XonoticKeyBinder, setSelected, void(entity, float))
diff --cc qcsrc/menu/xonotic/languagelist.qc
index 4f0101341,367da94df..af9d75211
--- a/qcsrc/menu/xonotic/languagelist.qc
+++ b/qcsrc/menu/xonotic/languagelist.qc
@@@ -1,8 -1,10 +1,10 @@@
- #ifdef INTERFACE
- CLASS(XonoticLanguageList) EXTENDS(XonoticListBox)
+ #ifndef LANGUAGELIST_H
+ #define LANGUAGELIST_H
+ #include "listbox.qc"
+ CLASS(XonoticLanguageList, XonoticListBox)
  	METHOD(XonoticLanguageList, configureXonoticLanguageList, void(entity))
  	ATTRIB(XonoticLanguageList, rowsPerItem, float, 1)
 -	METHOD(XonoticLanguageList, drawListBoxItem, void(entity, float, vector, float))
 +	METHOD(XonoticLanguageList, drawListBoxItem, void(entity, int, vector, bool, bool))
  	METHOD(XonoticLanguageList, resizeNotify, void(entity, vector, vector, vector, vector))
  	METHOD(XonoticLanguageList, setSelected, void(entity, float))
  	METHOD(XonoticLanguageList, loadCvars, void(entity))
diff --cc qcsrc/menu/xonotic/picker.qc
index 77b934862,000000000..9fe3f7f36
mode 100644,000000..100644
--- a/qcsrc/menu/xonotic/picker.qc
+++ b/qcsrc/menu/xonotic/picker.qc
@@@ -1,204 -1,0 +1,206 @@@
- #ifdef INTERFACE
- CLASS(XonoticPicker) EXTENDS(Item)
++#ifndef PICKER_H
++#define PICKER_H
++#include "../item.qc"
++CLASS(XonoticPicker, Item)
 +	METHOD(XonoticPicker, configureXonoticPicker, void(entity))
 +	METHOD(XonoticPicker, mousePress, float(entity, vector))
 +	METHOD(XonoticPicker, mouseRelease, float(entity, vector))
 +	METHOD(XonoticPicker, mouseMove, float(entity, vector))
 +	METHOD(XonoticPicker, mouseDrag, float(entity, vector))
 +	METHOD(XonoticPicker, keyDown, float(entity, float, float, float))
 +	METHOD(XonoticPicker, draw, void(entity))
 +	ATTRIB(XonoticPicker, focusable, float, 1)
 +	ATTRIB(XonoticPicker, disabled, float, 0)
 +	ATTRIB(XonoticPicker, alpha, float, 1)
 +	ATTRIB(XonoticPicker, disabledAlpha, float, SKINALPHA_DISABLED)
 +
 +	ATTRIB(XonoticPicker, rows, float, 3)
 +	ATTRIB(XonoticPicker, columns, float, 2)
 +
 +	METHOD(XonoticPicker, moveFocus, void(entity, vector, vector))
 +	METHOD(XonoticPicker, cellSelect, void(entity, vector))
 +	METHOD(XonoticPicker, cellDraw, void(entity, vector, vector))
 +	METHOD(XonoticPicker, cellIsValid, bool(entity, vector))
 +	ATTRIB(XonoticPicker, realCellSize, vector, '0 0 0')
 +	ATTRIB(XonoticPicker, selectedCell, vector, '-1 -1 0')
 +	ATTRIB(XonoticPicker, focusedCell, vector, '-1 -1 0')
 +	ATTRIB(XonoticPicker, focusedCellAlpha, float, 0)
 +	ATTRIB(XonoticPicker, focusedCellTime, float, 0)
 +	ATTRIB(XonoticPicker, pressedCell, vector, '-1 -1 0')
 +ENDCLASS(XonoticPicker)
 +entity makeXonoticPicker();
 +#endif
 +
 +#ifdef IMPLEMENTATION
 +
 +entity makeXonoticPicker()
 +{
 +	entity me;
- 	me = spawnXonoticPicker();
++	me = NEW(XonoticPicker);
 +	me.configureXonoticPicker(me);
 +	return me;
 +}
 +
 +void XonoticPicker_configureXonoticPicker(entity me)
 +{
 +	me.realCellSize = eX / me.columns + eY / me.rows;
 +}
 +
 +float XonoticPicker_mouseMove(entity me, vector coords)
 +{
 +	vector prevFocusedCell = me.focusedCell;
 +	me.focusedCell_x = floor(coords.x * me.columns);
 +	me.focusedCell_y = floor(coords.y * me.rows);
 +
 +	if(me.focusedCell.x < 0 || me.focusedCell.y < 0 ||
 +	   me.focusedCell.x >= me.columns || me.focusedCell.y >= me.rows)
 +	{
 +		me.focusedCell = '-1 -1 0';
 +		return 0;
 +	}
 +
 +	if(me.focusedCell != prevFocusedCell)
 +		me.focusedCellAlpha = SKINALPHA_LISTBOX_FOCUSED;
 +
 +	return 1;
 +}
 +
 +float XonoticPicker_mouseDrag(entity me, vector coords)
 +{
 +	return me.mouseMove(me, coords);
 +}
 +
 +float XonoticPicker_mousePress(entity me, vector coords)
 +{
 +	me.mouseMove(me, coords);
 +
 +	if(me.focusedCell.x >= 0)
 +	{
 +		me.pressed = 1;
 +		me.pressedCell = me.focusedCell;
 +	}
 +
 +	return 1;
 +}
 +
 +float XonoticPicker_mouseRelease(entity me, vector coords)
 +{
 +	if(!me.pressed)
 +		return 0;
 +
 +	me.mouseMove(me, coords);
 +
 +	if(me.focusedCell == me.pressedCell)
 +		me.cellSelect(me, me.focusedCell);
 +
 +	me.pressed = 0;
 +	return 1;
 +}
 +
 +float XonoticPicker_keyDown(entity me, float key, float ascii, float shift)
 +{
 +	switch(key)
 +	{
 +		case K_LEFTARROW:
 +		case K_KP_LEFTARROW:
 +			me.moveFocus(me, me.focusedCell, '-1 0 0');
 +			return 1;
 +		case K_RIGHTARROW:
 +		case K_KP_RIGHTARROW:
 +			me.moveFocus(me, me.focusedCell, '1 0 0');
 +			return 1;
 +		case K_UPARROW:
 +		case K_KP_UPARROW:
 +			me.moveFocus(me, me.focusedCell, '0 -1 0');
 +			return 1;
 +		case K_DOWNARROW:
 +		case K_KP_DOWNARROW:
 +			me.moveFocus(me, me.focusedCell, '0 1 0');
 +			return 1;
 +		case K_HOME:
 +		case K_KP_HOME:
 +			me.focusedCell = '0 0 0';
 +			return 1;
 +		case K_END:
 +		case K_KP_END:
 +			me.focusedCell_x = me.columns - 1;
 +			me.focusedCell_y = me.rows - 1;
 +			return 1;
 +		case K_ENTER:
 +		case K_KP_ENTER:
 +		case K_INS:
 +		case K_KP_INS:
 +			me.cellSelect(me, me.focusedCell);
 +			return 1;
 +	}
 +	return 0;
 +}
 +
 +void XonoticPicker_moveFocus(entity me, vector initialCell, vector step)
 +{
 +	me.focusedCell_x = mod(me.focusedCell.x + step.x + me.columns, me.columns);
 +	me.focusedCell_y = mod(me.focusedCell.y + step.y + me.rows, me.rows);
 +
 +	if(me.focusedCell != initialCell) // Recursion break
 +		if(!me.cellIsValid(me, me.focusedCell))
 +			me.moveFocus(me, initialCell, step);
 +}
 +
 +void XonoticPicker_cellSelect(entity me, vector cell)
 +{
 +	me.selectedCell = cell;
 +}
 +
 +bool XonoticPicker_cellIsValid(entity me, vector cell)
 +{
 +	return true;
 +}
 +
 +void XonoticPicker_cellDraw(entity me, vector cell, vector cellPos)
 +{
 +}
 +
 +void XonoticPicker_draw(entity me)
 +{
 +	float save;
 +
 +	me.focusable = !me.disabled;
 +
 +	save = draw_alpha;
 +	if(me.disabled)
 +		draw_alpha *= me.disabledAlpha;
 +
 +	vector cell, cellPos;
 +	cell = '0 0 0';
 +	cellPos = '0 0 0';
 +
 +	for(cell_y = 0; cell.y < me.rows; ++cell.y)
 +	{
 +		cellPos_y = mod(cell.y, me.rows) / me.rows;
 +		for(cell_x = 0; cell.x < me.columns; ++cell.x)
 +		{
 +			if(!me.cellIsValid(me, cell))
 +				continue;
 +
 +			cellPos_x = mod(cell.x, me.columns) / me.columns;
 +
 +			if(cell == me.selectedCell)
 +				draw_Fill(cellPos, me.realCellSize, SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
 +			else if(cell == me.focusedCell && me.focused)
 +			{
 +				if(!me.pressed || me.focusedCell == me.pressedCell)
 +				{
 +					me.focusedCellAlpha = getFadedAlpha(me.focusedCellAlpha, SKINALPHA_LISTBOX_FOCUSED, SKINFADEALPHA_LISTBOX_FOCUSED);
 +					draw_Fill(cellPos, me.realCellSize, SKINCOLOR_LISTBOX_FOCUSED, me.focusedCellAlpha);
 +				}
 +			}
 +
 +			me.cellDraw(me, cell, cellPos);
 +		}
 +	}
 +
 +	draw_alpha = save;
 +
 +	SUPER(XonoticPicker).draw(me);
 +}
 +#endif
diff --cc qcsrc/menu/xonotic/playerlist.qc
index f6fba8bc8,437206b88..db3ac8a20
--- a/qcsrc/menu/xonotic/playerlist.qc
+++ b/qcsrc/menu/xonotic/playerlist.qc
@@@ -1,8 -1,10 +1,10 @@@
- #ifdef INTERFACE
- CLASS(XonoticPlayerList) EXTENDS(XonoticListBox)
+ #ifndef PLAYERLIST_H
+ #define PLAYERLIST_H
+ #include "listbox.qc"
+ CLASS(XonoticPlayerList, XonoticListBox)
  	ATTRIB(XonoticPlayerList, rowsPerItem, float, 1)
  	METHOD(XonoticPlayerList, resizeNotify, void(entity, vector, vector, vector, vector))
 -	METHOD(XonoticPlayerList, drawListBoxItem, void(entity, float, vector, float))
 +	METHOD(XonoticPlayerList, drawListBoxItem, void(entity, int, vector, bool, bool))
  	ATTRIB(XonoticPlayerList, allowFocusSound, float, 0)
  	ATTRIB(XonoticPlayerList, realFontSize, vector, '0 0 0')
  	ATTRIB(XonoticPlayerList, columnNameOrigin, float, 0)