Organize Options Menu Plugin v1.02b

bluebooth

Veteran
Veteran
Joined
Nov 12, 2015
Messages
95
Reaction score
115
First Language
English
Primarily Uses
N/A
Organize Options Menu Plugin v1.02c

Author: Michael Morris @Blue Booth Studios


 

Introduction


Organizes options menu into categories.  No more disorganized options menu!  Designed to hook in to option menu additions from other plugins with minimal changes.  Options are then sorted for each category in order of decreasing entry complexity (based on number of accepted input values).


Features
- Organizes options menu into three categories - Graphics, Audio, Gameplay.  


- Entries added to the options menu by other plugins are detected and automatically sorted into the appropriate section.


- Entries that cannot be successfully sorted into a category are placed in Gameplay.


- Entries for a category are sorted by decreasing order of complexity - scene changing options are listed first; followed by sliding options, and finally true/false options.


- Does not interfere with storing or retrieving values for any options in the options menu.


How to Use
- Copy script into your game js/plugins directory.


- Setup plugin options.


- This plugin needs to be very close to the TOP of your plugin list, because it alias functions to catch options from Yanfly, SRD, and other plugins.  If the plugin is placed near the bottom of your plugin list, then the options menu may appear to be missing options.


Requirements


This script has no external requirements.


Demo


No demo provided.


Script

//=============================================================================
// Bluebooth Plugins - Organized Options Menu
// BBS_OrgOptionsMenu.js
//=============================================================================

//=============================================================================
/*:
* @title Organized Options Menu
* @author Michael Morris (https://www.banned.url/bluebooth)
* @date Feb 13, 2016
* @filename BBS_OrgOptionsMenu.js
* If you enjoy my work, consider supporting me on banned.url!
*
* https://www.banned.url/bluebooth
*
* @plugindesc v1.02c Organizes options menu into categories. No more disorganized
* options menu! Designed to hook in to option menu additions from other plugins
* with minimal changes.
* Special Thanks to Tsukihime for all the help.
* Special Thanks to 'Ramza' Michael Sweeney for helping so much with testing.
*
* ============================================================================
* Terms of Use
* ============================================================================
* - Free for use in non-commercial projects with credits
* - Contact me for commercial use
*
* ============================================================================
* Parameters
* ============================================================================
* @param Graphics Option Text
* @desc Text to display for the graphics option item. Default: Graphics
* @default Graphics
*
* @param Audio Option Text
* @desc Text to display for the graphics option item. Default: Audio
* @default Audio
*
* @param Gameplay Option Text
* @desc Text to display for all other option item. Default: Gameplay
* @default Gameplay
*
* @param Debug Mode
* @desc Enable to activate console variable logging. Use for debugging odd behaviour.
* true to enable console variable logging.
* @default false
*
* @help
* ============================================================================
* Description
* ============================================================================
*
* Organizes options menu into categories. No more disorganized options menu! Designed to hook in
* to option menu additions from other plugins with minimal changes.
*
* ============================================================================
* Change Log
* ============================================================================
* 1.02c - SRD's Fullscreen option now sorts into Graphics menu.
* 1.02b - Sliders are now processed as separate category after scene change commands and before common option commands (true/false).
* Options are therefore organized in decreasing order of complexity.
* 1.02a - Input options moved to main options menu, as submenu caused scene creation to fail for them.
* 1.02 - Alphabetical sorting added by special and then normal commands.
* 1.01 - Plugin finished.
*
*/
//=============================================================================

//=============================================================================
var Imported = Imported || {} ;
var BBS = BBS || {};
Imported.OrgOptionsMenu = 1;
BBS.OrgOptionsMenu = BBS.OrgOptionsMenu || {};

