private void AddGenericModConfigMenu() { IGenericModConfigMenuAPI modconfigAPI = this.Helper.ModRegistry.GetApi <IGenericModConfigMenuAPI>("spacechase0.GenericModConfigMenu"); if (modconfigAPI != null) { modconfigAPI.RegisterModConfig( mod: this.ModManifest, revertToDefault: () => ModEntry.Config = new Config(), saveToFile: () => this.Helper.WriteConfig(ModEntry.Config)); modconfigAPI.SetDefaultIngameOptinValue( mod: this.ModManifest, optedIn: true); System.Reflection.PropertyInfo[] properties = ModEntry.Config .GetType() .GetProperties() .Where(p => p.PropertyType == typeof(bool)) .ToArray(); foreach (System.Reflection.PropertyInfo property in properties) { string key = property.Name.ToLower(); string description = Translations.GetTranslation($"config.{key}.description", defaultToNull: true); modconfigAPI.RegisterSimpleOption( mod: this.ModManifest, optionName: Translations.GetTranslation($"config.{key}.name"), optionDesc: string.IsNullOrWhiteSpace(description) ? null : description, optionGet: () => (bool)property.GetValue(ModEntry.Config), optionSet: (bool value) => property.SetValue(ModEntry.Config, value: value)); } } }
/// <summary> /// Return the display name for an item definition in the <see cref="Translations.ItemTranslations"/> dictionary. /// </summary> /// <param name="data">Item definition entry.</param> public static string GetNameTranslation(ItemDefinition data) { string pack = data.ContentPack.Manifest.UniqueID; string item = data.LocalName; foreach (LocalizedContentManager.LanguageCode lc in Translations.LanguageCodesToTry) { Dictionary <string, Dictionary <string, string> > packs; Dictionary <string, string> items; string translation; if (Translations.ItemTranslations.TryGetValue(lc.ToString(), out packs) && packs != null && packs.TryGetValue(pack, out items) && items != null && items.TryGetValue(item, out translation) && !string.IsNullOrWhiteSpace(translation)) { return(Translations.GetTranslation("item.name.variant", tokens: new[] { translation ?? data.LocalName })); } } return(data.LocalName); }
public NewRecipeMenu(List <string> variantKeys) : base(x: 0, y: 0, width: 0, height: 0) { Log.T($"Opened end of night menu: {this.GetType().FullName}"); Game1.player.team.endOfNightStatus.UpdateState(ModEntry.EndOfNightState); this.VariantKeys = variantKeys; this.width = (int)Dimensions.X; this.height = ((int)Dimensions.Y / 2) + (this.VariantKeys.Count * Game1.smallestTileSize * 3 / 2 * Game1.pixelZoom); this.OkButton = new ClickableTextureComponent( bounds: Rectangle.Empty, texture: Game1.mouseCursors, sourceRect: Game1.getSourceRectForStandardTileSheet(Game1.mouseCursors, 46), scale: 1f) { myID = NewRecipeMenu.OkButtonId }; this._isActive = true; this._timerBeforeStart = 250; Game1.player.completelyStopAnimatingOrDoingAction(); Game1.player.freezePause = 100; this.gameWindowSizeChanged(Rectangle.Empty, Rectangle.Empty); this.populateClickableComponentList(); string craftingString = Game1.content.LoadString("Strings\\UI:LearnedRecipe_crafting"); this._titleString = Translations.GetTranslation("menu.title.new"); this._itemStrings = this.VariantKeys .ToDictionary( vk => vk, vk => Game1.content.LoadString("Strings\\UI:LevelUp_NewRecipe", craftingString, OutdoorPot.GetDisplayNameFromVariantKey(variantKey: vk))); this._itemSprites = this.VariantKeys .ToDictionary( vk => vk, vk => OutdoorPot.GetSpriteFromVariantKey(variantKey: vk)); }
public void Edit <T>(IAssetData asset) { /********* * Local data *********/ if (asset.AssetNameEquals(GameContentEventDataPath)) { var events = ((Newtonsoft.Json.Linq.JArray)asset .AsDictionary <string, object>() .Data["Events"]) .ToObject <List <Dictionary <string, string> > >(); // Events are populated with preset tokens and script dialogues depending on game locale. // Root event tokenisation for (int i = 0; i < events.Count; ++i) { // Format event script with event NPC name, as well as their dialogue strings string[] args = new string[] { events[i]["Who"] } .Concat(new int[] { 1, 2, 3, 4 } .Select(j => Translations.GetTranslation($"event.{i}.dialogue.{j}"))) .ToArray(); events[i]["Script"] = string.Format( format: events[i]["Script"], args: args); events[i]["Conditions"] = string.Format( format: events[i]["Conditions"], events[i]["Who"]); } Log.T($"Loaded {events.Count} event(s).{Environment.NewLine}Root event: {events[0]["Where"]}/{events[0]["Conditions"]}"); ModEntry.EventData = events; return; } /******** * Game data ********/ int id = OutdoorPot.BaseParentSheetIndex; if (asset.AssetNameEquals(Path.Combine("Data", "BigCraftablesInformation"))) { if (ModEntry.ItemDefinitions == null) { return; } string[] fields; var data = asset.AsDictionary <int, string>().Data; // Set or reset the item ID for the generic object to some first best available index id = OutdoorPot.BaseParentSheetIndex = data.Keys.Max() + 2; string name, description; // Patch generic object entry into bigcraftables file, including display name and description from localisations file name = Translations.GetTranslation("item.name"); description = Translations.GetTranslation("item.description.default"); fields = data.First().Value.Split('/'); // Use existing data as a template; most fields are common or unused fields[0] = OutdoorPot.GenericName; fields[4] = description; fields[8] = name; data[id] = string.Join("/", fields); // Patch in dummy object entries after generic object entry for (int i = 1; i < ModEntry.ItemDefinitions.Count; ++i) { ItemDefinition d = ModEntry.ItemDefinitions[ModEntry.ItemDefinitions.Keys.ElementAt(i)]; name = Translations.GetNameTranslation(data: d); fields = data[id].Split('/'); fields[4] = description; fields[8] = name; data[id + i] = string.Join("/", fields); } // Don't remove the generic craftable from data lookup, since it's used later for crafting recipes and defaults return; } if (asset.AssetNameEquals(Path.Combine("Data", "CraftingRecipes"))) { if (ModEntry.ItemDefinitions == null || id < 0) { return; } // As above for the craftables dictionary, the recipes dictionary needs to have // our varieties patched in to have them appear. // Since all objects share a single ParentSheetIndex, each crafting recipe will normally // only produce a generic/wooden object. // This is handled in HarmonyPatches.CraftingPage_ClickCraftingRecipe_Prefix, which // is also needed to produce an OutdoorPot instance rather than a StardewValley.Object. // Add crafting recipes for all object variants var data = asset.AsDictionary <string, string>().Data; foreach (KeyValuePair <string, ItemDefinition> idAndFields in ModEntry.ItemDefinitions) { string[] newFields = new string[] { // Crafting ingredients: ItemDefinition.ParseRecipeIngredients(data: idAndFields.Value), // Unused field: "blue berry", // Crafted item ID and quantity: $"{OutdoorPot.BaseParentSheetIndex} {idAndFields.Value.RecipeCraftedCount}", // Recipe is bigCraftable: "true", // Recipe conditions (we ignore these): "blue berry", // Recipe display name: Translations.GetNameTranslation(data: idAndFields.Value) }; data[OutdoorPot.GetNameFromVariantKey(idAndFields.Key)] = string.Join("/", newFields); } return; } if (asset.AssetName.StartsWith(Path.Combine("Data", "Events")) && Path.GetFileNameWithoutExtension(asset.AssetName) is string where) { // Patch our event data into whatever location happens to match the one specified. // Event tokenisation is handled in the Edit block for GameContentEventDataPath. if (ModEntry.EventData != null && ModEntry.EventData.FirstOrDefault(e => e["Where"] == where) is Dictionary <string, string> eventData) { string key = $"{ModEntry.EventRootId}{ModEntry.EventData.IndexOf(eventData)}/{eventData["Conditions"]}"; asset.AsDictionary <string, string>().Data[key] = eventData["Script"]; } return; } }