]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'BuddyFriendGuy/mapStringFilter' into 'master'
authorMario <zacjardine@y7mail.com>
Tue, 4 Aug 2015 05:23:22 +0000 (05:23 +0000)
committerMario <zacjardine@y7mail.com>
Tue, 4 Aug 2015 05:23:22 +0000 (05:23 +0000)
request merging BuddyFriendGuy's map string filter

This is the feature for the multi-user game creation tab. It filters the list of the maps by a user-provided string.

See merge request !117

qcsrc/common/mapinfo.qc
qcsrc/common/mapinfo.qh
qcsrc/menu/xonotic/dialog_multiplayer_create.qc
qcsrc/menu/xonotic/maplist.qc
tooltips.db

index 2e692d7268f9e7b55eefc8ea9c8c25d7c832825c..55165a12c2efa02fe371e683c49b0a89f18e5d15 100644 (file)
@@ -187,6 +187,32 @@ float MapInfo_FilterGametype(int pGametype, int pFeatures, int pFlagsRequired, i
 
        return 1;
 }
+void MapInfo_FilterString(string sf)
+{
+       // this function further filters _MapInfo_filtered, which is prepared by MapInfo_FilterGametype by string
+       float i, j;
+       string title;
+
+       for(i = 0, j = -1; i < MapInfo_count; ++i)
+       {
+               if (MapInfo_Get_ByID(i))
+               {
+                       // prepare for keyword filter
+                       if (MapInfo_Map_title && strstrofs(MapInfo_Map_title, "<TITLE>", 0) == -1)
+                               title = MapInfo_Map_title;
+                       else
+                               title = MapInfo_Map_bspname;
+                       // keyword filter
+                       if((strstrofs(strtolower(title), strtolower(sf), 0)) >= 0)
+                               bufstr_set(_MapInfo_filtered, ++j, bufstr_get(_MapInfo_filtered, i));
+               }
+       }
+       MapInfo_count = j + 1;
+       MapInfo_ClearTemps();
+
+       // sometimes the glob isn't sorted nicely, so fix it here...
+       heapsort(MapInfo_count, _MapInfo_FilterList_swap, _MapInfo_FilterList_cmp, world);
+}
 
 void MapInfo_Filter_Free()
 {
index 134103b89f3f9b49ea014916403ea772452ebf4b..3038cce60e70a0ebe8380f2c47f350a779a151ba 100644 (file)
@@ -120,6 +120,7 @@ void MapInfo_Enumerate();
 // filter the info by game type mask (updates MapInfo_count)
 float MapInfo_progress;
 float MapInfo_FilterGametype(float gametype, float features, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate); // 1 on success, 0 on temporary failure (call it again next frame then; use MapInfo_progress as progress indicator)
+void MapInfo_FilterString(string sf); // filter _MapInfo_filtered (created by MapInfo_FilterGametype) with keyword
 int MapInfo_CurrentFeatures(); // retrieves currently required features from cvars
 int MapInfo_CurrentGametype(); // retrieves current gametype from cvars
 int MapInfo_ForbiddenFlags(); // retrieves current flags from cvars
index e56f2eef3d991e447f6b5e9d09ffdbff7a30bdd1..0624f6d89f67813d578050e8fd08621775539f45 100644 (file)
@@ -66,6 +66,8 @@ void XonoticServerCreateTab_fill(entity me)
 {
        entity e, e0;
 
+       // the left half begins here
+
        me.gotoRC(me, 0.5, 0);
                me.TD(me, 1, 3, makeXonoticHeaderLabel(_("Gametype")));
        me.TR(me);
@@ -131,32 +133,63 @@ void XonoticServerCreateTab_fill(entity me)
                        e.configureXonoticTextSliderValues(e);
                        setDependent(e, "bot_number", 0, -1);
 
-       me.gotoRC(me, me.rows - 3.5, 0);
+       me.gotoRC(me, me.rows - 3.8, 0);
                me.TD(me, 1, 3, e0 = makeXonoticTextLabel(0.5, string_null));
                        e0.textEntity = main.mutatorsDialog;
                        e0.allowCut = 1;
                        //e0.allowWrap = 1;
-       me.TR(me);
+
+       // mapListBox is in the right column but the ref is needed for mutators dialog here
+       me.mapListBox = makeXonoticMapList();
+       // here we use the following line instead of me.TR(me) for better visual spacing;
+       // this decision was made in this poll: http://forums.xonotic.org/showthread.php?tid=5445
+       me.gotoRC(me, me.rows - 2.5, 0);
                me.TDempty(me, 0.5);
                me.TD(me, 1, 2, e = makeXonoticButton(_("Mutators"), '0 0 0'));
                        e.onClick = DialogOpenButton_Click;
                        e.onClickEntity = main.mutatorsDialog;
                        main.mutatorsDialog.refilterEntity = me.mapListBox;
 
+       // The right half begins here
+
        me.gotoRC(me, 0.5, 3.2); me.setFirstColumn(me, me.currentColumn);
-               me.mapListBox = makeXonoticMapList();
+               // the maplistbox
                me.TD(me, 1, 3, e = makeXonoticHeaderLabel(_("Maplist")));
                        makeCallback(e, me.mapListBox, me.mapListBox.refilterCallback);
        me.TR(me);
-               me.TD(me, me.rows - 4, 3, me.mapListBox);
-       me.gotoRC(me, me.rows - 2.5, 3.2);
-               me.TDempty(me, 0.375);
-               me.TD(me, 1, 1.125, e = makeXonoticButton(_("Select all"), '0 0 0'));
-                       e.onClick = MapList_All;
+               // we use 5.8 here to visually match the bottom line of the component on the left (Bot Skill)
+               me.TD(me, me.rows - 6.8, 3, me.mapListBox);
+
+       me.gotoRC(me, me.rows - 4.5, me.firstColumn);
+               // string filter label and box
+               me.TD(me, 1, 0.35, e = makeXonoticTextLabel(1, _("Filter:")));
+               me.mapListBox.stringFilterBox = makeXonoticMapListStringFilterBox(me, 0, string_null);
+               me.mapListBox.stringFilterBox.tooltip = getZonedTooltipForIdentifier("XonoticMultiplayerDialog_StringFilterBox");
+               me.TD(me, 1, me.columns - me.firstColumn - 0.35, e = me.mapListBox.stringFilterBox);
+                       e.onChange = MapList_StringFilterBox_Change;
+                       e.keyDown = MapList_StringFilterBox_keyDown;
+                       e.onChangeEntity = me.mapListBox;
+                       me.mapListBox.controlledTextbox = e;
+
+       me.gotoRC(me, me.rows - 3.5, me.firstColumn);
+               // the selection buttons
+               me.TDempty(me, 0.2);
+               me.TD(me, 1, 1.3, e = makeXonoticButton(_("Add shown"), '0 0 0'));
+                       e.onClick = MapList_Add_Shown;
                        e.onClickEntity = me.mapListBox;
-               me.TD(me, 1, 1.125, e = makeXonoticButton(_("Select none"), '0 0 0'));
-                       e.onClick = MapList_None;
+               me.TD(me, 1, 1.3, e = makeXonoticButton(_("Remove shown"), '0 0 0'));
+                       e.onClick = MapList_Remove_Shown;
                        e.onClickEntity = me.mapListBox;
+       me.gotoRC(me, me.rows - 2.5, me.firstColumn);
+               me.TDempty(me, 0.2);
+               me.TD(me, 1, 1.3, e = makeXonoticButton(_("Add all"), '0 0 0'));
+                       e.onClick = MapList_Add_All;
+                       e.onClickEntity = me.mapListBox;
+               me.TD(me, 1, 1.3, e = makeXonoticButton(_("Remove all"), '0 0 0'));
+                       e.onClick = MapList_Remove_All;
+                       e.onClickEntity = me.mapListBox;
+
+       // The big button at the bottom
 
        me.gotoRC(me, me.rows - 1, 0);
                me.TD(me, 1, me.columns, e = makeXonoticButton(_("Start Multiplayer!"), '0 0 0'));
index f2749abb5ce17847a399e06d7f87a0fa872637b3..a4cc0565c31d3999f83b6f87edbf44619dfeb955 100644 (file)
@@ -33,6 +33,9 @@ CLASS(XonoticMapList, XonoticListBox)
        METHOD(XonoticMapList, g_maplistCacheToggle, void(entity, float))
        METHOD(XonoticMapList, g_maplistCacheQuery, float(entity, float))
 
+       ATTRIB(XonoticMapList, stringFilter, string, string_null)
+       ATTRIB(XonoticMapList, stringFilterBox, entity, NULL)
+
        ATTRIB(XonoticMapList, startButton, entity, NULL)
 
        METHOD(XonoticMapList, loadCvars, void(entity))
@@ -45,8 +48,13 @@ CLASS(XonoticMapList, XonoticListBox)
        ATTRIB(XonoticMapList, alphaBG, float, 0)
 ENDCLASS(XonoticMapList)
 entity makeXonoticMapList();
-void MapList_All(entity btn, entity me);
-void MapList_None(entity btn, entity me);
+entity makeXonoticMapListStringFilterBox(entity me, float doEditColorCodes, string theCvar);
+void MapList_StringFilterBox_Change(entity box, entity me);
+float MapList_StringFilterBox_keyDown(entity me, float key, float ascii, float shift);
+void MapList_Add_Shown(entity btn, entity me);
+void MapList_Remove_Shown(entity btn, entity me);
+void MapList_Add_All(entity btn, entity me);
+void MapList_Remove_All(entity btn, entity me);
 void MapList_LoadMap(entity btn, entity me);
 #endif
 
@@ -56,6 +64,10 @@ void XonoticMapList_destroy(entity me)
        MapInfo_Shutdown();
 }
 
+entity makeXonoticMapListStringFilterBox(entity me, float doEditColorCodes, string theCvar)
+{
+       return makeXonoticInputBox(doEditColorCodes, theCvar);
+}
 entity makeXonoticMapList()
 {
        entity me;
@@ -64,6 +76,12 @@ entity makeXonoticMapList()
        return me;
 }
 
+entity MapList_Set_String_Filter_Box(entity me, entity e)
+{
+       me.stringFilterBox = e;
+       return e;
+}
+
 void XonoticMapList_configureXonoticMapList(entity me)
 {
        me.configureXonoticListBox(me);
@@ -75,6 +93,7 @@ void XonoticMapList_loadCvars(entity me)
        me.refilter(me);
 }
 
+
 float XonoticMapList_g_maplistCacheQuery(entity me, float i)
 {
        return stof(substring(me.g_maplistCache, i, 1));
@@ -213,7 +232,10 @@ void XonoticMapList_refilter(entity me)
        gt = MapInfo_CurrentGametype();
        f = MapInfo_CurrentFeatures();
        MapInfo_FilterGametype(gt, f, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
+       if (me.stringFilter)
+               MapInfo_FilterString(me.stringFilter);
        me.nItems = MapInfo_count;
+
        for(i = 0; i < MapInfo_count; ++i)
                draw_PreloadPicture(strcat("/maps/", MapInfo_BSPName_ByID(i)));
        if(me.g_maplistCache)
@@ -226,11 +248,15 @@ void XonoticMapList_refilter(entity me)
        {
                j = MapInfo_FindName(argv(i));
                if(j >= 0)
-                       s = strcat(
-                               substring(s, 0, j),
-                               "1",
-                               substring(s, j+1, MapInfo_count - (j+1))
-                       );
+               {
+                       // double check that the two mapnames are "identical", not just share the same prefix
+                       if (strlen(MapInfo_BSPName_ByID(j)) == strlen(argv(i)))
+                               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)
@@ -246,7 +272,43 @@ void XonoticMapList_refilterCallback(entity me, entity cb)
        me.refilter(me);
 }
 
-void MapList_All(entity btn, entity me)
+void MapList_StringFilterBox_Change(entity box, entity me)
+{
+       if(me.stringFilter)
+               strunzone(me.stringFilter);
+       if(box.text != "")
+               me.stringFilter = strzone(box.text);
+       else
+               me.stringFilter = string_null;
+       
+       me.refilter(me);
+}
+
+void MapList_Add_Shown(entity btn, entity me)
+{
+       float i, n;
+       n = strlen(me.g_maplistCache);
+       for (i = 0 ; i < n; i++)
+       {
+               if (!me.g_maplistCacheQuery(me, i))
+                       me.g_maplistCacheToggle(me, i);
+       }
+       me.refilter(me);
+}
+
+void MapList_Remove_Shown(entity btn, entity me)
+{
+       float i, n;
+       n = strlen(me.g_maplistCache);
+       for (i = 0 ; i < n; i++)
+       {
+               if (me.g_maplistCacheQuery(me, i))
+                       me.g_maplistCacheToggle(me, i);
+       }
+       me.refilter(me);
+}
+
+void MapList_Add_All(entity btn, entity me)
 {
        float i;
        string s;
@@ -258,7 +320,7 @@ void MapList_All(entity btn, entity me)
        me.refilter(me);
 }
 
-void MapList_None(entity btn, entity me)
+void MapList_Remove_All(entity btn, entity me)
 {
        cvar_set("g_maplist", "");
        me.refilter(me);
@@ -367,9 +429,38 @@ float XonoticMapList_keyDown(entity me, float scan, float ascii, float shift)
                if(MapInfo_FindName_firstResult >= 0)
                        me.setSelected(me, MapInfo_FindName_firstResult);
        }
+       else if(shift & S_CTRL && scan == 'f') // ctrl-f (as in "F"ind)
+       {
+               me.parent.setFocus(me.parent, me.stringFilterBox);
+       }
+       else if(shift & S_CTRL && scan == 'u') // ctrl-u (remove stringFilter line
+       {
+               me.stringFilterBox.setText(me.stringFilterBox, "");
+               MapList_StringFilterBox_Change(me.stringFilterBox, me);
+       }
        else
                return SUPER(XonoticMapList).keyDown(me, scan, ascii, shift);
        return 1;
 }
 
+float MapList_StringFilterBox_keyDown(entity me, float scan, float ascii, float shift)
+{
+       // in this section, note that onChangeEntity has the ref to mapListBox
+       // we make use of that, instead of extending a class to add one more attrib
+       switch(scan)
+       {
+               case K_KP_ENTER:
+               case K_ENTER:
+                       // move the focus to the mapListBox
+                       me.onChangeEntity.parent.setFocus(me.onChangeEntity.parent, me.onChangeEntity);
+                       return 1;
+               case K_KP_UPARROW:
+               case K_UPARROW:
+               case K_KP_DOWNARROW:
+               case K_DOWNARROW:
+                       // pass the event to the mapListBox (to scroll up and down)
+                       return me.onChangeEntity.keyDown(me.onChangeEntity, scan, ascii, shift);
+       }
+       return SUPER(XonoticInputBox).keyDown(me, scan, ascii, shift);
+}
 #endif
index a0c06485b20cb4b950049f05c5bc00ed9a5be66c..02887e6cc94d30d64d700d058e578fd4fa6a57cc 100644 (file)
 \g_instagib\Players will be given the Minstanex, which is a railgun with infinite damage. If the player runs out of ammo, he will have 10 seconds to find some or if he fails to do so, face death. The secondary fire mode is a laser which does not inflict any damage and is good for doing trickjumps.
 \g_nix\No items Xonotic - instead of pickup items, everyone plays with the same weapon. After some time, a countdown will start, after which everyone will switch to another weapon.
 \g_nix_with_laser\Always carry the laser as an additional weapon in Nix
-\XonoticMultiplayerDialog/Select all\Select all maps
-\XonoticMultiplayerDialog/Select none\Unselect all maps
+
+\XonoticMultiplayerDialog_StringFilterBox\Click here or Ctrl-F to provide a keyword to narrow down the maplist above. Ctrl-Delete to clear; Enter when done.
+\XonoticMultiplayerDialog/Add shown\Add the maps shown in Maplist above to your selection
+\XonoticMultiplayerDialog/Remove shown\Remove the maps shown in Maplist above from your selection
+\XonoticMultiplayerDialog/Add all\Add every available map to your selection
+\XonoticMultiplayerDialog/Remove all\Remove all the maps from your selection
 
 
 \XonoticMultiplayerDialog/Timedemo\Benchmark how fast your computer can run the highlighted demo