(function() {

//=============================================================================
// Parameter Variables
//=============================================================================
var parameters = PluginManager.parameters('BBS_OrgOptionsMenu');
var pGrOptText = String(parameters['Graphics Option Text'] || 'Graphics');
var pAuOptText = String(parameters['Audio Option Text'] || 'Audio');
var pGaOptText = String(parameters['Gameplay Option Text'] || 'Gameplay');
var pDebugging = eval(String(parameters['Debug Mode'] || 'false'));

// Prepare array vars
var _allGrOptSymbols = ['animateTiles', 'battleCamera', 'synchFps', 'flashes', 'fullscreen'];
var _allAuOptSymbols = ['bgmVolume', 'bgsVolume', 'meVolume', 'seVolume', 'closedCaptioning'];
var _allGaOptSymbols = [];

// Controller/Keyboard Input members of gameplay.
var _allReOptSymbols = ['auConfig', 'gaConfig', 'gamepadConfig', 'keyConfig', 'grConfig'];

// The following commands all cause scene changes, and are not drawn or processed like other options.
var _allSceneChangeSymbols = ['auConfig', 'gaConfig', 'gamepadConfig', 'grConfig', 'keyConfig'];
var _allSliderSymbols = ['bgmVolume', 'bgsVolume', 'meVolume', 'seVolume'];

// Arrays for sorting and catching input options from other plugins.
var _grOptCommands = [];
var _auOptCommands = [];
var _gaOptCommands = [];

// For the sake of readability. Debugging this is hard enough already.
var searchArr = function(symbol, arr) {
if (pDebugging === true) { console.log("Searching: " + arr + " for " + symbol); }

for (var i=0; i < arr.length; i++) {
console.log("Comparing: " + arr.symbol + " vs " + symbol);
if (arr.symbol === symbol) {
if (pDebugging === true) { console.log("Found: " + symbol + " in " + arr + " at index: " + i); }
return i;
}
}

if (pDebugging === true) { console.log("Did not find: " + symbol + " in " + arr); }
return -1;
};

//=============================================================================
// Window_GrOptions
//=============================================================================
function Window_GrOptions() {
this.initialize.apply(this, arguments);
}

Window_GrOptions.prototype = Object.create(Window_Options.prototype);
Window_GrOptions.prototype.constructor = Window_GrOptions;

Window_GrOptions.prototype.initialize = function() {
Window_Options.prototype.initialize.call(this, 0, 0);
};

Window_GrOptions.prototype.makeCommandList = function() {
this.cmdFilter = _allGrOptSymbols;
this.addGraphicOptions();
};

Window_GrOptions.prototype.addGraphicOptions = function() {
var grSpecCommands = [];
var grSliderCommands = [];
var grNormCommands = [];

for (var i = 0; i < _grOptCommands.length; i++) {
if (_allSceneChangeSymbols.indexOf(_grOptCommands.symbol) != -1) {
grSpecCommands.push({ name: _grOptCommands.name, symbol: _grOptCommands.symbol, enabled: _grOptCommands.enabled, ext: _grOptCommands.ext });
} else if (_allSliderSymbols.indexOf(_grOptCommands.symbol) != -1) {
grSliderCommands.push({ name: _grOptCommands.name, symbol: _grOptCommands.symbol, enabled: _grOptCommands.enabled, ext: _grOptCommands.ext });
} else {
grNormCommands.push({ name: _grOptCommands.name, symbol: _grOptCommands.symbol, enabled: _grOptCommands.enabled, ext: _grOptCommands.ext });
}
}

// Sort
grSpecCommands.sort();
grSliderCommands.sort();
grNormCommands.sort();

// Scene change options always go first, in alphabetical order.
for (var i = 0; i < grSpecCommands.length; i++) {
this.addCommand(grSpecCommands.name, grSpecCommands.symbol, grSpecCommands.enabled, grSpecCommands.ext);
}

// Sliders go next, in alphabetical order.
for (var i = 0; i < grSliderCommands.length; i++) {
this.addCommand(grSliderCommands.name, grSliderCommands.symbol, grSliderCommands.enabled, grSliderCommands.ext);
}

// Remaining options go last, in alphabetical order.
for (var i = 0; i < grNormCommands.length; i++) {
this.addCommand(grNormCommands.name, grNormCommands.symbol, grNormCommands.enabled, grNormCommands.ext);
}
};

//=============================================================================
// Window_AuOptions
//=============================================================================
function Window_AuOptions() {
this.initialize.apply(this, arguments);
}

Window_AuOptions.prototype = Object.create(Window_Options.prototype);
Window_AuOptions.prototype.constructor = Window_AuOptions;

Window_AuOptions.prototype.initialize = function() {
Window_Options.prototype.initialize.call(this, 0, 0);
};

Window_AuOptions.prototype.makeCommandList = function() {
this.cmdFilter = _allAuOptSymbols;
this.addVolumeOptions();
};

Window_AuOptions.prototype.addVolumeOptions = function() {
var auSpecCommands = [];
var auSliderCommands = [];
var auNormCommands = [];

for (var i = 0; i < _auOptCommands.length; i++) {
if (_allSceneChangeSymbols.indexOf(_auOptCommands.symbol) != -1) {
auSpecCommands.push({ name: _auOptCommands.name, symbol: _auOptCommands.symbol, enabled: _auOptCommands.enabled, ext: _auOptCommands.ext });
}
else if (_allSliderSymbols.indexOf(_auOptCommands.symbol) != -1) {
auSliderCommands.push({ name: _auOptCommands.name, symbol: _auOptCommands.symbol, enabled: _auOptCommands.enabled, ext: _auOptCommands.ext });
} else {
auNormCommands.push({ name: _auOptCommands.name, symbol: _auOptCommands.symbol, enabled: _auOptCommands.enabled, ext: _auOptCommands.ext });
}
}

// Sort
auSpecCommands.sort();
auSliderCommands.sort();
auNormCommands.sort();

// Scene change options always go first, in alphabetical order.
for (var i = 0; i < auSpecCommands.length; i++) {
this.addCommand(auSpecCommands.name, auSpecCommands.symbol, auSpecCommands.enabled, auSpecCommands.ext);
}

// Sliders go next, in alphabetical order.
for (var i = 0; i < auSliderCommands.length; i++) {
this.addCommand(auSliderCommands.name, auSliderCommands.symbol, auSliderCommands.enabled, auSliderCommands.ext);
}

// Remaining options go last, in alphabetical order.
for (var i = 0; i < auNormCommands.length; i++) {
this.addCommand(auNormCommands.name, auNormCommands.symbol, auNormCommands.enabled, auNormCommands.ext);
}
};

//=============================================================================
// Window_GaOptions
//=============================================================================
function Window_GaOptions() {
this.initialize.apply(this, arguments);
}

Window_GaOptions.prototype = Object.create(Window_Options.prototype);
Window_GaOptions.prototype.constructor = Window_GaOptions;

Window_GaOptions.prototype.initialize = function() {
Window_Options.prototype.initialize.call(this, 0, 0);
};

Window_GaOptions.prototype.makeCommandList = function() {
this.cmdFilter = _allGaOptSymbols;
this.addGeneralOptions();
};

Window_GaOptions.prototype.addGeneralOptions = function() {
var gaSpecCommands = [];
var gaSliderCommands = [];
var gaNormCommands = [];

for (var i = 0; i < _gaOptCommands.length; i++) {
if (_allSceneChangeSymbols.indexOf(_gaOptCommands.symbol) != -1) {
gaSpecCommands.push({ name: _gaOptCommands.name, symbol: _gaOptCommands.symbol, enabled: _gaOptCommands.enabled, ext: _gaOptCommands.ext });
} else if (_allSliderSymbols.indexOf(_gaOptCommands.symbol) != -1) {
gaSliderCommands.push({ name: _gaOptCommands.name, symbol: _gaOptCommands.symbol, enabled: _gaOptCommands.enabled, ext: _gaOptCommands.ext });
} else {
gaNormCommands.push({ name: _gaOptCommands.name, symbol: _gaOptCommands.symbol, enabled: _gaOptCommands.enabled, ext: _gaOptCommands.ext });
}
}

// Sort
gaSpecCommands.sort();
gaSliderCommands.sort();
gaNormCommands.sort();

// Scene change options always go first, in alphabetical order.
for (var i = 0; i < gaSpecCommands.length; i++) {
this.addCommand(gaSpecCommands.name, gaSpecCommands.symbol, gaSpecCommands.enabled, gaSpecCommands.ext);
}

// Sliders go next, in alphabetical order.
for (var i = 0; i < gaSliderCommands.length; i++) {
this.addCommand(gaSliderCommands.name, gaSliderCommands.symbol, gaSliderCommands.enabled, gaSliderCommands.ext);
}

// Remaining options go last, in alphabetical order.
for (var i = 0; i < gaNormCommands.length; i++) {
this.addCommand(gaNormCommands.name, gaNormCommands.symbol, gaNormCommands.enabled, gaNormCommands.ext);
}
};

//=============================================================================
// Window_Options
//=============================================================================
BBS.OrgOptionsMenu.Window_Options_makeCommandList =
Window_Options.prototype.makeCommandList;
Window_Options.prototype.makeCommandList = function() {
this.cmdFilter = _allReOptSymbols;
this.addCommand(pGrOptText, 'grConfig');
this.addCommand(pAuOptText, 'auConfig');
this.addCommand(pGaOptText, 'gaConfig');

BBS.OrgOptionsMenu.Window_Options_makeCommandList.call(this);
};

BBS.OrgOptionsMenu.Window_Options_addCommand =
Window_Options.prototype.addCommand;
Window_Options.prototype.addCommand = function(name, symbol, enabled, ext) {

// Add data grab to get name and symbols from other plugins adding to Options menu, and then reorganize entries.

// Graphics
if (_allGrOptSymbols.indexOf(symbol) != -1) {
// Then this symbol is for a graphics option. Make sure it hasn't already been added to graphic options.
if (searchArr(symbol, _grOptCommands) === -1) {
_grOptCommands.push({ name: name, symbol: symbol, enabled: enabled, ext: ext });
}
}

// Audio
else if (_allAuOptSymbols.indexOf(symbol) != -1) {
// Then this symbol is for an audio option. Make sure it hasn't already been added to audio options.
if (searchArr(symbol, _auOptCommands) === -1) {
_auOptCommands.push({ name: name, symbol: symbol, enabled: enabled, ext: ext });
}
}

// Gameplay, if not reserved symbol
else if (_allReOptSymbols.indexOf(symbol) === -1) {
// Then this symbol is not one of the new options categories.
_allGaOptSymbols.push(symbol);

// Make sure it hasn't already been added to gameplay options.
if (searchArr(symbol, _gaOptCommands) === -1) {
_gaOptCommands.push({ name: name, symbol: symbol, enabled: enabled, ext: ext });
}
}

// Else: Item not sorted.
if (this.cmdFilter.indexOf(symbol) != -1) {
BBS.OrgOptionsMenu.Window_Options_addCommand.call(this, name, symbol, enabled, ext);
}
};

// Alias for scene redirects.
BBS.OrgOptionsMenu.Window_Options_drawItem =
Window_Options.prototype.drawItem;
Window_Options.prototype.drawItem = function(index) {
// If this option is for a command causing a scene change, it is drawn differently.
if(_allSceneChangeSymbols.indexOf(this.commandSymbol(index)) != -1) {
var rect = this.itemRectForText(index);
var text = this.commandName(index);
this.resetTextColor();
this.changePaintOpacity(this.isCommandEnabled(index));
this.drawText(text, rect.x, rect.y, rect.width, 'left');
} else {
BBS.OrgOptionsMenu.Window_Options_drawItem.call(this, index);
}
};

// Alias for scene redirects.
BBS.OrgOptionsMenu.Window_Options_processOk =
Window_Options.prototype.processOk;
Window_Options.prototype.processOk = function() {
// If this option is for a command causing a scene change, it is handled differently.
if(_allSceneChangeSymbols.indexOf(this.commandSymbol(this.index())) != -1) {
Window_Command.prototype.processOk.call(this);
}
else {
BBS.OrgOptionsMenu.Window_Options_processOk.call(this);
}
};

//=============================================================================
// Scene_Options
//=============================================================================
// Override for scene and command redirects.
Scene_Options.prototype.createOptionsWindow = function() {
console.log(_grOptCommands);
console.log(_auOptCommands);
console.log(_gaOptCommands);

this._optionsWindow = new Window_Options();
this._optionsWindow.setHandler('grConfig', this.commandGrConfig.bind(this));
this._optionsWindow.setHandler('auConfig', this.commandAuConfig.bind(this));
this._optionsWindow.setHandler('gaConfig', this.commandGaConfig.bind(this));
this._optionsWindow.setHandler('cancel', this.popScene.bind(this));
this.addWindow(this._optionsWindow);
};

Scene_Options.prototype.commandGrConfig = function() {
SceneManager.push(Scene_GrConfig);
};

Scene_Options.prototype.commandAuConfig = function() {
SceneManager.push(Scene_AuConfig);
};

Scene_Options.prototype.commandGaConfig = function() {
SceneManager.push(Scene_GaConfig);
};

//=============================================================================
// Scene_GrConfig
//=============================================================================
function Scene_GrConfig() {
this.initialize.apply(this, arguments);
}

Scene_GrConfig.prototype = Object.create(Scene_MenuBase.prototype);
Scene_GrConfig.prototype.constructor = Scene_GrConfig;

Scene_GrConfig.prototype.initialize = function() {
Scene_MenuBase.prototype.initialize.call(this);
};

Scene_GrConfig.prototype.create = function() {
Scene_MenuBase.prototype.create.call(this);
this.createGrOptionsWindow();
};

Scene_GrConfig.prototype.terminate = function() {
Scene_MenuBase.prototype.terminate.call(this);
ConfigManager.save();
};

Scene_GrConfig.prototype.createGrOptionsWindow = function() {
this._optionsWindow = new Window_GrOptions();
this._optionsWindow.setHandler('cancel', this.popScene.bind(this));
this.addWindow(this._optionsWindow);
};

//=============================================================================
// Scene_AuConfig
//=============================================================================
function Scene_AuConfig() {
this.initialize.apply(this, arguments);
}

Scene_AuConfig.prototype = Object.create(Scene_MenuBase.prototype);
Scene_AuConfig.prototype.constructor = Scene_AuConfig;

Scene_AuConfig.prototype.initialize = function() {
Scene_MenuBase.prototype.initialize.call(this);
};

Scene_AuConfig.prototype.create = function() {
Scene_MenuBase.prototype.create.call(this);
this.createAuOptionsWindow();
};

Scene_AuConfig.prototype.terminate = function() {
Scene_MenuBase.prototype.terminate.call(this);
ConfigManager.save();
};

Scene_AuConfig.prototype.createAuOptionsWindow = function() {
this._optionsWindow = new Window_AuOptions();
this._optionsWindow.setHandler('cancel', this.popScene.bind(this));
this.addWindow(this._optionsWindow);
};

//=============================================================================
// Scene_GaConfig
//=============================================================================
function Scene_GaConfig() {
this.initialize.apply(this, arguments);
}

Scene_GaConfig.prototype = Object.create(Scene_MenuBase.prototype);
Scene_GaConfig.prototype.constructor = Scene_GaConfig;

Scene_GaConfig.prototype.initialize = function() {
Scene_MenuBase.prototype.initialize.call(this);
};

Scene_GaConfig.prototype.create = function() {
Scene_MenuBase.prototype.create.call(this);
this.createGaOptionsWindow();
};

Scene_GaConfig.prototype.terminate = function() {
Scene_MenuBase.prototype.terminate.call(this);
ConfigManager.save();
};

Scene_GaConfig.prototype.createGaOptionsWindow = function() {
this._optionsWindow = new Window_GaOptions();
this._optionsWindow.setHandler('cancel', this.popScene.bind(this));
this.addWindow(this._optionsWindow);
};

})(BBS.OrgOptionsMenu);
//=============================================================================
// End of File
//=============================================================================






Change Log


1.02c


  - SRD's Fullscreen now sorts into Graphics menu.


1.02b


  - Sliders are now processed as separate category after scene change commands and before common option commands (true/false). 
  - Options are therefore organized in decreasing order of complexity.


1.02a


  - Input options moved to main options menu, as submenu caused scene creation to fail for them.


1.02


  - Alphabetical sorting added by special and then normal commands.
1.01


  - Plugin finished.


Known Bugs / TODO


Suggestions, bug reports, and feature requests are welcomed!


Compatibility Issues


None known.


Credit and Thanks
- Micheal Morris @Blue Booth Studios


- Yanfly for his help, and for checking this plugin is compatible with all of his.


Author's Notes
Free for non-commercial usage as long as credit is given.  Contact me for commercial usage.


View attachment BBS_OrgOptionsMenu.js
 
Last edited by a moderator:

BozeGame

Villager
Member
Joined
Sep 3, 2016
Messages
7
Reaction score
3
First Language
Thai
Primarily Uses
Nice ! I'll give it a try. Thanks :D


edit 1 : Seems like I've had a problem with the Graphics option. Not sure if it's because of the other of my plugin or this plugin itself. It said "TypeError : Cannot read property 'symbol' of undefined


  ,everything seems to be fine. Hope this helps :D
 
