Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@spacechase0
Last active May 12, 2021 07:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save spacechase0/2d8d4dbffe5f2ce9457d2c891a8b99e3 to your computer and use it in GitHub Desktop.
Save spacechase0/2d8d4dbffe5f2ce9457d2c891a8b99e3 to your computer and use it in GitHub Desktop.
Generic Mod Config Menu example
// Your config class
public class DummyConfig
{
public bool dummyBool = true;
public int dummyInt1 = 50;
public int dummyInt2 = 50;
public float dummyFloat1 = 0.5f;
public float dummyFloat2 = 0.5f;
public string dummyString1 = "Kirby";
public string dummyString2 = "Default";
internal static string[] dummyString2Choices = new string[] { "Default", "Kitties", "Cats", "Meow" };
public SButton dummyKeybinding = SButton.K;
public KeybindList dummyKeybinding2 = new KeybindList( new Keybind( SButton.LeftShift, SButton.S ) );
public Color dummyColor = Color.White;
}
public DummyConfig config;
// Used for the complex widget
public class RandomColorWidgetState
{
public Color color;
}
// In your Entry method
Helper.Events.GameLoop.GameLaunched += onLaunched;
// In your mod entry class
private void onLaunched(object sender, GameLaunchedEventArgs e)
{
config = Helper.ReadConfig<DummyConfig>();
var api = Helper.ModRegistry.GetApi<GenericModConfigMenuAPI>("spacechase0.GenericModConfigMenu");
api.RegisterModConfig(ModManifest, () => config = new DummyConfig(), () => Helper.WriteConfig(config));
api.SetDefaultIngameOptinValue( ModManifest, true );
api.RegisterLabel(ModManifest, "Dummy Label", "Testing labels");
api.RegisterParagraph( ModManifest, "Testing paragraph text. These are smaller than labels and should wrap based on length. In theory. They should also (in theory) support multiple rows. Whether that will look good or not is another question. But hey, it looks like it worked! Imagine that. Should I support images in documentation, too?" );
api.RegisterImage( ModManifest, "Maps\\springobjects", new Rectangle( 32, 48, 16, 16 ) );
api.RegisterImage( ModManifest, "Portraits\\Penny", null, 1 );
api.SetDefaultIngameOptinValue( ModManifest, false );
api.RegisterPageLabel( ModManifest, "Go to page: Simple Options", "", "Simple Options" );
api.SetDefaultIngameOptinValue( ModManifest, true );
api.RegisterPageLabel( ModManifest, "Go to page: Complex Options", "", "Complex Options" );
api.SetDefaultIngameOptinValue( ModManifest, false );
api.StartNewPage( ModManifest, "Simple Options" );
api.RegisterPageLabel( ModManifest, "Back to main page", "", "" );
api.RegisterSimpleOption(ModManifest, "Dummy Bool", "Testing a checkbox", () => config.dummyBool, (bool val) => config.dummyBool = val);
api.RegisterSimpleOption(ModManifest, "Dummy Int (1)", "Testing an int (simple)", () => config.dummyInt1, (int val) => config.dummyInt1 = val);
api.RegisterClampedOption(ModManifest, "Dummy Int (2)", "Testing an int (range)", () => config.dummyInt2, (int val) => config.dummyInt2 = val, 0, 100);
api.RegisterSimpleOption(ModManifest, "Dummy Float (1)", "Testing a float (simple)", () => config.dummyFloat1, (float val) => config.dummyFloat1 = val);
api.RegisterClampedOption(ModManifest, "Dummy Float (2)", "Testing a float (range)", () => config.dummyFloat2, (float val) => config.dummyFloat2 = val, 0, 1);
api.SetDefaultIngameOptinValue( ModManifest, true );
api.StartNewPage( ModManifest, "Complex Options" );
api.RegisterPageLabel( ModManifest, "Back to main page", "", "" );
api.RegisterSimpleOption(ModManifest, "Dummy String (1)", "Testing a string", () => config.dummyString1, (string val) => config.dummyString1 = val);
api.RegisterChoiceOption(ModManifest, "Dummy String (2)", "Testing a dropdown box", () => config.dummyString2, (string val) => config.dummyString2 = val, DummyConfig.dummyString2Choices);
api.RegisterSimpleOption(ModManifest, "Dummy Keybinding", "Testing a keybinding", () => config.dummyKeybinding, (SButton val) => config.dummyKeybinding = val);
api.RegisterSimpleOption(ModManifest, "Dummy Keybinding 2", "Testing a keybinding list", () => config.dummyKeybinding2, (KeybindList val) => config.dummyKeybinding2 = val);
api.RegisterLabel(ModManifest, "", "");
// Complex widget - this just generates a random color on click.
Func<Vector2, object, object> randomColorUpdate =
(Vector2 pos, object state_) =>
{
var state = state_ as RandomColorWidgetState;
if (state == null)
state = new RandomColorWidgetState() { color = config.dummyColor };
var bounds = new Rectangle((int)pos.X + 12, (int)pos.Y + 12, 50 - 12 * 2, 50 - 12 * 2);
bool hover = bounds.Contains(Game1.getOldMouseX(), Game1.getOldMouseY());
if ( hover && Game1.oldMouseState.LeftButton == ButtonState.Released && Mouse.GetState().LeftButton == ButtonState.Pressed)
{
Game1.playSound("drumkit6");
Random r = new Random();
state.color.R = (byte) r.Next(256);
state.color.G = (byte) r.Next(256);
state.color.B = (byte) r.Next(256);
}
return state;
};
Func<SpriteBatch, Vector2, object, object> randomColorDraw =
(SpriteBatch b, Vector2 pos, object state_) =>
{
var state = state_ as RandomColorWidgetState;
IClickableMenu.drawTextureBox(b, (int)pos.X, (int)pos.Y, 50, 50, Color.White);
var colorBox = new Rectangle((int)pos.X + 12, (int)pos.Y + 12, 50 - 12 * 2, 50 - 12 * 2);
b.Draw(Game1.staminaRect, colorBox, state.color);
return state;
};
Action<object> randomColorSave =
(object state) =>
{
if ( state == null )
return;
config.dummyColor = (state as RandomColorWidgetState).color;
};
api.RegisterComplexOption(ModManifest, "Dummy Color", "Testing a complex widget (random color on click)", randomColorUpdate, randomColorDraw, randomColorSave);
}
public interface GenericModConfigMenuAPI
{
void RegisterModConfig(IManifest mod, Action revertToDefault, Action saveToFile);
void UnregisterModConfig(IManifest mod);
void SetDefaultIngameOptinValue( IManifest mod, bool optedIn );
void StartNewPage(IManifest mod, string pageName);
void OverridePageDisplayName(IManifest mod, string pageName, string displayName);
void RegisterLabel(IManifest mod, string labelName, string labelDesc);
void RegisterPageLabel( IManifest mod, string labelName, string labelDesc, string newPage );
void RegisterParagraph(IManifest mod, string paragraph);
void RegisterImage( IManifest mod, string texPath, Rectangle? texRect = null, int scale = 4 );
void RegisterSimpleOption(IManifest mod, string optionName, string optionDesc, Func<bool> optionGet, Action<bool> optionSet);
void RegisterSimpleOption(IManifest mod, string optionName, string optionDesc, Func<int> optionGet, Action<int> optionSet);
void RegisterSimpleOption(IManifest mod, string optionName, string optionDesc, Func<float> optionGet, Action<float> optionSet);
void RegisterSimpleOption(IManifest mod, string optionName, string optionDesc, Func<string> optionGet, Action<string> optionSet);
void RegisterSimpleOption(IManifest mod, string optionName, string optionDesc, Func<SButton> optionGet, Action<SButton> optionSet);
void RegisterSimpleOption(IManifest mod, string optionName, string optionDesc, Func<KeybindList> optionGet, Action<KeybindList> optionSet);
void RegisterClampedOption(IManifest mod, string optionName, string optionDesc, Func<int> optionGet, Action<int> optionSet, int min, int max);
void RegisterClampedOption(IManifest mod, string optionName, string optionDesc, Func<float> optionGet, Action<float> optionSet, float min, float max);
void RegisterClampedOption(IManifest mod, string optionName, string optionDesc, Func<int> optionGet, Action<int> optionSet, int min, int max, int interval);
void RegisterClampedOption(IManifest mod, string optionName, string optionDesc, Func<float> optionGet, Action<float> optionSet, float min, float max, float interval);
void RegisterChoiceOption(IManifest mod, string optionName, string optionDesc, Func<string> optionGet, Action<string> optionSet, string[] choices);
void RegisterComplexOption(IManifest mod, string optionName, string optionDesc,
Func<Vector2, object, object> widgetUpdate,
Func<SpriteBatch, Vector2, object, object> widgetDraw,
Action<object> onSave);
void SubscribeToChange(IManifest mod, Action<string, bool> changeHandler);
void SubscribeToChange(IManifest mod, Action<string, int> changeHandler);
void SubscribeToChange(IManifest mod, Action<string, float> changeHandler);
void SubscribeToChange(IManifest mod, Action<string, string> changeHandler);
void OpenModMenu(IManifest mod);
}
@pepoluan
Copy link

pepoluan commented May 12, 2021

For API.cs, I suggest adding the following on top:

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewModdingAPI.Utilities;

If not, Visual Studio will complain about missing references for IManifest, Rectangle, Vector2, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment