private IEnumerable <AnimalSkin> LoadSkins() { foreach (FileInfo file in new DirectoryInfo(Path.Combine(this.Helper.DirectoryPath, "assets", "skins")).EnumerateFiles()) { // check extension string extension = Path.GetExtension(file.Name); if (!this.ValidExtensions.Contains(extension)) { this.Monitor.Log($"Ignored skin `assets/skins/{file.Name}` with invalid extension (must be one of {string.Join(", ", this.ValidExtensions)}).", LogLevel.Warn); continue; } // parse name string[] parts = Path.GetFileNameWithoutExtension(file.Name).Split(new[] { '_' }, 2); if (!AnimalSkin.TryParseType(parts[0], out AnimalType type)) { this.Monitor.Log($"Ignored skin `assets/skins/{file.Name}` with invalid naming convention (can't parse '{parts[0]}' as an animal type, expected one of {string.Join(", ", Enum.GetNames(typeof(AnimalType)))}).", LogLevel.Warn); continue; } int index = 0; if (parts.Length == 2 && !int.TryParse(parts[1], out index)) { this.Monitor.Log($"Ignored skin `assets/skins/{file.Name}` with invalid naming convention (can't parse '{parts[1]}' as a number).", LogLevel.Warn); continue; } // yield string assetKey = this.Helper.Content.GetActualAssetKey(Path.Combine("assets", "skins", file.Name)); yield return(new AnimalSkin(type, index, assetKey)); } }
/********* ** Public methods *********/ public AdoptQuestion(AnimalSkin skin) { this.Skin = skin; this.Sprite = new AnimatedSprite(skin.AssetKey, 28, 32, 32) { loop = true }; }
/// <summary>Raised after the game state is updated (≈60 times per second).</summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event arguments.</param> private void OnUpdateTicked(object sender, UpdateTickedEventArgs e) { if (!Context.IsPlayerFree) { return; } if (!SkinsReady) { this.LoadSkins(); } // patch bus stop if (this.ReplaceBus && Game1.getLocationFromName("BusStop")?.map.Properties.ContainsKey("MA.Patched") == false) { MoreEvents.ActionTriggered += this.OnActionTriggered; this.Monitor.Log("Patching bus stop...", LogLevel.Trace); GameLocation bus = Game1.getLocationFromName("BusStop"); bus.map.Properties.Add("MA.Patched", true); bus.map.AddTileSheet(new TileSheet("MorePetsTilesheet", bus.map, this.Helper.Content.GetActualAssetKey("assets/box.png"), new Size(2, 2), new Size(16, 16))); bus.SetTile(1, 2, "Front", 0, "MorePetsTilesheet"); bus.SetTile(2, 2, "Front", 1, "MorePetsTilesheet"); bus.SetTile(1, 3, "Buildings", 2, "MorePetsTilesheet"); bus.SetTile(2, 3, "Buildings", 3, "MorePetsTilesheet"); bus.SetTileProperty(1, 3, "Buildings", "Action", "MorePetsAdoption"); bus.SetTileProperty(2, 3, "Buildings", "Action", "MorePetsAdoption"); } // set pet skins foreach (Pet pet in GetAllPets()) { if (pet.Manners > 0) { AnimalSkin skin = this.GetSkin(pet); if (skin != null && pet.Sprite.textureName.Value != skin.AssetKey) { pet.Sprite = new AnimatedSprite(skin.AssetKey, 0, 32, 32); pet.Manners = skin.ID; } } } // set farm animal skins foreach (FarmAnimal animal in this.GetFarmAnimals()) { AnimalSkin skin = this.GetSkin(animal); if (skin != null && animal.Sprite.textureName.Value != skin.AssetKey) { animal.Sprite = new AnimatedSprite(skin.AssetKey, 0, animal.frontBackSourceRect.Width, animal.frontBackSourceRect.Height); SkinMap[animal.myID.Value] = skin.ID; } else if (skin == null) { SkinMap[animal.myID.Value] = 0; } } }
/// <summary>Refreshes the texture of the creature's sprite if the texture it has is different from the one in the skin mapping</summary> internal static void UpdateSkin(Character creature) { if (!ModApi.IsInDatabase(creature) && !ModApi.IsStray(creature) && !ModApi.IsWildHorse(creature)) { return; } AnimalSkin skin = GetSkin(creature); if (skin != null && creature.Sprite.textureName.Value != skin.AssetKey) { int[] spriteInfo = ModApi.GetSpriteInfo(creature); creature.Sprite = new AnimatedSprite(skin.AssetKey, spriteInfo[0], spriteInfo[1], spriteInfo[2]); } }
private void GameEvents_UpdateTick(object s, EventArgs e) { if (!Context.IsWorldReady) { return; } // patch bus stop if (this.ReplaceBus && Game1.getLocationFromName("BusStop") != null) { this.Monitor.Log("Patching bus stop...", LogLevel.Trace); GameLocation bus = Game1.getLocationFromName("BusStop"); bus.map.AddTileSheet(new TileSheet("MorePetsTilesheet", bus.map, this.Helper.Content.GetActualAssetKey("assets/box.png"), new Size(2, 2), new Size(16, 16))); bus.SetTile(1, 2, "Front", 0, "MorePetsTilesheet"); bus.SetTile(2, 2, "Front", 1, "MorePetsTilesheet"); bus.SetTile(1, 3, "Buildings", 2, "MorePetsTilesheet"); bus.SetTile(2, 3, "Buildings", 3, "MorePetsTilesheet"); bus.SetTileProperty(1, 3, "Buildings", "Action", "MorePetsAdoption"); bus.SetTileProperty(2, 3, "Buildings", "Action", "MorePetsAdoption"); this.ReplaceBus = false; } // set pet skins foreach (Pet pet in this.GetAllPets()) { if (pet.Manners > 0 && !pet.updatedDialogueYet) { AnimalSkin skin = this.GetSkin(pet); if (skin != null) { pet.Sprite = new AnimatedSprite(skin.AssetKey, 0, 32, 32); pet.Manners = skin.ID; } pet.updatedDialogueYet = true; } } // set farm animal skins foreach (FarmAnimal animal in this.GetFarmAnimals()) { AnimalSkin skin = this.GetSkin(animal); if (skin != null && animal.Sprite.textureName.Value != skin.AssetKey) { animal.Sprite = new AnimatedSprite(skin.AssetKey, 0, animal.frontBackSourceRect.Width, animal.frontBackSourceRect.Height); animal.meatIndex.Value = skin.ID + 999; } } }
/// <summary>Raised after the game is launched, right before the first update tick. This happens once per game session (unrelated to loading saves). All mods are loaded and initialised at this point, so this is a good time to set up mod integrations.</summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event data.</param> private void OnGameLaunched(object sender, GameLaunchedEventArgs e) { // Prepare BabyDuck override try { string asset = this.Helper.Content.GetActualAssetKey(Path.Combine("assets", "skins", "BabyDuck.png")); Game1.content.Load <Texture2D>(asset); BabyDuck = new AnimalSkin("BabyDuck", 0, asset); } catch (Exception ex) { this.Monitor.Log("Unable to patch BabyDuck due to exception:", LogLevel.Error, ex); } // Register default supported animal types Api.RegisterAnimalType("Blue Chicken"); Api.RegisterAnimalType("Brown Chicken"); Api.RegisterAnimalType("Brown Cow"); Api.RegisterAnimalType("Dinosaur", false); Api.RegisterAnimalType("Duck"); Api.RegisterAnimalType("Goat"); Api.RegisterAnimalType("Pig"); Api.RegisterAnimalType("Rabbit"); Api.RegisterAnimalType("Sheep", true, true); Api.RegisterAnimalType("Void Chicken"); Api.RegisterAnimalType("White Chicken"); Api.RegisterAnimalType("White Cow"); if (Config.ExtraTypes != null && Config.ExtraTypes.Length > 0) { foreach (string type in Config.ExtraTypes) { Api.RegisterAnimalType(type, false); } } // Register default supported pet types Api.RegisterPetType("cat", typeof(Cat)); Api.RegisterPetType("dog", typeof(Dog)); // Trigger setup this.DoSetup(); }
/// <summary>Refreshes the given animal, pet, or horse's skin texture with the one Adopt & Skin has saved for it.</summary> internal void UpdateSkin(Character creature) { switch (creature) { case Horse horse: if (Creator.HorseInfo != null && horse.Manners == WildHorse.WildID) { horse.Sprite = new AnimatedSprite(GetSkinFromID(horse.GetType().Name, Creator.HorseInfo.SkinID).AssetKey, 0, 32, 32); break; } AnimalSkin horseSkin = GetSkin(horse); if (horseSkin != null && horse.Sprite.textureName.Value != horseSkin.AssetKey) { horse.Sprite = new AnimatedSprite(horseSkin.AssetKey, 7, 32, 32); } break; case Pet pet: if (Creator.StrayInfo != null && pet.Manners == Stray.StrayID) { pet.Sprite = new AnimatedSprite(GetSkinFromID(pet.GetType().Name, Creator.StrayInfo.SkinID).AssetKey, 28, 32, 32); break; } AnimalSkin petSkin = GetSkin(pet); if (petSkin != null && pet.Sprite.textureName.Value != petSkin.AssetKey) { pet.Sprite = new AnimatedSprite(petSkin.AssetKey, 28, 32, 32); } break; case FarmAnimal animal: AnimalSkin animalSkin = GetSkin(animal); if (animalSkin != null && animal.Sprite.textureName.Value != animalSkin.AssetKey) { animal.Sprite = new AnimatedSprite(animalSkin.AssetKey, 0, animal.frontBackSourceRect.Width, animal.frontBackSourceRect.Height); } break; default: break; } }
/// <summary>Get the skin to apply for an animal.</summary> /// <param name="npc">The animal to check.</param> private AnimalSkin GetSkin(Character npc) { switch (npc) { case Pet pet: return(this.GetSkin(type: pet is Dog ? AnimalType.Dog : AnimalType.Cat, index: pet.Manners)); case FarmAnimal animal: { // get type string typeStr = animal.type.Value; if (animal.age.Value < animal.ageWhenMature.Value) { typeStr = $"Baby{animal.type.Value}"; } else if (animal.showDifferentTextureWhenReadyForHarvest.Value && animal.currentProduce.Value <= 0) { typeStr = $"Sheared{animal.type.Value}"; } if (!AnimalSkin.TryParseType(typeStr, out AnimalType type)) { return(null); } // get index int index = animal.meatIndex.Value > 999 ? animal.meatIndex.Value - 999 : -1; // get skin return(this.GetSkin(type: type, index: index)); } default: return(null); } }
/// <summary>Get the skin to apply for an animal.</summary> /// <param name="type">The animal type.</param> /// <param name="index">The animal index.</param> private AnimalSkin GetSkin(string type, int index) { type = Sanitize(type); if (!ModEntry.Animals.TryGetValue(type, out List <AnimalSkin> skins) && !ModEntry.Pets.TryGetValue(type, out skins)) { return(null); } // get assigned skin if (index >= 1) { AnimalSkin skin = skins.FirstOrDefault(p => p.ID == index); if (skin != null) { return(skin); } this.Monitor.Log($"Found animal type {type} with index {index}, but there's no matching file. Reassigning a random skin to that animal."); } // get random skin int skinID = ModEntry.Animals.ContainsKey(type) ? this.SkinRandom.Next(skins.Count + 1) - 1 : this.SkinRandom.Next(skins.Count); return(skinID >= 0 ? skins[skinID] : null); }
/********* ** Public methods *********/ /// <summary>The mod entry point, called after the mod is first loaded.</summary> /// <param name="helper">Provides simplified APIs for writing mods.</param> public override void Entry(IModHelper helper) { // init ModEntry.Config = helper.ReadConfig <ModConfig>(); ModEntry.SHelper = helper; ModEntry.SMonitor = this.Monitor; // Event listeners helper.Events.GameLoop.SaveLoaded += this.LoadSkinMap; helper.Events.GameLoop.Saving += this.SaveSkinMap; helper.Events.GameLoop.Saved += this.LoadSkinMap; // add commands helper.ConsoleCommands.Add("abandon_pet", "Remove a pet with the given name.", this.OnCommandReceived); helper.ConsoleCommands.Add("abandon_all_pets", "Remove all pets adopted using this mod, you monster.", this.OnCommandReceived); helper.ConsoleCommands.Add("list_animal_types", "Lists all animal types on your farm.", this.OnCommandReceived); helper.ConsoleCommands.Add("list_animal_skins", "Lists all animal skins used on your farm.", this.OnCommandReceived); helper.ConsoleCommands.Add("reset_animal_skins", "Lists all animal skins used on your farm.", this.OnCommandReceived); // Prepare BabyDuck override try { string asset = this.Helper.Content.GetActualAssetKey(Path.Combine("assets", "skins", "BabyDuck.png")); Game1.content.Load <Texture2D>(asset); BabyDuck = new AnimalSkin("BabyDuck", 0, asset); } catch (Exception e) { this.Monitor.Log("Unable to patch BabyDuck due to exception:", LogLevel.Error, e); } // Register default supported animal types Api.RegisterAnimalType("Blue Chicken"); Api.RegisterAnimalType("Brown Chicken"); Api.RegisterAnimalType("Brown Cow"); Api.RegisterAnimalType("Dinosaur", false); Api.RegisterAnimalType("Duck"); Api.RegisterAnimalType("Goat"); Api.RegisterAnimalType("Pig"); Api.RegisterAnimalType("Rabbit"); Api.RegisterAnimalType("Sheep", true, true); Api.RegisterAnimalType("Void Chicken"); Api.RegisterAnimalType("White Chicken"); Api.RegisterAnimalType("White Cow"); if (Config.ExtraTypes != null && Config.ExtraTypes.Length > 0) { foreach (string type in Config.ExtraTypes) { Api.RegisterAnimalType(type, false); } } // Register default supported pet types Api.RegisterPetType("cat", typeof(Cat)); Api.RegisterPetType("dog", typeof(Dog)); // configure bus replacement if (ModEntry.Config.AnimalsOnly) { this.ReplaceBus = false; } if (this.ReplaceBus) { try { string asset = this.Helper.Content.GetActualAssetKey(Path.Combine("assets", "box.png")); Game1.content.Load <Texture2D>(asset); } catch (Exception e) { this.ReplaceBus = false; this.Monitor.Log("Unable to patch BusStop due to exception:", LogLevel.Error, e); } } // hook events helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked; }
public static void Show() { Random random = ModEntry.Random; string type = null; AnimalSkin skin = null; if (ModEntry.Config.BalancedPetTypes) { double totalType = ModEntry.Pets.Count; Dictionary <string, double> types = ModEntry.Pets.Where(a => a.Value.Count > 0).ToDictionary(k => k.Key, v => totalType); foreach (Pet pet in ModEntry.GetAllPets().Where(p => ModEntry.PetTypesRev.ContainsKey(p.GetType()) && ModEntry.Pets[ModEntry.PetTypesRev[p.GetType()]].Count > 0)) { types[ModEntry.PetTypesRev[pet.GetType()]] *= 0.5; } types = types.ToDictionary(k => k.Key, v => v.Value / totalType); double typeMax = types.Values.OrderByDescending(a => a).First(); double typeChance = random.NextDouble() * typeMax; string[] validTypes = types.Where(a => a.Value >= typeChance).Select(a => a.Key).ToArray(); if (validTypes.Length > 0) { type = validTypes[random.Next(validTypes.Length)]; } } if (string.IsNullOrEmpty(type)) { string[] arr = ModEntry.Pets.Where(a => a.Value.Count > 0).Select(a => a.Key).ToArray(); type = arr[random.Next(arr.Length)]; } if (ModEntry.Config.BalancedPetSkins) { double totalSkin = ModEntry.Pets[type].Count; Dictionary <int, double> skins = ModEntry.Pets[type].ToDictionary(k => k.ID, v => totalSkin); foreach (Pet pet in ModEntry.GetAllPets().Where(pet => ModEntry.PetTypesRev.ContainsKey(pet.GetType()) && ModEntry.PetTypesRev[pet.GetType()].Equals(type) && skins.ContainsKey(pet.Manners))) { skins[pet.Manners] *= 0.5; } skins = skins.ToDictionary(k => k.Key, v => v.Value / totalSkin); double skinMax = skins.Values.OrderByDescending(a => a).First(); int[] validSkins; if (ModEntry.Config.ForceUniqueSkins) { validSkins = skins.Where(a => a.Value == skinMax).Select(a => a.Key).ToArray(); } else { double skinChance = Math.Min(random.NextDouble(), skinMax); validSkins = skins.Where(a => a.Value >= skinChance).Select(a => a.Key).ToArray(); } int id = 0; if (validSkins.Length > 0) { id = validSkins[random.Next(validSkins.Length)]; } skin = ModEntry.Pets[type].FirstOrDefault(a => a.ID == id); } if (skin == null) { skin = ModEntry.Pets[type][random.Next(ModEntry.Pets[type].Count)]; } AdoptQuestion q = new AdoptQuestion(skin); ModEntry.SHelper.Events.Display.RenderedHud += q.Display; Game1.currentLocation.lastQuestionKey = "AdoptPetQuestion"; Game1.currentLocation.createQuestionDialogue( ModEntry.SHelper.Translation.Get("AdoptMessage", new { petType = type, adoptionPrice = ModEntry.Config.AdoptionPrice }), Game1.player.Money < ModEntry.Config.AdoptionPrice ? new[] { new Response("n", ModEntry.SHelper.Translation.Get("AdoptNoGold", new { adoptionPrice = ModEntry.Config.AdoptionPrice })) } : new[] { new Response("y", ModEntry.SHelper.Translation.Get("AdoptYes")), new Response("n", ModEntry.SHelper.Translation.Get("AdoptNo")) }, q.Resolver); }