Last edited by a moderator:

bluebooth

Veteran
Veteran
Joined
Nov 12, 2015
Messages
95
Reaction score
115
First Language
English
Primarily Uses
N/A
Nice ! I'll give it a try. Thanks :D


edit 1 : Seems like I've had a problem with the Graphics option. Not sure if it's because of the other of my plugin or this plugin itself. It said "TypeError : Cannot read property 'symbol' of undefined


  ,everything seems to be fine. Hope this helps :D


Well now, there are many things that should be happening, but that's not one of them.


What options did you have on the options menu before activating the plugin?
 

BozeGame

Villager
Member
Joined
Sep 3, 2016
Messages
7
Reaction score
3
First Language
Thai
Primarily Uses
Well now, there are many things that should be happening, but that's not one of them.


What options did you have on the options menu before activating the plugin?
Always dash


Command Remember


Fullscreen(from SRD)


and 4 volume options


,After disable the fullscreen plugin It's still there
 

bluebooth

Veteran
Veteran
Joined
Nov 12, 2015
Messages
95
Reaction score
115
First Language
English
Primarily Uses
N/A
Okay, thanks!    I see what the issue is now.  The symbol for Fullscreen (SRD) isn't currently defined, so it doesn't know to include it as a Graphics option.  Therefore, the Graphics menu would be empty, resulting in the error you get.  Always dash and Command Remember are known Gameplay options, and the Volume options are all known, so those menus load without issue.  I can make a simple fix for this, but first I'll need more information on Fullscreen (SRD).


UPDATE: Done!  v1.02c is up.  Please let me know if the new version fixes your issue, @BozeGame. :)
 
Last edited by a moderator:

BozeGame

Villager
Member
Joined
Sep 3, 2016
Messages
7
Reaction score
3
First Language
Thai
Primarily Uses
 Please let me know if the new version fixes your issue, @BozeGame. :)
Yep. works fine now. but the people who doesn't install the fullscreen plugin may get that issue though.


And one more thing : To make Yanfly keyboard config plugin(other yanfly plugin maybe?) works correctly, this plugin needs to be arranged upper of Yanfly keyboard config plugin.
 

bluebooth

Veteran
Veteran
Joined
Nov 12, 2015
Messages
95
Reaction score
115
First Language
English
Primarily Uses
N/A
No worries, the plugin is set up so it will work correctly without the fullscreen plugin installed.  I'll look into the plausibility of adding a failsafe in the case of a menu with 0 sub items.


Good catch!  Yes, the plugin should be very close to the top of the plugin config, because it alias the option command added in Yanfly's and other plugins.
 

JoePie

of Art/Game
Veteran
Joined
Jun 9, 2014
Messages
170
Reaction score
226
First Language
Not English
Primarily Uses
Hey mate, this jump out when I go into option. Any idea what should I do to use this flawlessly?

