/// <summary>Refreshes creature information based on how much information the save file contains</summary> internal static void LoadCreatureSkins() { foreach (FarmAnimal animal in ModApi.GetAnimals()) { ModEntry.UpdateSkin(animal); } foreach (Pet pet in ModApi.GetPets()) { // Remove extra Strays left on the map if (ModApi.IsStray(pet)) { Game1.removeThisCharacterFromAllLocations(pet); } else { ModEntry.UpdateSkin(pet); } } foreach (Horse horse in ModApi.GetHorses()) { // Remove extra WildHorses left on the map if (ModApi.IsWildHorse(horse)) { Game1.removeThisCharacterFromAllLocations(horse); } else if (ModApi.IsNotATractor(horse)) { ModEntry.UpdateSkin(horse); } } }
/// <summary>Load skin assets from the /assets/skins directory into the A&S database</summary> internal static void LoadAssets() { // Gather handled types string validTypes = string.Join(", ", ModApi.GetHandledAllTypes()); foreach (string path in Directory.EnumerateFiles(Path.Combine(SHelper.DirectoryPath, "assets", "skins"), "*", SearchOption.AllDirectories)) { string extension = Path.GetExtension(path); string fileName = Path.GetFileNameWithoutExtension(path); string[] nameParts = fileName.Split(new[] { '_' }, 2); string type = ModEntry.Sanitize(nameParts[0]); int skinID = 0; if (!ValidExtensions.Contains(extension)) { ModEntry.SMonitor.Log($"Ignored skin `{fileName}` with invalid extension (extension must be one of type {string.Join(", ", ValidExtensions)})", LogLevel.Warn); } else if (!ModEntry.Assets.ContainsKey(type)) { ModEntry.SMonitor.Log($"Ignored skin `{fileName}` with invalid naming convention (can't parse {nameParts[0]} as an animal, pet, or horse. Expected one of type: {validTypes})", LogLevel.Warn); } else if (nameParts.Length != 2) { ModEntry.SMonitor.Log($"Ignored skin `{fileName} with invalid naming convention (no skin ID found)", LogLevel.Warn); } else if (nameParts.Length == 2 && !int.TryParse(nameParts[1], out skinID)) { ModEntry.SMonitor.Log($"Ignored skin `{fileName}` with invalid skin ID (can't parse {nameParts[1]} as a number)", LogLevel.Warn); } else if (skinID <= 0) { ModEntry.SMonitor.Log($"Ignored skin `{fileName}` with skin ID of less than or equal to 0. Skins must have an ID of at least 1.", LogLevel.Warn); } else { // File naming is valid, add asset into system string assetKey = SHelper.Content.GetActualAssetKey(Path.Combine(Path.GetDirectoryName(path), extension.Equals("xnb") ? Path.GetFileNameWithoutExtension(path) : Path.GetFileName(path))); ModEntry.Assets[type].Add(skinID, new AnimalSkin(type, skinID, assetKey)); } } // Print loaded assets to console StringBuilder summary = new StringBuilder(); summary.AppendLine( "Statistics:\n" + "\n Registered types: " + validTypes + "\n Skins:" ); foreach (KeyValuePair <string, Dictionary <int, AnimalSkin> > skinEntry in ModEntry.Assets) { if (skinEntry.Value.Count > 0) { summary.AppendLine($" {skinEntry.Key}: {skinEntry.Value.Count} skins ({string.Join(", ", skinEntry.Value.Select(p => Path.GetFileName(p.Value.AssetKey)).OrderBy(p => p))})"); } } ModEntry.SMonitor.Log(summary.ToString(), LogLevel.Trace); ModEntry.AssetsLoaded = true; }
/// <summary>Creates a new WildHorse instance</summary> internal WildHorse() { // Create WildHorse traits SkinID = ModEntry.GetRandomSkin(ModEntry.Sanitize(typeof(Horse).Name)); Map = Game1.getLocationFromName(SpawningMaps[Randomizer.Next(0, SpawningMaps.Count)]); Tile = GetRandomSpawnLocation(Map); // Create Horse instance HorseInstance = new Horse(new Guid(), (int)Tile.X, (int)Tile.Y) { Manners = WildID, Name = "Wild horse", displayName = "Wild horse" }; int[] info = ModApi.GetSpriteInfo(HorseInstance); if (SkinID != 0) { HorseInstance.Sprite = new AnimatedSprite(ModEntry.GetSkin(ModEntry.Sanitize(typeof(Horse).Name), SkinID).AssetKey, info[0], info[1], info[2]); } // Put that thing where it belongs Game1.warpCharacter(HorseInstance, Map, Tile); if (ModEntry.Config.NotifyHorseSpawn) { string message = $"A wild horse has been spotted at: {Map.Name} -- {Tile.X}, {Tile.Y}"; ModEntry.SMonitor.Log(message, LogLevel.Debug); Game1.chatBox.addInfoMessage(message); } }
/// <summary>Creates a new Stray</summary> internal Stray() { // Create Stray traits PetType = ModEntry.PetTypeMap.Keys.ToList()[Randomizer.Next(0, ModEntry.PetTypeMap.Count)]; SkinID = ModEntry.GetRandomSkin(PetType); // Create Pet instance PetInstance = (Pet)Activator.CreateInstance(ModEntry.PetTypeMap[PetType], (int)CreationLocation.X, (int)CreationLocation.Y); PetInstance.Manners = StrayID; PetInstance.Name = "Stray"; PetInstance.displayName = "Stray"; PetInstance.farmerPassesThrough = true; int[] info = ModApi.GetSpriteInfo(PetInstance); PetInstance.Sprite = new AnimatedSprite(ModEntry.GetSkin(PetType, SkinID).AssetKey, info[0], info[1], info[2]); // Put that thing where it belongs Game1.warpCharacter(PetInstance, Marnies, CreationLocation); if (ModEntry.Config.NotifyStraySpawn) { string message = $"A stray pet is available to adopt at Marnie's!"; ModEntry.SMonitor.Log(message, LogLevel.Debug); Game1.chatBox.addInfoMessage(message); } }
/// <summary>Spawns all owned pets into the FarmHouse</summary> internal void IndoorWeatherPetSpawn() { foreach (Pet pet in ModApi.GetPets()) { pet.warpToFarmHouse(Game1.player); } }
internal static void SaveData(object s, EventArgs e) { // Only allow the host player to save Adopt & Skin data if (!Context.IsMainPlayer) { return; } // Ensure strays and wild horses don't persist ModApi.ClearUnownedPets(); // Save skin and category maps SHelper.Data.WriteSaveData("skin-map", ModEntry.SkinMap); SHelper.Data.WriteSaveData("id-to-category", ModEntry.IDToCategory); // Save Short ID maps SHelper.Data.WriteSaveData("animal-long-to-short-ids", ModEntry.AnimalLongToShortIDs); SHelper.Data.WriteSaveData("animal-short-to-long-ids", ModEntry.AnimalShortToLongIDs); // Save Stray and WildHorse spawn potential SHelper.Data.WriteSaveData("first-pet-received", ModEntry.Creator.FirstPetReceived.ToString()); SHelper.Data.WriteSaveData("first-horse-received", ModEntry.Creator.FirstHorseReceived.ToString()); // Save data version. May be used for reverse-compatibility for files. SHelper.Data.WriteSaveData("data-version", "4"); // Remove Adopt & Skin from update loop StopUpdateChecks(null, null); }
/// <summary>Checks whether the user's cursor is currently over a Pet or Horse and updates the text to display in the tooltip</summary> internal void HoverCheck() { bool isHovering = false; Vector2 mousePos = new Vector2(Game1.getOldMouseX() + Game1.viewport.X, Game1.getOldMouseY() + Game1.viewport.Y) / Game1.tileSize; // Show pet tooltip foreach (Pet pet in ModApi.GetPets()) { if (IsWithinSpriteBox(mousePos, pet)) { isHovering = true; HoverText = pet.displayName; } } // Show horse tooltip foreach (Horse horse in ModApi.GetHorses()) { if (IsWithinSpriteBox(mousePos, horse)) { isHovering = true; HoverText = horse.displayName; } } // Clear hover text when not hovering over a pet or horse if (!isHovering) { HoverText = null; } }
public void RegisterBFAVAnimals() { Dictionary <string, string[]> bfavAnimals = BFAVApi.GetFarmAnimalsByCategory("2"); foreach (KeyValuePair <string, string[]> pair in bfavAnimals) { foreach (string type in pair.Value) { // Only register types that are not already registered if (!ModApi.IsRegisteredType(type)) { string newType = ModEntry.Sanitize(pair.Key); if (newType.Contains("dinosaur")) { ModApi.RegisterAnimalType(type, false, false); } else if (newType.Contains("sheep")) { ModApi.RegisterAnimalType(type, true, true); } else { ModApi.RegisterAnimalType(type); } } } } }
public static void ClearUnownedPets() { foreach (NPC npc in Utility.getAllCharacters()) { if (npc is Horse horse && ModApi.IsWildHorse(horse)) { Game1.removeThisCharacterFromAllLocations(horse); }
/// <summary>Returns an enumerable list of all owned Pet instances. This excludes strays.</summary> public static IEnumerable <Pet> GetPets() { foreach (NPC npc in Utility.getAllCharacters()) { if (npc is Pet pet && !ModApi.IsStray(pet)) { yield return(pet); } } }
/************************** ** Setup + Load/Save Logic ***************************/ /// <summary>Sets up initial values needed for A&S.</summary> internal static void Setup(object sender, SaveLoadedEventArgs e) { ModApi.RegisterDefaultTypes(); IntegrateMods(); LoadAssets(); // Remove the Setup from the loop, so that it isn't done twice when the player returns to the title screen and loads again SHelper.Events.GameLoop.SaveLoaded -= Setup; }
/// <summary>Checks that the given argument is of a recognized creature group or recognized creature type.</summary> /// <param name="arg">The argument to be checked</param> /// <returns>Returns true if the given argument is stored in CreatureGroups or is a known FarmAnimal, Pet, or Horse type. Otherwise gives a console error report and returns false.</returns> internal static bool EnforceArgTypeGroup(string arg) { string type = ModEntry.Sanitize(arg); List <string> handledTypes = ModApi.GetHandledAllTypes(); if (!CreatureGroups.Contains(type) && !handledTypes.Contains(type)) { ModEntry.SMonitor.Log($"Argument given isn't one of {string.Join(", ", CreatureGroups)}, or a handled creature type. Handled types:\n{string.Join(", ", handledTypes)}", LogLevel.Error); return(false); } return(true); }
/// <summary>Return the information on a pet or horse that the list_animals console command uses. internal static string GetPrintString(Character creature) { List <string> handledTypes = new List <string>(); string name = ""; string type = ""; int shortID = 0; int skinID = 0; switch (creature) { case Horse horse: handledTypes = ModApi.GetHandledHorseTypes(); name = horse.Name; type = ModEntry.Sanitize(horse.GetType().Name); shortID = horse.Manners; skinID = ModEntry.HorseSkinMap[horse.Manners]; break; case Pet pet: handledTypes = ModApi.GetHandledPetTypes(); name = pet.Name; type = ModEntry.Sanitize(pet.GetType().Name); shortID = pet.Manners; skinID = ModEntry.PetSkinMap[pet.Manners]; break; case FarmAnimal animal: handledTypes = ModApi.GetHandledAnimalTypes(); name = animal.Name; type = ModEntry.Sanitize(animal.type.Value); shortID = ModEntry.AnimalLongToShortIDs[animal.myID.Value]; skinID = ModEntry.AnimalSkinMap[animal.myID.Value]; break; default: return(""); } if (handledTypes.Contains(type)) { return($"\n # {name}: Type - {type}\n" + $"Short ID: {shortID}\n" + $"Skin ID: {skinID}"); } else { return($"\n # {name}: Type - {type}\n" + "Skin type not handled\n"); } }
/********************************* ** H O R S E A D O P T I O N ** *********************************/ /// <summary>Adopts and names the wild horse being interacted with. Called in the CheckHorse event handler.</summary> internal void HorseNamer(string horseName) { // Name Horse and add to Adopt & Skin database HorseInfo.HorseInstance.Name = horseName; HorseInfo.HorseInstance.displayName = horseName; HorseInfo.HorseInstance.HorseId = ModApi.GetRandomStableID(); Earth.AddCreature(HorseInfo.HorseInstance, HorseInfo.SkinID); // Horse is no longer a WildHorse to keep track of HorseInfo = null; // Exit the naming menu Game1.drawObjectDialogue($"Adopted {horseName}."); }
/// <summary>Refreshes creature information based on how much information the save file contains</summary> internal static void LoadCreatureSkins() { foreach (FarmAnimal animal in ModApi.GetAnimals()) { if (!ModEntry.AnimalLongToShortIDs.ContainsKey(ModEntry.GetLongID(animal))) { Entry.AddCreature(animal); } else { ModEntry.UpdateSkin(animal); } } foreach (Pet pet in ModApi.GetPets()) { // Remove extra Strays left on the map if (ModApi.IsStray(pet)) { Game1.removeThisCharacterFromAllLocations(pet); } else if (ModEntry.GetLongID(pet) == 0) { Entry.AddCreature(pet); } else { ModEntry.UpdateSkin(pet); } } foreach (Horse horse in ModApi.GetHorses()) { // Remove extra WildHorses left on the map if (ModApi.IsWildHorse(horse)) { Game1.removeThisCharacterFromAllLocations(horse); } else if (ModApi.IsNotATractor(horse) && ModEntry.GetLongID(horse) == 0) { Entry.AddCreature(horse); } else if (ModApi.IsNotATractor(horse)) { ModEntry.UpdateSkin(horse); } } }
/// <summary>Returns the first Stable instance found on the farm.</summary> internal static Guid GetStableID() { Guid stableID = ZeroHorseID; foreach (Horse horse in ModApi.GetHorses()) { if (horse.HorseId != ZeroHorseID) { stableID = horse.HorseId; break; } } return(stableID); }
/// <summary>Returns an enumerable list of all existing Horse instances. This includes WildHorses and excludes tractors.</summary> public static IEnumerable <Horse> GetAllHorses() { foreach (NPC npc in Utility.getAllCharacters()) { if (npc is Horse horse && ModApi.IsNotATractor(horse)) { yield return(horse); } } // Horses being ridden don't technically exist, and must be added separately foreach (Horse horse in ModEntry.BeingRidden) { yield return(horse); } }
/************************** ** Setup + Load/Save Logic ***************************/ /// <summary>Sets up initial values needed for A&S.</summary> internal static void Setup(object sender, SaveLoadedEventArgs e) { // Register FarmAnimal Types Dictionary <string, string> farmAnimalData = ModEntry.SHelper.Content.Load <Dictionary <string, string> >("Data/FarmAnimals", ContentSource.GameContent); foreach (KeyValuePair <string, string> pair in farmAnimalData) { // Ignore unused FarmAnimal type in SDV code if (pair.Key.ToLower() == "hog" || pair.Key.ToLower() == "babyhog") { continue; } string[] animalInfo = pair.Value.Split(new[] { '/' }); string harvestTool = animalInfo[22]; int maturedDay = int.Parse(animalInfo[1]); ModApi.RegisterType(pair.Key, typeof(FarmAnimal), maturedDay > 0, ModEntry.Sanitize(harvestTool) == "shears"); } // Register default supported pet types ModApi.RegisterType("cat", typeof(Cat)); ModApi.RegisterType("dog", typeof(Dog)); // Register horse type ModApi.RegisterType("horse", typeof(Horse)); LoadAssets(); // Alert player that there are creatures with no skins loaded for them List <string> skinless = new List <string>(); foreach (string type in ModEntry.Assets.Keys) { if (ModEntry.Assets[type].Count == 0) { skinless.Add(type); } } if (skinless.Count > 0) { ModEntry.SMonitor.Log($"NOTICE: The following creature types have no skins located in `/assets/skins`:\n" + $"{string.Join(", ", skinless)}", LogLevel.Debug); } // Remove the Setup from the loop, so that it isn't done twice when the player returns to the title screen and loads again SHelper.Events.GameLoop.SaveLoaded -= Setup; }
internal static void SaveData(object s, EventArgs e) { // Only allow the host player to save Adopt & Skin data if (!Context.IsMainPlayer) { return; } // Ensure strays and wild horses don't persist ModApi.ClearUnownedPets(); // Save skin and category maps SHelper.Data.WriteSaveData("skin-map", ModEntry.SkinMap); SHelper.Data.WriteSaveData("id-to-category", ModEntry.IDToCategory); // Save Short ID maps SHelper.Data.WriteSaveData("animal-long-to-short-ids", ModEntry.AnimalLongToShortIDs); SHelper.Data.WriteSaveData("animal-short-to-long-ids", ModEntry.AnimalShortToLongIDs); // Save Pet to owner map /*Dictionary<long, long> petOwnership = new Dictionary<long, long>(); * foreach (KeyValuePair<long, Farmer> pair in ModEntry.OwnerMap) * { * petOwnership[pair.Key] = ModApi.FarmerToID(pair.Value); * } * SHelper.Data.WriteSaveData("pet-ownership-map", petOwnership); */ // Save Horse to owner map Dictionary <long, long> horseOwnership = new Dictionary <long, long>(); foreach (KeyValuePair <long, Farmer> pair in ModEntry.HorseOwnershipMap) { horseOwnership[pair.Key] = ModApi.FarmerToID(pair.Value); } SHelper.Data.WriteSaveData("horse-ownership-map", horseOwnership); // Save Stray and WildHorse spawn potential SHelper.Data.WriteSaveData("first-pet-received", ModEntry.Creator.FirstPetReceived.ToString()); SHelper.Data.WriteSaveData("first-horse-received", ModEntry.Creator.FirstHorseReceived.ToString()); // Save data version. May be used for reverse-compatibility for files. SHelper.Data.WriteSaveData("data-version", "5"); // Remove Adopt & Skin from update loop StopUpdateChecks(null, null); }
/// <summary>Return the information on a pet or horse that the list_animals console command uses. internal static string GetPrintString(Character creature) { string name = creature.Name; string type = ModApi.GetInternalType(creature); int shortID = ModEntry.GetShortID(creature); long longID = ModEntry.GetLongID(creature); int skinID = 0; if (ModEntry.SkinMap.ContainsKey(longID)) { skinID = ModEntry.SkinMap[longID]; } return($"\n # {name}: Type - {type}\n" + $"Short ID: {shortID}\n" + $"Skin ID: {skinID}"); }
/// <summary>Refreshes creature information based on how much information the save file contains</summary> internal static void LoadCreatureSkins() { foreach (FarmAnimal animal in ModApi.GetAnimals()) { if (!ModEntry.AnimalLongToShortIDs.ContainsKey(ModEntry.GetLongID(animal))) { Entry.AddCreature(animal); } else { ModEntry.UpdateSkin(animal); } } foreach (Pet pet in ModApi.GetPets()) { if (ModEntry.GetLongID(pet) == 0) { Entry.AddCreature(pet); } else { ModEntry.UpdateSkin(pet); } } foreach (Horse horse in ModApi.GetHorses()) { if (ModEntry.GetLongID(horse) == 0) { Entry.AddCreature(horse); } else { ModEntry.UpdateSkin(horse); } } }
/// <summary>Creates a new Stray</summary> internal Stray() { // Create Stray traits PetType = ModEntry.PetTypeMap.Keys.ToList()[Randomizer.Next(0, ModEntry.PetTypeMap.Count)]; SkinID = ModEntry.GetRandomSkin(PetType); // Create Pet instance PetInstance = PetConstructors[ModEntry.PetTypeMap[PetType]](); PetInstance.Manners = StrayID; PetInstance.Name = "Stray"; PetInstance.displayName = "Stray"; PetInstance.farmerPassesThrough = true; int[] info = ModApi.GetSpriteInfo(PetInstance); if (SkinID != 0) { PetInstance.Sprite = new AnimatedSprite(ModEntry.GetSkin(PetType, SkinID).AssetKey, info[0], info[1], info[2]); } // Put that thing where it belongs PetInstance.currentLocation = Marnies; Game1.warpCharacter(PetInstance, Marnies, CreationLocation); }
/// <summary>Creates a new WildHorse instance</summary> internal WildHorse() { // Create WildHorse traits SkinID = ModEntry.GetRandomSkin(ModEntry.Sanitize(typeof(Horse).Name)); Map = Game1.getLocationFromName(PlayerSpecifiedSpawnMaps[Randomizer.Next(0, PlayerSpecifiedSpawnMaps.Count)]); Tile = GetRandomSpawnLocation(Map); // Create Horse instance HorseInstance = new Horse(new Guid(), (int)Tile.X, (int)Tile.Y) { Manners = WildID, Name = "Wild horse", displayName = "Wild horse" }; int[] info = ModApi.GetSpriteInfo(HorseInstance); if (SkinID != 0) { HorseInstance.Sprite = new AnimatedSprite(ModEntry.GetSkin(ModEntry.Sanitize(typeof(Horse).Name), SkinID).AssetKey, info[0], info[1], info[2]); } // Put that thing where it belongs Game1.warpCharacter(HorseInstance, Map, Tile); }
internal void SpreadPets(object sender, WarpedEventArgs e) { List <Pet> pets = ModApi.GetPets().ToList(); // No pets are in the game if (pets.Count == 0) { return; } // Only do teleport if the player is entering the Farm, FarmHouse, or Marnie's if (!typeof(Farm).IsAssignableFrom(e.NewLocation.GetType()) && !typeof(FarmHouse).IsAssignableFrom(e.NewLocation.GetType()) && (Stray.Marnies != e.NewLocation)) { return; } // Ensure Stray isn't moved around by vanilla ModEntry.Creator.MoveStrayToSpawn(); if (IsIndoorWeather()) { IndoorWeatherPetSpawn(); return; } else { // Find area to warp pets to Farm farm = Game1.getFarm(); int initX = (int)pets[0].getTileLocation().X; int initY = (int)pets[0].getTileLocation().Y; List <Vector2> warpableTiles = new List <Vector2>(); int cer = ModEntry.Config.CuddleExplosionRadius; // Collect a set of potential tiles to warp a pet to for (int i = -cer; i < cer; i++) { for (int j = -cer; j < cer; j++) { int warpX = initX + i; int warpY = initY + j; if (warpX < 0) { warpX = 0; } if (warpY < 0) { warpY = 0; } Vector2 tile = new Vector2(warpX, warpY); if (IsTileAccessible(farm, tile)) { warpableTiles.Add(tile); } } } // No placeable tiles found within the range given in the Config if (warpableTiles.Count == 0) { ModEntry.SMonitor.Log($"Pets cannot be spread within the given radius: {cer}", LogLevel.Debug); return; } // Spread pets foreach (Pet pet in ModApi.GetPets()) { Vector2 ranTile = warpableTiles[Randomizer.Next(0, warpableTiles.Count)]; Game1.warpCharacter(pet, farm, ranTile); } } }
/// <summary>Load skin assets from the /assets/skins directory into the A&S database</summary> internal static void LoadAssets() { // Gather handled types string validTypes = string.Join(", ", ModApi.GetHandledAllTypes()); // Add custom sprites foreach (string path in Directory.EnumerateFiles(Path.Combine(SHelper.DirectoryPath, "assets", "skins"), "*", SearchOption.AllDirectories)) { PullSprite(Path.GetRelativePath(SHelper.DirectoryPath, path)); // must be a relative path } // Grab the directory for the /Mods folder from the /Mods/AdoptSkin //string modFolderPath = SHelper.DirectoryPath foreach (string path in Directory.EnumerateFiles(Path.Combine(SHelper.DirectoryPath))) { // Warn for invalid files if (InvalidExt.Count > 0) { ModEntry.SMonitor.Log($"Ignored skins with invalid extension:\n`{string.Join("`, `", InvalidExt)}`\nExtension must be one of type {string.Join(", ", ValidExtensions)}", LogLevel.Warn); } } if (InvalidType.Count > 0) { ModEntry.SMonitor.Log($"Ignored skins with invalid naming convention:\n`{string.Join("`, `", InvalidType)}`\nCan't parse as an animal, pet, or horse. Expected one of type: {validTypes}", LogLevel.Warn); } if (InvalidID.Count > 0) { ModEntry.SMonitor.Log($"Ignored skins with invalid naming convention (no skin ID found):\n`{string.Join("`, `", InvalidID)}`", LogLevel.Warn); } if (InvalidNum.Count > 0) { ModEntry.SMonitor.Log($"Ignored skins with invalid ID (can't parse ID number):\n`{string.Join("`, `", InvalidNum)}`", LogLevel.Warn); } if (InvalidRange.Count > 0) { ModEntry.SMonitor.Log($"Ignored skins with ID of less than or equal to 0 (Skins must have an ID of at least 1):\n`{string.Join("`, `", InvalidRange)}`", LogLevel.Warn); } EnforceSpriteSets(); // Print loaded assets to console StringBuilder summary = new StringBuilder(); summary.AppendLine( "Statistics:\n" + "\n Registered types: " + validTypes + "\n Skins:" ); foreach (KeyValuePair <string, Dictionary <int, AnimalSkin> > skinEntry in ModEntry.Assets) { if (skinEntry.Value.Count > 0) { summary.AppendLine($" {skinEntry.Key}: {skinEntry.Value.Count} skins ({string.Join(", ", skinEntry.Value.Select(p => Path.GetFileName(p.Value.AssetKey)).OrderBy(p => p))})"); } } ModEntry.SMonitor.Log(summary.ToString(), LogLevel.Trace); ModEntry.AssetsLoaded = true; }
/// <summary>Adds creatures into the system based on the data from older versions' save files or for files new to A&S.</summary> internal static void LoadSkinsOldVersion() { Dictionary <Character, int> creaturesToAdd = new Dictionary <Character, int>(); // Load pet information stored from older version formats Dictionary <long, int> petSkinMap = SHelper.Data.ReadSaveData <Dictionary <long, int> >("pet-skin-map") ?? new Dictionary <long, int>(); foreach (Pet pet in ModApi.GetPets()) { long longID = ModEntry.GetLongID(pet); // Pet unregistered if (longID == 0) { creaturesToAdd.Add(pet, 0); } // Pet registered in previous system else if (petSkinMap.ContainsKey(longID)) { creaturesToAdd.Add(pet, petSkinMap[longID]); } // Reset any previous known ShortID pet.Manners = 0; } // Load horse information stored from older version formats Dictionary <long, int> horseSkinMap = SHelper.Data.ReadSaveData <Dictionary <long, int> >("horse-skin-map") ?? new Dictionary <long, int>(); foreach (Horse horse in ModApi.GetHorses()) { long longID = ModEntry.GetLongID(horse); // Horse unregistered if (longID == 0) { creaturesToAdd.Add(horse, 0); } // Horse registered in previous system else if (horseSkinMap.ContainsKey(longID)) { creaturesToAdd.Add(horse, horseSkinMap[longID]); } // Reset any previous known ShortID horse.Manners = 0; } // Load animal information stored from older version formats Dictionary <long, int> animalSkinMap = SHelper.Data.ReadSaveData <Dictionary <long, int> >("animal-skin-map") ?? new Dictionary <long, int>(); ModEntry.AnimalLongToShortIDs = new Dictionary <long, int>(); ModEntry.AnimalShortToLongIDs = new Dictionary <int, long>(); foreach (FarmAnimal animal in ModApi.GetAnimals()) { long longID = ModEntry.GetLongID(animal); // Animal registered in previous system if (animalSkinMap.ContainsKey(longID)) { creaturesToAdd.Add(animal, animalSkinMap[longID]); } // Animal unregistered else { creaturesToAdd.Add(animal, 0); } } // Add in all creatures from older systems foreach (KeyValuePair <Character, int> creatureInfo in creaturesToAdd) { Entry.AddCreature(creatureInfo.Key, creatureInfo.Value); } }
internal static void LoadData(object s, EventArgs e) { // Only allow the host player to load Adopt & Skin data if (Context.IsMainPlayer) { //SMonitor.Log("Multiplayer Farmhand detected. Adopt & Skin has been disabled.", LogLevel.Debug); SMonitor.Log("Host detected! Let's WRECK IT", LogLevel.Debug); // Load skin and category maps ModEntry.SkinMap = SHelper.Data.ReadSaveData <Dictionary <long, int> >("skin-map") ?? new Dictionary <long, int>(); ModEntry.IDToCategory = SHelper.Data.ReadSaveData <Dictionary <long, ModEntry.CreatureCategory> >("id-to-category") ?? new Dictionary <long, ModEntry.CreatureCategory>(); // Load Short ID maps ModEntry.AnimalLongToShortIDs = SHelper.Data.ReadSaveData <Dictionary <long, int> >("animal-long-to-short-ids") ?? new Dictionary <long, int>(); ModEntry.AnimalShortToLongIDs = SHelper.Data.ReadSaveData <Dictionary <int, long> >("animal-short-to-long-ids") ?? new Dictionary <int, long>(); // Set up maps if save data is from an older data format of A&S if (ModEntry.SkinMap.Count == 0) { LoadSkinsOldVersion(); } // Load Pet ownership map // TODO: Similar to horse ownership loading // Load Horse ownership map Dictionary <long, long> horseOwnerMap = SHelper.Data.ReadSaveData <Dictionary <long, long> >("horse-ownership-map") ?? new Dictionary <long, long>(); ModEntry.HorseOwnershipMap = new Dictionary <long, Farmer>(); foreach (KeyValuePair <long, long> pair in horseOwnerMap) { foreach (Farmer farmer in Game1.getAllFarmers()) { if (pair.Key == ModApi.FarmerToID(farmer)) { ModEntry.HorseOwnershipMap[pair.Key] = farmer; } } } // If no horses are mapped to owners, or there are some unmapped horses, assign them all to the host player if (ModEntry.HorseOwnershipMap.Count != ModApi.GetHorses().Count()) { foreach (Horse horse in ModApi.GetHorses()) { long horseID = ModEntry.GetLongID(horse); if (horseID != 0 && !ModEntry.HorseOwnershipMap.ContainsKey(horseID)) { ModEntry.HorseOwnershipMap.Add(horseID, Game1.MasterPlayer); } } } // If there are extra horses on the mapping, remove them. This deal with a bug in older versions of A&S if (ModEntry.HorseOwnershipMap.Count > ModApi.GetHorses().Count()) { List <Horse> horses = ModApi.GetHorses().ToList(); List <long> existingHorses = new List <long>(); // Find the IDs of all existing, owned Horses foreach (Horse horse in horses) { existingHorses.Add(ModEntry.GetLongID(horse)); } // Find the stowaways. List <long> idsToRemove = new List <long>(); foreach (long horseID in ModEntry.HorseOwnershipMap.Keys) { if (!existingHorses.Contains(horseID)) { idsToRemove.Add(horseID); } } // Yeet them from the mapping. foreach (long id in idsToRemove) { ModEntry.HorseOwnershipMap.Remove(id); } } // Load received first pet/horse status ModEntry.Creator.FirstHorseReceived = bool.Parse(SHelper.Data.ReadSaveData <string>("first-horse-received") ?? "false"); ModEntry.Creator.FirstPetReceived = bool.Parse(SHelper.Data.ReadSaveData <string>("first-pet-received") ?? "false"); Entry.CheckForFirstPet(null, null); Entry.CheckForFirstHorse(null, null); //return; } // Refresh skins via skinmap LoadCreatureSkins(); // Make sure Marnie's cows put some clothes on foreach (GameLocation loc in Game1.locations) { if (loc is Forest forest) { foreach (FarmAnimal animal in forest.marniesLivestock) { string type = ModApi.GetInternalType(animal); if (ModApi.HasSkins(type)) { int[] spriteInfo = ModApi.GetSpriteInfo(animal); AnimalSkin skin = ModEntry.GetSkin(type, ModEntry.GetRandomSkin(type)); animal.Sprite = new AnimatedSprite(skin.AssetKey, spriteInfo[0], spriteInfo[1], spriteInfo[2]); } } } } // Set configuration for walk-through pets foreach (Pet pet in ModApi.GetPets()) { if (ModEntry.Config.WalkThroughPets) { pet.farmerPassesThrough = true; } else { pet.farmerPassesThrough = false; } } // Set last known animal count ModEntry.AnimalCount = Game1.getFarm().getAllFarmAnimals().Count; // Add Adopt & Skin to the update loop StartUpdateChecks(); }
internal void SpreadPets(object sender, WarpedEventArgs e) { // ** TODO: Only one pet on farmer's bed // TODO: If no pets, handle error List <Pet> pets = ModApi.GetPets().ToList(); // No pets are in the game if (pets.Count == 0) { return; } // Ensure Stray isn't moved around by vanilla if (typeof(Farm).IsAssignableFrom(e.NewLocation.GetType()) || typeof(FarmHouse).IsAssignableFrom(e.NewLocation.GetType()) || Stray.Marnies != e.NewLocation) { ModEntry.Creator.MoveStrayToSpawn(); } // Only move pets once at beginning of day and once at night if ((!Game1.isDarkOut() && PetsPlacedForDay) || (Game1.isDarkOut() && PetsPlacedForNight)) { return; } if (IsIndoorWeather()) { IndoorWeatherPetSpawn(); PetsPlacedForDay = true; PetsPlacedForNight = true; return; } else { // Place everyone at the correct starting point, at the water dish foreach (Pet pet in ModApi.GetPets()) { pet.setAtFarmPosition(); } // Find area to warp pets to Farm farm = Game1.getFarm(); int initX = (int)pets[0].getTileLocation().X; int initY = (int)pets[0].getTileLocation().Y; List <Vector2> warpableTiles = new List <Vector2>(); int cer = ModEntry.Config.CuddleExplosionRadius; // Collect a set of potential tiles to warp a pet to for (int i = -cer; i < cer; i++) { for (int j = -cer; j < cer; j++) { int warpX = initX + i; int warpY = initY + j; if (warpX < 0) { warpX = 0; } if (warpY < 0) { warpY = 0; } Vector2 tile = new Vector2(warpX, warpY); if (IsTileAccessible(farm, tile)) { warpableTiles.Add(tile); } } } // No placeable tiles found within the range given in the Config if (warpableTiles.Count == 0) { ModEntry.SMonitor.Log($"Pets cannot be spread within the given radius: {cer}", LogLevel.Debug); return; } // Spread pets foreach (Pet pet in ModApi.GetPets()) { Vector2 ranTile = warpableTiles[Randomizer.Next(0, warpableTiles.Count)]; Game1.warpCharacter(pet, farm, ranTile); } PetsPlacedForDay = true; } }
/************************* ** Miscellaneous Helpers *************************/ /// <summary>Returns a List of Characters of all creatures of the specified creature type or custom grouping</summary> internal List <Character> GetCreaturesFromGroup(string group) { group = ModEntry.Sanitize(group); List <Character> calledGroup = new List <Character>(); if (!CreatureGroups.Contains(group) && !ModApi.GetHandledAllTypes().Contains(group)) { ModEntry.SMonitor.Log($"Specified grouping is not handled by Adopt & Skin: {group}", LogLevel.Error); return(calledGroup); } // Add FarmAnimal types to the return list if (group == "all" || group == "animal") { foreach (FarmAnimal animal in ModApi.GetAnimals()) { calledGroup.Add(animal); } } else if (group == "coop") { foreach (FarmAnimal animal in ModApi.GetAnimals()) { if (animal.isCoopDweller()) { calledGroup.Add(animal); } } } else if (group == "barn") { foreach (FarmAnimal animal in ModApi.GetAnimals()) { if (!animal.isCoopDweller()) { calledGroup.Add(animal); } } } else if (group == "chicken") { foreach (FarmAnimal animal in ModApi.GetAnimals()) { if (ModApi.IsChicken(animal)) { calledGroup.Add(animal); } } } else if (group == "cow") { foreach (FarmAnimal animal in ModApi.GetAnimals()) { if (ModApi.IsCow(animal)) { calledGroup.Add(animal); } } } else if (ModApi.GetHandledAnimalTypes().Contains(group)) { foreach (FarmAnimal animal in ModApi.GetAnimals()) { if (ModApi.GetInternalType(animal) == group) { calledGroup.Add(animal); } } } // Add Pet types to the return list if (group == "all" || group == "pet") { foreach (Pet pet in ModApi.GetPets()) { calledGroup.Add(pet); } } else if (ModApi.GetHandledPetTypes().Contains(group)) { foreach (Pet pet in ModApi.GetPets()) { if (ModApi.GetInternalType(pet) == group) { calledGroup.Add(pet); } } } // Add Horse types to the return list if (group == "all" || ModApi.GetHandledHorseTypes().Contains(group)) { foreach (Horse horse in ModApi.GetHorses()) { calledGroup.Add(horse); } } return(calledGroup); }
/****************** ** Print Strings ******************/ /// <summary>Prints the the requested creature information from the list_animals console command.</summary> internal static void PrintRequestedCreatures(string arg) { string type = ModEntry.Sanitize(arg); // -- Handle FarmAnimal type arguments -- if (type == "all" || type == "animal") { List <string> animalInfo = new List <string>(); foreach (FarmAnimal animal in ModApi.GetAnimals()) { animalInfo.Add(GetPrintString(animal)); } ModEntry.SMonitor.Log("Animals:", LogLevel.Debug); ModEntry.SMonitor.Log($"{string.Join(", ", animalInfo)}\n", LogLevel.Info); } // Handle coop animal types only else if (type == "coop") { List <string> coopInfo = new List <string>(); foreach (FarmAnimal animal in ModApi.GetAnimals()) { if (animal.isCoopDweller()) { coopInfo.Add(GetPrintString(animal)); } } ModEntry.SMonitor.Log("Coop Animals:", LogLevel.Debug); ModEntry.SMonitor.Log($"{string.Join(", ", coopInfo)}\n", LogLevel.Info); } // Handle barn animal types only else if (type == "barn") { List <string> barnInfo = new List <string>(); foreach (FarmAnimal animal in ModApi.GetAnimals()) { if (!animal.isCoopDweller()) { barnInfo.Add(GetPrintString(animal)); } } ModEntry.SMonitor.Log("Barn Animals:", LogLevel.Debug); ModEntry.SMonitor.Log($"{string.Join(", ", barnInfo)}\n", LogLevel.Info); } // Handle chicken type arguments else if (type == "chicken") { List <string> chickenInfo = new List <string>(); foreach (FarmAnimal animal in ModApi.GetAnimals()) { if (ModApi.IsChicken(ModApi.GetInternalType(animal))) { chickenInfo.Add(GetPrintString(animal)); } } ModEntry.SMonitor.Log("Chickens:", LogLevel.Debug); ModEntry.SMonitor.Log($"{string.Join(", ", chickenInfo)}\n", LogLevel.Info); } // Handle cow type arguments else if (type == "cow") { List <string> cowInfo = new List <string>(); foreach (FarmAnimal animal in ModApi.GetAnimals()) { if (ModApi.IsCow(ModApi.GetInternalType(animal))) { cowInfo.Add(GetPrintString(animal)); } } ModEntry.SMonitor.Log("Cows:", LogLevel.Debug); ModEntry.SMonitor.Log($"{string.Join(", ", cowInfo)}\n", LogLevel.Info); } // Handle other animal type arguments else if (ModApi.GetHandledAnimalTypes().Contains(type)) { List <string> animalInfo = new List <string>(); foreach (FarmAnimal animal in ModApi.GetAnimals()) { if (type == ModEntry.Sanitize(animal.type.Value)) { animalInfo.Add(GetPrintString(animal)); } } ModEntry.SMonitor.Log($"{arg}s:", LogLevel.Debug); ModEntry.SMonitor.Log($"{string.Join(", ", animalInfo)}\n", LogLevel.Info); } // -- Handle Pet type arguments -- if (type == "all" || type == "pet") { List <string> petInfo = new List <string>(); foreach (Pet pet in ModApi.GetPets()) { petInfo.Add(GetPrintString(pet)); } ModEntry.SMonitor.Log("Pets:", LogLevel.Debug); ModEntry.SMonitor.Log($"{string.Join(", ", petInfo)}\n", LogLevel.Info); } else if (ModApi.GetHandledPetTypes().Contains(type)) { List <string> petInfo = new List <string>(); foreach (Pet pet in ModApi.GetPets()) { if (type == ModEntry.Sanitize(pet.GetType().Name)) { petInfo.Add(GetPrintString(pet)); } } ModEntry.SMonitor.Log($"{arg}s:", LogLevel.Debug); ModEntry.SMonitor.Log($"{string.Join(", ", petInfo)}\n", LogLevel.Info); } // -- Handle Horse type arguments -- if (type == "all" || ModApi.GetHandledHorseTypes().Contains(type)) { List <string> horseInfo = new List <string>(); foreach (Horse horse in ModApi.GetHorses()) { horseInfo.Add(GetPrintString(horse)); } ModEntry.SMonitor.Log("Horses:", LogLevel.Debug); ModEntry.SMonitor.Log($"{string.Join(", ", horseInfo)}\n", LogLevel.Info); } }