public override void Entry(IModHelper helper) { instance = this; Log.Monitor = Monitor; ui = new RootElement(); Texture2D tex = Helper.Content.Load <Texture2D>("assets/config-button.png"); configButton = new Button(tex); configButton.LocalPosition = new Vector2(36, Game1.viewport.Height - 100); configButton.Callback = (Element e) => { Game1.playSound("newArtifact"); TitleMenu.subMenu = new ModConfigMenu(false); }; ui.AddChild(configButton); helper.Events.GameLoop.GameLaunched += onLaunched; helper.Events.GameLoop.UpdateTicking += onUpdate; helper.Events.Display.WindowResized += onWindowResized; helper.Events.Display.Rendered += onRendered; helper.Events.Display.MenuChanged += onMenuChanged; helper.Events.Input.MouseWheelScrolled += onMouseWheelScrolled; }
public override void gameWindowSizeChanged(Rectangle oldBounds, Rectangle newBounds) { ui = new RootElement(); Vector2 newSize = new Vector2(800, Game1.viewport.Height - 128); table.LocalPosition = new Vector2((Game1.viewport.Width - 800) / 2, 64); foreach (Element opt in table.Children) { opt.LocalPosition = new Vector2(newSize.X / (table.Size.X / opt.LocalPosition.X), opt.LocalPosition.Y); } table.Size = newSize; table.Scrollbar.Update(); ui.AddChild(table); }
public ModConfigMenu() { ui = new RootElement(); table = new Table(); table.LocalPosition = new Vector2((Game1.viewport.Width - 800) / 2, 32); table.Size = new Vector2(800, Game1.viewport.Height - 64); table.RowHeight = 50; foreach (var modConfigEntry in Mod.instance.configs.OrderBy(pair => pair.Key.Name)) { var label = new Label() { String = modConfigEntry.Key.Name }; label.Callback = (Element e) => changeToModPage(modConfigEntry.Key); table.AddRow(new Element[] { label }); } ui.AddChild(table); }
public ModConfigMenu(bool inGame) { ingame = inGame; ui = new RootElement(); table = new Table(); table.RowHeight = 50; table.LocalPosition = new Vector2((Game1.viewport.Width - 800) / 2, 64); table.Size = new Vector2(800, Game1.viewport.Height - 128); var heading = new Label() { String = "Configure Mods", Bold = true }; heading.LocalPosition = new Vector2((800 - heading.Measure().X) / 2, heading.LocalPosition.Y); table.AddRow(new Element[] { heading }); foreach (var modConfigEntry in Mod.instance.configs.OrderBy(pair => pair.Key.Name)) { if (ingame && !modConfigEntry.Value.HasAnyInGame) { continue; } var label = new Label() { String = modConfigEntry.Key.Name }; label.Callback = (Element e) => changeToModPage(modConfigEntry.Key); table.AddRow(new Element[] { label }); } ui.AddChild(table); if (ingame || Constants.TargetPlatform == GamePlatform.Android) { initializeUpperRightCloseButton(); } ActiveConfigMenu = this; }
public override void gameWindowSizeChanged(Rectangle oldBounds, Rectangle newBounds) { ui = new RootElement(); Vector2 newSize = new Vector2(Game1.viewport.Width - 200, Game1.viewport.Height - 64 - 100); foreach (Element opt in table.Children) { opt.LocalPosition = new Vector2(newSize.X / (table.Size.X / opt.LocalPosition.X), opt.LocalPosition.Y); if (opt is Slider slider) { slider.Width = (int)(newSize.X / (table.Size.X / slider.Width)); } } table.Size = newSize; table.Scrollbar.Update(); ui.AddChild(table); addDefaultLabels(mod); }
public CustomCropMenu() : base((Game1.uiViewport.Width - WIDTH) / 2, (Game1.uiViewport.Height - HEIGHT) / 2, WIDTH, HEIGHT) { ui = new RootElement(); ui.LocalPosition = new Microsoft.Xna.Framework.Vector2(xPositionOnScreen, yPositionOnScreen); seedSlot = new ItemSlot() { ItemDisplay = new DummyItem(I18n.Menu_Seeds_Name(), I18n.Menu_Seeds_Description(), Game1.objectSpriteSheet, Game1.getSourceRectForStandardTileSheet(Game1.objectSpriteSheet, 472, 16, 16)), TransparentItemDisplay = true, }; seedSlot.LocalPosition = new(0, (HEIGHT - seedSlot.Height) / 2 - 125); seedSlot.UserData = (Func <Item, bool>)((item) => (item is StardewValley.Object sobj && sobj.Category == StardewValley.Object.SeedsCategory)); stuff = new StaticContainer() { LocalPosition = new((WIDTH - seedSlot.Width) / 2, 0), }; stuff.AddChild(seedSlot); ui.AddChild(stuff); var statsLebel = new Label() { String = I18n.Stats(), LocalPosition = new((WIDTH - seedSlot.Width) / 2 + seedSlot.Width, 8), }; statsLebel.LocalPosition = new(statsLebel.LocalPosition.X - statsLebel.Measure().X / 2, statsLebel.LocalPosition.Y); stuff.AddChild(statsLebel); stuff.AddChild(growthLabel = new Label() { String = I18n.Stats_Growth(0), LocalPosition = new(150, statsLebel.LocalPosition.Y + statsLebel.Measure().Y + 16), HoverTextColor = Color.Red, IdleTextColor = Color.Red, NonBoldScale = 0.5f, NonBoldShadow = false, });
public SpecificModConfigMenu(IManifest modManifest) { mod = modManifest; modConfig = Mod.instance.configs[mod]; table = new Table(); table.LocalPosition = new Vector2(200 / 2, 82); table.Size = new Vector2(Game1.viewport.Width - 200, Game1.viewport.Height - 64 - 100); table.RowHeight = 50; foreach (var opt in modConfig.Options) { opt.SyncToMod(); var label = new Label() { String = opt.Name }; label.UserData = opt.Description; if (opt.Description != null && opt.Description != "") { optHovers.Add(label); } Element other = new Label() { String = "TODO", LocalPosition = new Vector2(500, 0) }; Element other2 = null; if (opt is ComplexModOption c) { var custom = new ComplexModOptionWidget(c); custom.LocalPosition = new Vector2(table.Size.X / 5 * 3, 0); other = custom; } else if (opt is SimpleModOption <bool> b) { var checkbox = new Checkbox(); checkbox.LocalPosition = new Vector2(table.Size.X / 3 * 2, 0); checkbox.Checked = b.Value; checkbox.Callback = (Element e) => b.Value = (e as Checkbox).Checked; other = checkbox; } else if (opt is SimpleModOption <SButton> k) { var label2 = new Label() { String = k.Value.ToString() }; label2.LocalPosition = new Vector2(table.Size.X / 3 * 2, 0); label2.Callback = (Element e) => doKeybindingFor(k, e as Label); other = label2; } else if (opt is ClampedModOption <int> ci) { var label2 = new Label() { String = ci.Value.ToString() }; label2.LocalPosition = new Vector2(table.Size.X / 2 + table.Size.X / 3 + 50, 0); other2 = label2; var slider = new Slider <int>(); slider.LocalPosition = new Vector2(table.Size.X / 2, 0); slider.Width = (int)table.Size.X / 3; slider.Value = ci.Value; slider.Minimum = ci.Minimum; slider.Maximum = ci.Maximum; slider.Callback = (Element e) => { ci.Value = (e as Slider <int>).Value; label2.String = ci.Value.ToString(); }; other = slider; } else if (opt is ClampedModOption <float> cf) { var label2 = new Label() { String = cf.Value.ToString() }; label2.LocalPosition = new Vector2(table.Size.X / 2 + table.Size.X / 3 + 50, 0); other2 = label2; var slider = new Slider <float>(); slider.LocalPosition = new Vector2(table.Size.X / 2, 0); slider.Width = (int)table.Size.X / 3; slider.Value = cf.Value; slider.Minimum = cf.Minimum; slider.Maximum = cf.Maximum; slider.Callback = (Element e) => { cf.Value = (e as Slider <float>).Value; label2.String = cf.Value.ToString(); }; other = slider; } else if (opt is ChoiceModOption <string> cs) { var dropdown = new Dropdown() { Choices = cs.Choices }; dropdown.LocalPosition = new Vector2(table.Size.X / 7 * 4, 0); dropdown.Value = cs.Value; dropdown.Callback = (Element e) => cs.Value = (e as Dropdown).Value; other = dropdown; } // The following need to come after the Clamped/ChoiceModOption's since those subclass these else if (opt is SimpleModOption <int> i) { var intbox = new Intbox(); intbox.LocalPosition = new Vector2(table.Size.X / 5 * 3, 0); intbox.Value = i.Value; intbox.Callback = (Element e) => i.Value = (e as Intbox).Value; other = intbox; } else if (opt is SimpleModOption <float> f) { var floatbox = new Floatbox(); floatbox.LocalPosition = new Vector2(table.Size.X / 5 * 3, 0); floatbox.Value = f.Value; floatbox.Callback = (Element e) => f.Value = (e as Floatbox).Value; other = floatbox; } else if (opt is SimpleModOption <string> s) { var textbox = new Textbox(); textbox.LocalPosition = new Vector2(table.Size.X / 5 * 3, 0); textbox.String = s.Value; textbox.Callback = (Element e) => s.Value = (e as Textbox).String; other = textbox; } if (other2 == null) { table.AddRow(new Element[] { label, other }); } else { table.AddRow(new Element[] { label, other, other2 }); } } ui.AddChild(table); var titleLabel = new Label() { String = modManifest.Name }; titleLabel.LocalPosition = new Vector2((Game1.viewport.Width - titleLabel.Font.MeasureString(titleLabel.String).X) / 2, 12); titleLabel.HoverTextColor = titleLabel.IdleTextColor; ui.AddChild(titleLabel); var cancelLabel = new Label() { String = "Cancel" }; cancelLabel.LocalPosition = new Vector2(Game1.viewport.Width / 2 - 300, Game1.viewport.Height - 50); cancelLabel.Callback = (Element e) => TitleMenu.subMenu = new ModConfigMenu(); ui.AddChild(cancelLabel); var defaultLabel = new Label() { String = "Default" }; defaultLabel.LocalPosition = new Vector2(Game1.viewport.Width / 2 - 50, Game1.viewport.Height - 50); defaultLabel.Callback = (Element e) => revertToDefault(); ui.AddChild(defaultLabel); var saveLabel = new Label() { String = "Save" }; saveLabel.LocalPosition = new Vector2(Game1.viewport.Width / 2 + 200, Game1.viewport.Height - 50); saveLabel.Callback = (Element e) => save(); ui.AddChild(saveLabel); // We need to update widgets at least once so ComplexModOptionWidget's get initialized table.ForceUpdateEvenHidden(); }
public SpecificModConfigMenu(IManifest modManifest, string page = "", string prevPage = null) { mod = modManifest; modConfig = Mod.instance.configs[mod]; currPage = page; Mod.instance.configs[mod].ActiveDisplayPage = modConfig.Options[currPage]; table = new Table(); table.RowHeight = 50; table.Size = new Vector2(Math.Min(1200, Game1.viewport.Width - 200), Game1.viewport.Height - 128 - 116); table.LocalPosition = new Vector2((Game1.viewport.Width - table.Size.X) / 2, (Game1.viewport.Height - table.Size.Y) / 2); foreach (var opt in modConfig.Options[page].Options) { opt.SyncToMod(); var label = new Label() { String = opt.Name }; label.UserData = opt.Description; if (opt.Description != null && opt.Description != "") { optHovers.Add(label); } Element other = new Label() { String = "TODO", LocalPosition = new Vector2(500, 0) }; Element other2 = null; if (opt is ComplexModOption c) { var custom = new ComplexModOptionWidget(c); custom.LocalPosition = new Vector2(table.Size.X / 2, 0); other = custom; } else if (opt is SimpleModOption <bool> b) { var checkbox = new Checkbox(); checkbox.LocalPosition = new Vector2(table.Size.X / 2, 0); checkbox.Checked = b.Value; checkbox.Callback = (Element e) => b.Value = (e as Checkbox).Checked; other = checkbox; } else if (opt is SimpleModOption <SButton> k) { if (Constants.TargetPlatform == GamePlatform.Android) { continue; // TODO: Support virtual keyboard input. } var label2 = new Label() { String = k.Value.ToString() }; label2.LocalPosition = new Vector2(table.Size.X / 2, 0); label2.Callback = (Element e) => doKeybindingFor(k, e as Label); other = label2; } else if (opt is ClampedModOption <int> ci) { var label2 = new Label() { String = ci.Value.ToString() }; label2.LocalPosition = new Vector2(table.Size.X / 2 + table.Size.X / 3 + 50, 0); other2 = label2; var slider = new Slider <int>(); slider.LocalPosition = new Vector2(table.Size.X / 2, 0); slider.RequestWidth = (int)table.Size.X / 3; slider.Value = ci.Value; slider.Minimum = ci.Minimum; slider.Maximum = ci.Maximum; slider.Interval = ci.Interval; slider.Callback = (Element e) => { ci.Value = (e as Slider <int>).Value; label2.String = ci.Value.ToString(); }; other = slider; } else if (opt is ClampedModOption <float> cf) { var label2 = new Label() { String = cf.Value.ToString() }; label2.LocalPosition = new Vector2(table.Size.X / 2 + table.Size.X / 3 + 50, 0); other2 = label2; var slider = new Slider <float>(); slider.LocalPosition = new Vector2(table.Size.X / 2, 0); slider.RequestWidth = (int)table.Size.X / 3; slider.Value = cf.Value; slider.Minimum = cf.Minimum; slider.Maximum = cf.Maximum; slider.Interval = cf.Interval; slider.Callback = (Element e) => { cf.Value = (e as Slider <float>).Value; label2.String = cf.Value.ToString(); }; other = slider; } else if (opt is ChoiceModOption <string> cs) { var dropdown = new Dropdown() { Choices = cs.Choices }; dropdown.LocalPosition = new Vector2(table.Size.X / 2, 0); dropdown.RequestWidth = (int)table.Size.X / 2; dropdown.Value = cs.Value; dropdown.MaxValuesAtOnce = Math.Min(dropdown.Choices.Length, 5); dropdown.Callback = (Element e) => cs.Value = (e as Dropdown).Value; other = dropdown; } // The following need to come after the Clamped/ChoiceModOption's since those subclass these else if (opt is SimpleModOption <int> i) { if (Constants.TargetPlatform == GamePlatform.Android) { continue; // TODO: Support virtual keyboard input. } var intbox = new Intbox(); intbox.LocalPosition = new Vector2(table.Size.X / 2 - 8, 0); intbox.Value = i.Value; intbox.Callback = (Element e) => i.Value = (e as Intbox).Value; other = intbox; } else if (opt is SimpleModOption <float> f) { if (Constants.TargetPlatform == GamePlatform.Android) { continue; // TODO: Support virtual keyboard input. } var floatbox = new Floatbox(); floatbox.LocalPosition = new Vector2(table.Size.X / 2 - 8, 0); floatbox.Value = f.Value; floatbox.Callback = (Element e) => f.Value = (e as Floatbox).Value; other = floatbox; } else if (opt is SimpleModOption <string> s) { if (Constants.TargetPlatform == GamePlatform.Android) { continue; // TODO: Support virtual keyboard input. } var textbox = new Textbox(); textbox.LocalPosition = new Vector2(table.Size.X / 2 - 8, 0); textbox.String = s.Value; textbox.Callback = (Element e) => s.Value = (e as Textbox).String; other = textbox; } else if (opt is LabelModOption l) { label.LocalPosition = new Vector2(-8, 0); label.Bold = true; if (l.Name == "") { label = null; } other = null; } else if (opt is PageLabelModOption pl) { label.Bold = true; label.Callback = ( Element e ) => { if (TitleMenu.subMenu == this) { TitleMenu.subMenu = new SpecificModConfigMenu(mod, pl.NewPage, currPage); } else if (Game1.activeClickableMenu == this) { Game1.activeClickableMenu = new SpecificModConfigMenu(mod, pl.NewPage, currPage); } }; other = null; } else if (opt is ParagraphModOption p) { label.NonBoldScale = 0.75f; label.NonBoldShadow = false; other = null; string[] text = p.Name.Split(' '); label.String = text[0] + " "; for (int it = 1; it < text.Length; ++it) { string oldStr = label.String; label.String += text[it]; if (label.Measure().X >= table.Size.X) { label.String = oldStr + "\n" + text[it]; } if (it < text.Length - 1) { label.String += " "; } } string[] lines = label.String.Split('\n'); for (int il = 0; il < lines.Length; il += 2) { table.AddRow(new Element[] { new Label() { UserData = opt.Description, NonBoldScale = 0.75f, NonBoldShadow = false, String = lines[il + 0] + "\n" + (il + 1 >= lines.Length ? "" : lines[il + 1]) } }); continue; } continue; } else if (opt is ImageModOption t) { var tex = Game1.content.Load <Texture2D>(t.TexturePath); var imgSize = new Vector2(tex.Width, tex.Height); if (t.TextureRect.HasValue) { imgSize = new Vector2(t.TextureRect.Value.Width, t.TextureRect.Value.Height); } imgSize *= t.Scale; var localPos = new Vector2(table.Size.X / 2 - imgSize.X / 2, 0); var baseRectPos = new Vector2(t.TextureRect.HasValue ? t.TextureRect.Value.X : 0, t.TextureRect.HasValue ? t.TextureRect.Value.Y : 0); var texs = new List <Image>(); if (textures.ContainsKey(t.TexturePath)) { texs = textures[t.TexturePath]; } else { textures.Add(t.TexturePath, texs); } for (int ir = 0; ir < imgSize.Y / table.RowHeight; ++ir) { int section = Math.Min((int)(imgSize.Y / t.Scale), table.RowHeight); int baseY = ( int )(baseRectPos.Y + section * ir); if (baseY + section > baseRectPos.Y + imgSize.Y / t.Scale) { section = ( int )(baseRectPos.Y + imgSize.Y / t.Scale) - baseY; } var img = new Image() { Texture = tex, TextureRect = new Rectangle((int)baseRectPos.X, baseY, (int)imgSize.X / t.Scale, section), Scale = t.Scale, }; img.LocalPosition = localPos; texs.Add(img); table.AddRow(new Element[] { img }); } continue; } if (label == null) { table.AddRow(new Element[] { }); } else if (other == null) { table.AddRow(new Element[] { label }); } else if (other2 == null) { table.AddRow(new Element[] { label, other }); } else { table.AddRow(new Element[] { label, other, other2 }); } } ui.AddChild(table); addDefaultLabels(modManifest); // We need to update widgets at least once so ComplexModOptionWidget's get initialized table.ForceUpdateEvenHidden(); ActiveConfigMenu = this; Mod.instance.Helper.Content.AssetEditors.Add(this); }
public SpecificModConfigMenu(IManifest modManifest) { mod = modManifest; modConfig = Mod.instance.configs[mod]; table = new Table(); table.RowHeight = 50; table.Size = new Vector2(Math.Min(1200, Game1.viewport.Width - 200), Game1.viewport.Height - 128 - 116); table.LocalPosition = new Vector2((Game1.viewport.Width - table.Size.X) / 2, (Game1.viewport.Height - table.Size.Y) / 2); foreach (var opt in modConfig.Options) { opt.SyncToMod(); var label = new Label() { String = opt.Name }; label.UserData = opt.Description; if (opt.Description != null && opt.Description != "") { optHovers.Add(label); } Element other = new Label() { String = "TODO", LocalPosition = new Vector2(500, 0) }; Element other2 = null; if (opt is ComplexModOption c) { var custom = new ComplexModOptionWidget(c); custom.LocalPosition = new Vector2(table.Size.X / 2, 0); other = custom; } else if (opt is SimpleModOption <bool> b) { var checkbox = new Checkbox(); checkbox.LocalPosition = new Vector2(table.Size.X / 2, 0); checkbox.Checked = b.Value; checkbox.Callback = (Element e) => b.Value = (e as Checkbox).Checked; other = checkbox; } else if (opt is SimpleModOption <SButton> k) { if (Constants.TargetPlatform == GamePlatform.Android) { continue; // TODO: Support virtual keyboard input. } var label2 = new Label() { String = k.Value.ToString() }; label2.LocalPosition = new Vector2(table.Size.X / 2, 0); label2.Callback = (Element e) => doKeybindingFor(k, e as Label); other = label2; } else if (opt is ClampedModOption <int> ci) { var label2 = new Label() { String = ci.Value.ToString() }; label2.LocalPosition = new Vector2(table.Size.X / 2 + table.Size.X / 3 + 50, 0); other2 = label2; var slider = new Slider <int>(); slider.LocalPosition = new Vector2(table.Size.X / 2, 0); slider.RequestWidth = (int)table.Size.X / 3; slider.Value = ci.Value; slider.Minimum = ci.Minimum; slider.Maximum = ci.Maximum; slider.Interval = ci.Interval; slider.Callback = (Element e) => { ci.Value = (e as Slider <int>).Value; label2.String = ci.Value.ToString(); }; other = slider; } else if (opt is ClampedModOption <float> cf) { var label2 = new Label() { String = cf.Value.ToString() }; label2.LocalPosition = new Vector2(table.Size.X / 2 + table.Size.X / 3 + 50, 0); other2 = label2; var slider = new Slider <float>(); slider.LocalPosition = new Vector2(table.Size.X / 2, 0); slider.RequestWidth = (int)table.Size.X / 3; slider.Value = cf.Value; slider.Minimum = cf.Minimum; slider.Maximum = cf.Maximum; slider.Interval = cf.Interval; slider.Callback = (Element e) => { cf.Value = (e as Slider <float>).Value; label2.String = cf.Value.ToString(); }; other = slider; } else if (opt is ChoiceModOption <string> cs) { var dropdown = new Dropdown() { Choices = cs.Choices }; dropdown.LocalPosition = new Vector2(table.Size.X / 2, 0); dropdown.RequestWidth = (int)table.Size.X / 2; dropdown.Value = cs.Value; dropdown.MaxValuesAtOnce = Math.Min(dropdown.Choices.Length, 5); dropdown.Callback = (Element e) => cs.Value = (e as Dropdown).Value; other = dropdown; } // The following need to come after the Clamped/ChoiceModOption's since those subclass these else if (opt is SimpleModOption <int> i) { if (Constants.TargetPlatform == GamePlatform.Android) { continue; // TODO: Support virtual keyboard input. } var intbox = new Intbox(); intbox.LocalPosition = new Vector2(table.Size.X / 2 - 8, 0); intbox.Value = i.Value; intbox.Callback = (Element e) => i.Value = (e as Intbox).Value; other = intbox; } else if (opt is SimpleModOption <float> f) { if (Constants.TargetPlatform == GamePlatform.Android) { continue; // TODO: Support virtual keyboard input. } var floatbox = new Floatbox(); floatbox.LocalPosition = new Vector2(table.Size.X / 2 - 8, 0); floatbox.Value = f.Value; floatbox.Callback = (Element e) => f.Value = (e as Floatbox).Value; other = floatbox; } else if (opt is SimpleModOption <string> s) { if (Constants.TargetPlatform == GamePlatform.Android) { continue; // TODO: Support virtual keyboard input. } var textbox = new Textbox(); textbox.LocalPosition = new Vector2(table.Size.X / 2 - 8, 0); textbox.String = s.Value; textbox.Callback = (Element e) => s.Value = (e as Textbox).String; other = textbox; } else if (opt is LabelModOption l) { label.LocalPosition = new Vector2(-8, 0); label.Bold = true; if (l.Name == "") { label = null; } other = null; } if (label == null) { table.AddRow(new Element[] { }); } else if (other == null) { table.AddRow(new Element[] { label }); } else if (other2 == null) { table.AddRow(new Element[] { label, other }); } else { table.AddRow(new Element[] { label, other, other2 }); } } ui.AddChild(table); addDefaultLabels(modManifest); // We need to update widgets at least once so ComplexModOptionWidget's get initialized table.ForceUpdateEvenHidden(); ActiveConfigMenu = this; }
public MarryMenu() : base((Game1.uiViewport.Width - 800) / 2, (Game1.uiViewport.Height - 700) / 2, 800, 700) { var valid = new List <NPC>(); foreach (var npc in Utility.getAllCharacters()) { if (npc.datable.Value && npc.getSpouse() == null) { valid.Add(npc); } } valid.Sort((a, b) => a.Name.CompareTo(b.Name)); /* * for ( int i = 0; i < valid.Count; ++i ) * { * int oi = Game1.random.Next( valid.Count ); * var other = valid[ oi ]; * valid[ oi ] = valid[ i ]; * valid[ i ] = other; * } */ ui = new RootElement() { LocalPosition = new Vector2(xPositionOnScreen, yPositionOnScreen), }; var title = new Label() { String = Mod.instance.Helper.Translation.Get("menu.title"), Bold = true, }; title.LocalPosition = new Vector2((800 - title.Measure().X) / 2, 10); ui.AddChild(title); ui.AddChild(new Label() { String = Mod.instance.Helper.Translation.Get("menu.text").ToString().Replace("\\n", "\n"), LocalPosition = new Vector2(50, 75), NonBoldScale = 0.75f, NonBoldShadow = false, }); table = new Table() { RowHeight = 200, Size = new Vector2(700, 500), LocalPosition = new Vector2(50, 225), }; for (int i = 0; i < (valid.Count + 2) / 3; ++i) { var row = new StaticContainer(); for (int n_ = i * 3; n_ < (i + 1) * 3; ++n_) { if (n_ >= valid.Count) { continue; } int n = n_; var cont = new StaticContainer() { Size = new Vector2(115 * 2, 97 * 2), LocalPosition = new Vector2(250 * (n - i * 3) - 10, 0), }; // Note: This is being called 4 times for some reason // Probably a UI framework bug. Action <Element> selCallback = (e) => { if (selectedContainer != null) { selectedContainer.OutlineColor = null; } selectedContainer = cont; selectedContainer.OutlineColor = Color.Green; selectedNPC = valid[n].Name; Log.trace("Selected " + selectedNPC); }; cont.AddChild(new Image() { Texture = Game1.mouseCursors, TextureRect = new Rectangle(583, 411, 115, 97), Scale = 2, LocalPosition = new Vector2(0, 0), Callback = selCallback, }); cont.AddChild(new Image() { Texture = valid[n].Portrait, TextureRect = new Rectangle(0, 128, 64, 64), Scale = 2, LocalPosition = new Vector2(50, 16), }); var name = new Label() { String = valid[n].displayName, NonBoldScale = 0.5f, NonBoldShadow = false, }; name.LocalPosition = new Vector2(115 - name.Measure().X / 2, 160); cont.AddChild(name); row.AddChild(cont); } table.AddRow(new Element[] { row }); } ui.AddChild(table); ui.AddChild(new Label() { String = Mod.instance.Helper.Translation.Get("menu.button.cancel"), LocalPosition = new Vector2(175, 650), Callback = (e) => Game1.exitActiveMenu(), }); ui.AddChild(new Label() { String = Mod.instance.Helper.Translation.Get("menu.button.accept"), LocalPosition = new Vector2(500, 650), Callback = (e) => { DoMarriage(); } }); }