From 54834649ebfdaec94cb6c62476f40d52740242bd Mon Sep 17 00:00:00 2001 From: TimePath Date: Sun, 16 Aug 2015 14:32:29 +1000 Subject: [PATCH] Initial guide menu --- qcsrc/Makefile | 2 +- qcsrc/common/mapinfo.qc | 6 +- qcsrc/common/mapinfo.qh | 1 - qcsrc/common/oo.qh | 4 +- qcsrc/menu/classes.inc | 3 + qcsrc/menu/xonotic/dialog_media_guide.qc | 102 ++++++++++++++ .../xonotic/dialog_media_guide_entries.qc | 127 ++++++++++++++++++ .../menu/xonotic/dialog_media_guide_topics.qc | 110 +++++++++++++++ .../menu/xonotic/dialog_multiplayer_media.qc | 3 +- 9 files changed, 350 insertions(+), 8 deletions(-) create mode 100644 qcsrc/menu/xonotic/dialog_media_guide.qc create mode 100644 qcsrc/menu/xonotic/dialog_media_guide_entries.qc create mode 100644 qcsrc/menu/xonotic/dialog_media_guide_topics.qc diff --git a/qcsrc/Makefile b/qcsrc/Makefile index f53707e829..7ac264f943 100644 --- a/qcsrc/Makefile +++ b/qcsrc/Makefile @@ -13,7 +13,7 @@ QCCFLAGS ?= \ -std=gmqcc \ -O3 -flno \ -Werror -fno-bail-on-werror -Wall \ - -fftepp -fftepp-predefs -Wcpp -futf8 \ + -fftepp -fftepp-predefs -Wcpp -futf8 -freturn-assignments \ $(QCCFLAGS_WTFS) \ $(QCCFLAGS_FEATURES) \ $(QCCFLAGS_EXTRA) $(QCCFLAGS_WATERMARK) diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index 0b851d53b0..da45a491c7 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -252,11 +252,9 @@ string unquote(string s) return ""; } -float MapInfo_Get_ByID(float i) +bool MapInfo_Get_ByID(int i) { - if(MapInfo_Get_ByName(MapInfo_BSPName_ByID(i), 0, 0)) - return 1; - return 0; + return MapInfo_Get_ByName(MapInfo_BSPName_ByID(i), 0, 0) ? true : false; } string _MapInfo_Map_worldspawn_music; diff --git a/qcsrc/common/mapinfo.qh b/qcsrc/common/mapinfo.qh index 22b17d900d..f2f5b5c283 100644 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@ -28,7 +28,6 @@ CLASS(Gametype, Object) this.team = gteamplay; this.model2 = defaults; this.gametype_description = gdescription; - return this; } ENDCLASS(Gametype) diff --git a/qcsrc/common/oo.qh b/qcsrc/common/oo.qh index aa660ab15d..48511b393c 100644 --- a/qcsrc/common/oo.qh +++ b/qcsrc/common/oo.qh @@ -47,7 +47,8 @@ entity __spawn(string _classname, string _sourceFile, int _sourceLine) { OVERLOAD(spawn##cname, this, ##__VA_ARGS__) #define CONSTRUCTOR(cname, ...) \ - cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__) + cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__) { return = this; } \ + [[accumulate]] cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__) .string vtblname; .entity vtblbase; @@ -110,6 +111,7 @@ ACCUMULATE_FUNCTION(__static_init, RegisterClasses) [[last]] INIT(cname) { return this; } #define SUPER(cname) (cname##_vtbl.vtblbase) +#define super (this.vtblbase.vtblbase) #define spawn_static(this) #define spawn_1(this) diff --git a/qcsrc/menu/classes.inc b/qcsrc/menu/classes.inc index 01a7c8a040..2e86017f9f 100644 --- a/qcsrc/menu/classes.inc +++ b/qcsrc/menu/classes.inc @@ -60,6 +60,9 @@ #include "xonotic/dialog_hudpanel_vote.qc" #include "xonotic/dialog_hudpanel_weapons.qc" #include "xonotic/dialog_hudsetup_exit.qc" +#include "xonotic/dialog_media_guide.qc" +#include "xonotic/dialog_media_guide_entries.qc" +#include "xonotic/dialog_media_guide_topics.qc" #include "xonotic/dialog_monstertools.qc" #include "xonotic/dialog_multiplayer.qc" #include "xonotic/dialog_multiplayer_create.qc" diff --git a/qcsrc/menu/xonotic/dialog_media_guide.qc b/qcsrc/menu/xonotic/dialog_media_guide.qc new file mode 100644 index 0000000000..b5179dea00 --- /dev/null +++ b/qcsrc/menu/xonotic/dialog_media_guide.qc @@ -0,0 +1,102 @@ +#ifndef DIALOG_MEDIA_GUIDE_H +#define DIALOG_MEDIA_GUIDE_H +#include "dialog_media_guide_topics.qc" +#include "dialog_media_guide_entries.qc" +#include "tab.qc" +CLASS(XonoticGuideTab, XonoticTab) + ATTRIB(XonoticGuideTab, rows, float, 21) + ATTRIB(XonoticGuideTab, columns, float, 6) + METHOD(XonoticGuideTab, fill, void(entity)) + METHOD(XonoticGuideTab, topicChangeNotify, void(entity)) + METHOD(XonoticGuideTab, topicSelectNotify, void(entity)) + + entity Topics_get(int i); + int Topics_reload(string filter); + .string mdl, message; + ATTRIB(XonoticGuideTab, topicList, entity, NEW(XonoticTopicList, Topics_get, func_null, Topics_reload, mdl, message, func_null, this)) + + entity XonoticGuideTab_maps_get(int i); + int XonoticGuideTab_maps_indexOf(string s); + int XonoticGuideTab_maps_reload(string s); + void XonoticGuideTab_maps_destroy(entity this); + .string icon, name; + ATTRIB(XonoticGuideTab, entryList, entity, NEW(XonoticEntryList, XonoticGuideTab_maps_get, XonoticGuideTab_maps_indexOf, XonoticGuideTab_maps_reload, icon, name, XonoticGuideTab_maps_destroy)) + + INIT(XonoticGuideTab) { + this.topicList.entryIconPrefix = "gametype_"; + this.configureDialog(this); + } +ENDCLASS(XonoticGuideTab) +#endif + +#ifdef IMPLEMENTATION + +void XonoticGuideTab_fill(entity this) +{ + entity topics = this.topicList; + entity entries = this.entryList; + entity filter = entries.stringFilterBox = makeXonoticInputBox(false, string_null); + filter.keyDown = MapList_StringFilterBox_keyDown; + filter.onChange = MapList_StringFilterBox_Change; + filter.onChangeEntity = entries; + entries.controlledTextbox = filter; + + this.gotoRC(this, 0, 0); + this.TD(this, 1, 3 / 2, makeXonoticHeaderLabel(_("Topic"))); + this.TR(this); + this.TD(this, this.rows - 1, 3 / 2, topics); + + this.gotoRC(this, 0, 3 / 2); + this.setFirstColumn(this, this.currentColumn); + this.TD(this, 1, 2, makeXonoticHeaderLabel(_("Entry"))); + this.TR(this); + this.TD(this, this.rows - 1 - 1, 2, entries); + + this.gotoRC(this, this.rows - 1, this.firstColumn); + this.TD(this, 1, 0.3, makeXonoticTextLabel(0, _("Filter:"))); + this.TD(this, 1, 2 - 0.3, filter); + + this.topicChangeNotify(this); +} + +void XonoticGuideTab_topicChangeNotify(entity this) +{ + entity entries = this.entryList; + entries.refilter(entries); +} + +void XonoticGuideTab_topicSelectNotify(entity this) { this.setFocus(this, this.entryList); } + +entity Topics_get(int i) { return MAPINFO_TYPES[i]; } + +int Topics_reload(string filter) { return MAPINFO_TYPE_COUNT; } + +entity XonoticGuideTab_maps_get(int i) +{ + if (!MapInfo_Get_ByID(i)) return NULL; + static entity e; + if (!e) e = new(entry); + e.name = MapInfo_Map_titlestring; + string path = strcat("/maps/", MapInfo_Map_bspname); + string img = draw_PictureSize(path) ? path : "nopreview_map"; + e.icon = img; + MapInfo_ClearTemps(); + return e; +} + +int XonoticGuideTab_maps_indexOf(string s) +{ + MapInfo_FindName(s); + return MapInfo_FindName_firstResult; +} + +int XonoticGuideTab_maps_reload(string s) +{ + MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 0); + if (s) MapInfo_FilterString(s); + return MapInfo_count; +} + +void XonoticGuideTab_maps_destroy(entity this) { MapInfo_Shutdown(); } + +#endif diff --git a/qcsrc/menu/xonotic/dialog_media_guide_entries.qc b/qcsrc/menu/xonotic/dialog_media_guide_entries.qc new file mode 100644 index 0000000000..e7498b3dd3 --- /dev/null +++ b/qcsrc/menu/xonotic/dialog_media_guide_entries.qc @@ -0,0 +1,127 @@ +#ifndef DIALOG_MEDIA_GUIDE_ENTRIES_H +#define DIALOG_MEDIA_GUIDE_ENTRIES_H +#include "listbox.qc" +CLASS(XonoticEntryList, XonoticListBox) + ATTRIB(XonoticEntryList, alphaBG, float, 0) + ATTRIB(XonoticEntryList, columnNameOrigin, float, 0) + ATTRIB(XonoticEntryList, columnNameSize, float, 0) + ATTRIB(XonoticEntryList, columnPreviewOrigin, float, 0) + ATTRIB(XonoticEntryList, columnPreviewSize, float, 0) + ATTRIB(XonoticEntryList, itemAbsSize, vector, '0 0 0') + ATTRIB(XonoticEntryList, origin, vector, '0 0 0') + ATTRIB(XonoticEntryList, realFontSize, vector, '0 0 0') + ATTRIB(XonoticEntryList, realUpperMargin1, float, 0) + ATTRIB(XonoticEntryList, realUpperMargin2, float, 0) + ATTRIB(XonoticEntryList, rowsPerItem, float, 4) + ATTRIB(XonoticEntryList, stringFilterBox, entity, NULL) + ATTRIB(XonoticEntryList, stringFilter, string, string_null) + ATTRIB(XonoticEntryList, typeToSearchString, string, string_null) + ATTRIB(XonoticEntryList, typeToSearchTime, float, 0) + + METHOD(XonoticEntryList, drawListBoxItem, void(entity, int, vector, bool, bool)) + METHOD(XonoticEntryList, keyDown, float(entity, float, float, float)) + METHOD(XonoticEntryList, refilter, void(entity)) + METHOD(XonoticEntryList, resizeNotify, void(entity, vector, vector, vector, vector)) + + INIT(XonoticEntryList) { + this.configureXonoticListBox(this); + } + + .string stringfield_null; + ATTRIB(XonoticEntryList, destroy, void(entity), func_null) + ATTRIB(XonoticEntryList, entries, void(int), func_null) + ATTRIB(XonoticEntryList, entryIcon, .string, stringfield_null) + ATTRIB(XonoticEntryList, entryName, .string, stringfield_null) + ATTRIB(XonoticEntryList, indexOf, int(string), func_null) + ATTRIB(XonoticEntryList, reload, int(string), func_null) + + CONSTRUCTOR(XonoticEntryList, entity _entries(int), int _indexOf(string), int _reload(string), .string _entryIcon, .string _entryName, void _destroy(entity)) { + CONSTRUCT(XonoticEntryList); + this.entries = _entries; + this.indexOf = _indexOf; + this.reload = _reload; + this.entryIcon = _entryIcon; + this.entryName = _entryName; + this.destroy = _destroy; + this.refilter(this); + } + +ENDCLASS(XonoticEntryList) +#endif + +#ifdef IMPLEMENTATION + +void XonoticEntryList_drawListBoxItem(entity this, int i, vector absSize, bool isSelected, bool isFocused) +{ + entity e = this.entries(i); + if (!e) return; + + if (isSelected) { + draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED); + } else if (isFocused) { + this.focusedItemAlpha = getFadedAlpha(this.focusedItemAlpha, SKINALPHA_LISTBOX_FOCUSED, SKINFADEALPHA_LISTBOX_FOCUSED); + draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_FOCUSED, this.focusedItemAlpha); + } + string s = draw_TextShortenToWidth(strdecolorize(e.name), this.columnNameSize, 0, this.realFontSize); + draw_Picture(this.columnPreviewOrigin * eX, e.icon, this.columnPreviewSize * eX + eY, '1 1 1', SKINALPHA_LISTBOX_SELECTED); + draw_Text(this.realUpperMargin1 * eY + (this.columnNameOrigin + 0.00 * (this.columnNameSize - draw_TextWidth(s, 0, this.realFontSize))) * eX, s, this.realFontSize, '1 1 1', SKINALPHA_TEXT, 0); +} + +float XonoticEntryList_keyDown(entity this, float scan, float ascii, float shift) +{ + if (this.nItems <= 0) { + return super.keyDown(this, scan, ascii, shift); + } else if (scan == K_BACKSPACE) { + if (time < this.typeToSearchTime) { + string save = substring(this.typeToSearchString, 0, strlen(this.typeToSearchString) - 1); + if (this.typeToSearchString) strunzone(this.typeToSearchString); + this.typeToSearchString = strzone(save); + this.typeToSearchTime = time + 0.5; + if (strlen(this.typeToSearchString)) { + int idx = this.indexOf(this.typeToSearchString); + if (idx >= 0) this.setSelected(this, idx); + } + } + } else if (ascii >= 32 && ascii != 127) { + string ch = chr(ascii); + string save = (time > this.typeToSearchTime) ? ch : strcat(this.typeToSearchString, ch); + if (this.typeToSearchString) strunzone(this.typeToSearchString); + this.typeToSearchString = strzone(save); + this.typeToSearchTime = time + 0.5; + int idx = this.indexOf(this.typeToSearchString); + if (idx >= 0) this.setSelected(this, idx); + } else if (shift & S_CTRL && scan == 'f') { + this.parent.setFocus(this.parent, this.stringFilterBox); + } else if (shift & S_CTRL && scan == 'u') { + this.stringFilterBox.setText(this.stringFilterBox, ""); + if (this.stringFilter) strunzone(this.stringFilter); + this.stringFilter = string_null; + this.refilter(this); + } + return super.keyDown(this, scan, ascii, shift); +} + +void XonoticEntryList_refilter(entity this) +{ + this.nItems = this.reload(this.stringFilter); + for (int i = 0, n = this.nItems; i < n; ++i) { + draw_PreloadPicture(this.entries(i).icon); + } +} + +void XonoticEntryList_resizeNotify(entity this, vector relOrigin, vector relSize, vector absOrigin, vector absSize) +{ + this.itemAbsSize = '0 0 0'; + super.resizeNotify(this, relOrigin, relSize, absOrigin, absSize); + + this.realFontSize_y = this.fontSize / (this.itemAbsSize_y = (absSize.y * this.itemHeight)); + this.realFontSize_x = this.fontSize / (this.itemAbsSize_x = (absSize.x * (1 - this.controlWidth))); + this.realUpperMargin1 = 0.5 * (1 - 2.5 * this.realFontSize.y); + this.realUpperMargin2 = this.realUpperMargin1 + 1.5 * this.realFontSize.y; + + this.columnPreviewOrigin = 0; + this.columnPreviewSize = this.itemAbsSize.y / this.itemAbsSize.x * 4 / 3; + this.columnNameOrigin = this.columnPreviewOrigin + this.columnPreviewSize + this.realFontSize.x; + this.columnNameSize = 1 - this.columnPreviewSize - 2 * this.realFontSize.x; +} +#endif diff --git a/qcsrc/menu/xonotic/dialog_media_guide_topics.qc b/qcsrc/menu/xonotic/dialog_media_guide_topics.qc new file mode 100644 index 0000000000..5624216ddb --- /dev/null +++ b/qcsrc/menu/xonotic/dialog_media_guide_topics.qc @@ -0,0 +1,110 @@ +#ifndef DIALOG_MEDIA_GUIDE_TOPICS_H +#define DIALOG_MEDIA_GUIDE_TOPICS_H +#include "listbox.qc" +CLASS(XonoticTopicList, XonoticListBox) + ATTRIB(XonoticTopicList, columnIconOrigin, float, 0) + ATTRIB(XonoticTopicList, columnIconSize, float, 0) + ATTRIB(XonoticTopicList, columnNameOrigin, float, 0) + ATTRIB(XonoticTopicList, columnNameSize, float, 0) + ATTRIB(XonoticTopicList, realFontSize, vector, '0 0 0') + ATTRIB(XonoticTopicList, realUpperMargin, float, 0) + ATTRIB(XonoticTopicList, rowsPerItem, float, 3) + + METHOD(XonoticTopicList, clickListBoxItem, void(entity, float, vector)) + METHOD(XonoticTopicList, drawListBoxItem, void(entity, int, vector, bool, bool)) + METHOD(XonoticTopicList, keyDown, bool(entity, float, float, float)) + METHOD(XonoticTopicList, resizeNotify, void(entity, vector, vector, vector, vector)) + METHOD(XonoticTopicList, setSelected, void(entity, int)) + + INIT(XonoticTopicList) { + this.configureXonoticListBox(this); + } + + .string stringfield_null; + ATTRIB(XonoticTopicList, entries, entity(int), func_null) + ATTRIB(XonoticTopicList, entryIconPrefix, string, "") + ATTRIB(XonoticTopicList, entryIcon, .string, stringfield_null) + ATTRIB(XonoticTopicList, entryName, .string, stringfield_null) + ATTRIB(XonoticTopicList, listener, entity, NULL) + + CONSTRUCTOR(XonoticTopicList, entity(int) _entries, int _indexOf(string), int _reload(string), .string _entryIcon, .string _entryName, void _destroy(entity), entity _listener) { + CONSTRUCT(XonoticTopicList); + this.entries = _entries; + this.nItems = _reload(""); + this.entryIcon = _entryIcon; + this.entryName = _entryName; + this.listener = _listener; + } +ENDCLASS(XonoticTopicList) +#endif + +#ifdef IMPLEMENTATION + +void XonoticTopicList_clickListBoxItem(entity this, float i, vector where) +{ + m_play_click_sound(MENU_SOUND_SELECT); +} + +void XonoticTopicList_drawListBoxItem(entity this, int i, vector absSize, bool isSelected, bool isFocused) +{ + if (isSelected) { + draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED); + } else if (isFocused) { + this.focusedItemAlpha = getFadedAlpha(this.focusedItemAlpha, SKINALPHA_LISTBOX_FOCUSED, SKINFADEALPHA_LISTBOX_FOCUSED); + draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_FOCUSED, this.focusedItemAlpha); + } + entity entry = this.entries(i); + string icon = strcat(this.entryIconPrefix, entry.(this.entryIcon)); + string name = entry.(this.entryName); + draw_Picture(this.columnIconOrigin * eX, icon, this.columnIconSize * eX + eY, '1 1 1', SKINALPHA_LISTBOX_SELECTED); + vector save_fontscale = draw_fontscale; + float f = draw_CondensedFontFactor(name, false, this.realFontSize, 1); + draw_fontscale.x *= f; + vector fs = this.realFontSize; + fs.x *= f; + draw_Text(this.realUpperMargin * eY + this.columnNameOrigin * eX, name, fs, '1 1 1', SKINALPHA_TEXT, 0); + draw_fontscale = save_fontscale; +} + +bool XonoticTopicList_keyDown(entity this, float scan, float ascii, float shift) +{ + if (scan == K_ENTER || scan == K_KP_ENTER) { + m_play_click_sound(MENU_SOUND_EXECUTE); + entity l = this.listener; + if (l) { + void(entity) func = l.topicSelectNotify; + if (func) { + func(l); + } + } + return true; + } + return super.keyDown(this, scan, ascii, shift); +} + +void XonoticTopicList_resizeNotify(entity this, vector relOrigin, vector relSize, vector absOrigin, vector absSize) +{ + this.itemAbsSize = '0 0 0'; + super.resizeNotify(this, relOrigin, relSize, absOrigin, absSize); + + this.realFontSize_y = this.fontSize / (this.itemAbsSize_y = (absSize.y * this.itemHeight)); + this.realFontSize_x = this.fontSize / (this.itemAbsSize_x = (absSize.x * (1 - this.controlWidth))); + this.realUpperMargin = 0.5 * (1 - this.realFontSize.y); + this.columnIconOrigin = 0; + this.columnIconSize = this.itemAbsSize.y / this.itemAbsSize.x; + this.columnNameOrigin = this.columnIconOrigin + this.columnIconSize + (0.5 * this.realFontSize.x); + this.columnNameSize = 1 - this.columnIconSize - (1.5 * this.realFontSize.x); +} + +void XonoticTopicList_setSelected(entity this, int i) +{ + super.setSelected(this, i); + entity l = this.listener; + if (l) { + void(entity) func = l.topicChangeNotify; + if (func) { + func(l); + } + } +} +#endif diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_media.qc b/qcsrc/menu/xonotic/dialog_multiplayer_media.qc index 03ffd0f36d..0000d1d608 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_media.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_media.qc @@ -5,7 +5,7 @@ CLASS(XonoticMediaTab, XonoticTab) METHOD(XonoticMediaTab, fill, void(entity)) ATTRIB(XonoticMediaTab, intendedWidth, float, 0.9) ATTRIB(XonoticMediaTab, rows, float, 23) - ATTRIB(XonoticMediaTab, columns, float, 3) + ATTRIB(XonoticMediaTab, columns, float, 4) ATTRIB(XonoticMediaTab, name, string, "Media") ENDCLASS(XonoticMediaTab) entity makeXonoticMediaTab(); @@ -25,6 +25,7 @@ void XonoticMediaTab_fill(entity me) mc = makeXonoticTabController(me.rows - 2); me.gotoRC(me, 0.5, 0); + me.TD(me, 1, 1, e = mc.makeTabButton(mc, _("Guide"), NEW(XonoticGuideTab))); me.TD(me, 1, 1, e = mc.makeTabButton(mc, _("Demos"), makeXonoticDemoBrowserTab())); me.TD(me, 1, 1, e = mc.makeTabButton(mc, _("Screenshots"), makeXonoticScreenshotBrowserTab())); me.TD(me, 1, 1, e = mc.makeTabButton(mc, _("Music Player"), makeXonoticMusicPlayerTab())); -- 2.39.2