2016-10-08 11_14_28-Developer Tools - file____D___work_Game_RPGames_HotDog_index.html_test.png
 

bluebooth

Veteran
Veteran
Joined
Nov 12, 2015
Messages
95
Reaction score
115
First Language
English
Primarily Uses
N/A
Sorry for the late reply.


Hey, @JoePie, yeah, I think I know what the issue is.  Can you tell me what the list of options is before using this plugin?
 

DMIgames

Veteran
Veteran
Joined
Nov 18, 2014
Messages
71
Reaction score
26
First Language
english
Primarily Uses
Seems to not work for the latest version of MV v_v it creates the categories but once you go into one it's all kinds of broken. Graphics displays nothing (even if you have fullscreen or other plugins activated), audio duplicates the few options and gameplay options are also duplicated v_v
 

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Posts

Latest Profile Posts

suspended on twitter for calling an ahole an ahole & appealing the tweet; furious at myself for being even slightly upset with my suspension: oh no, you've been temporarily banned from hell. GET OVER IT, me.
how can you download the tiles from a forum post here? HELP
That moment when you post the wrong download link to your game and wonder why it got so few downloads... :mad:
Trying to make it so my map is cleared of "random" events after the player leaves the map... to little success >.< I think I might need to ask help for this.
Had my first interaction with a legit homophobe concerning one of my games today. Tempting to engage, but pointless.

Forum statistics

Threads
94,245
Messages
919,368
Members
123,959
Latest member
CecilGP
Top