/// <summary>Load legacy save data with the given key, if it's valid.</summary> /// <param name="key">The save key to load.</param> /// <param name="playerId">The parsed <see cref="Farmer.UniqueMultiplayerID"/> value.</param> /// <param name="data">The save data for the player.</param> private bool TryLoadSaveData(string key, out long playerId, out LegacySaveData data) { const string prefix = "spacechase0.anotherhungermod."; // extract player ID if (!key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { playerId = 0; data = null; return(false); } { string raw = key.Substring(prefix.Length); if (!long.TryParse(raw, out playerId)) { playerId = 0; data = null; return(false); } } // load data data = this.DataHelper.ReadSaveData <LegacySaveData>(key); return(data != null); }
/// <summary>Migrate tractors and garages from older versions of the mod.</summary> /// <remarks>The Robin construction logic is derived from <see cref="NPC.reloadSprite"/> and <see cref="Farm.resetForPlayerEntry"/>.</remarks> private void LoadLegacyData() { // fix building types foreach (BuildableGameLocation location in this.GetBuildableLocations()) { foreach (Stable stable in location.buildings.OfType <Stable>()) { if (stable.buildingType.Value == this.BlueprintBuildingType) { stable.buildingType.Value = "Stable"; stable.maxOccupants.Value = this.MaxOccupantsID; } } } // get save data LegacySaveData saveData = this.Helper.Data.ReadSaveData <LegacySaveData>("tractors"); // 4.6 if (saveData?.Buildings == null) { saveData = this.Helper.Data.ReadJsonFile <LegacySaveData>(this.LegacySaveDataRelativePath); // pre-4.6 } if (saveData?.Buildings == null) { return; } // add tractor + garages BuildableGameLocation[] locations = this.GetBuildableLocations().ToArray(); foreach (LegacySaveDataBuilding garageData in saveData.Buildings) { // get location BuildableGameLocation location = locations.FirstOrDefault(p => p.NameOrUniqueName == (garageData.Map ?? "Farm")); if (location == null) { this.Monitor.Log($"Ignored legacy tractor garage in unknown location '{garageData.Map}'.", LogLevel.Warn); continue; } // add garage Stable garage = location.buildings.OfType <Stable>().FirstOrDefault(p => p.tileX.Value == (int)garageData.Tile.X && p.tileY.Value == (int)garageData.Tile.Y); if (garage == null) { garage = new Stable(garageData.TractorID, this.GetBlueprint(), garageData.Tile); garage.daysOfConstructionLeft.Value = 0; location.buildings.Add(garage); } garage.maxOccupants.Value = this.MaxOccupantsID; garage.load(); } }
/********* ** Private methods *********/ /// <summary>Apply legacy save data to the player's current fields, if valid and the field doesn't already have a value.</summary> /// <param name="data">The save data to apply.</param> /// <returns>Returns the player IDs whose fields were changed.</returns> private IEnumerable <long> TryApply(LegacySaveData data) { foreach (var pair in data?.Players ?? new()) { if (pair.Value?.Mana is not > 0 && pair.Value?.ManaCap is not > 0) { continue; } Farmer player = Game1.getFarmerMaybeOffline(pair.Key); if (this.SetMaxManaIfNotSet(player, pair.Value.ManaCap) | this.SetCurrentManaIfNotSet(player, pair.Value.Mana)) { yield return(pair.Key); } } }
/// <summary>Migrate the legacy <c>TractorModSave.json</c> file to the new config files.</summary> /// <param name="helper">Provides simplified APIs for writing mods.</param> private void MigrateLegacySaveData(IModHelper helper) { // get file const string filename = "TractorModSave.json"; FileInfo file = new FileInfo(Path.Combine(helper.DirectoryPath, filename)); if (!file.Exists) { return; } // read legacy data this.Monitor.Log($"Found legacy {filename}, migrating to new save data..."); IDictionary <string, CustomSaveData> saves = new Dictionary <string, CustomSaveData>(); { LegacySaveData data = helper.ReadJsonFile <LegacySaveData>(filename); if (data.Saves != null && data.Saves.Any()) { foreach (LegacySaveData.LegacySaveEntry saveData in data.Saves) { saves[$"{saveData.FarmerName}_{saveData.SaveSeed}"] = new CustomSaveData( saveData.TractorHouse.Select(p => new CustomSaveBuilding(new Vector2(p.X, p.Y), this.GarageBuildingType, "Farm", 0)) ); } } } // write new files foreach (var save in saves) { if (save.Value.Buildings.Any()) { helper.WriteJsonFile(this.GetDataPath(save.Key), save.Value); } } // delete old file file.Delete(); }
/********* ** Private methods *********/ /// <summary>Apply legacy save data to the player's current fields, if valid and the field doesn't already have a value.</summary> /// <param name="legacyData">The save data to apply.</param> /// <returns>Returns the player IDs whose fields were changed.</returns> private IEnumerable <long> TryApply(LegacySaveData legacyData) { foreach (var pair in legacyData?.Players ?? new()) { // get player spellbook Farmer player = Game1.getFarmerMaybeOffline(pair.Key); if (player == null) { continue; } SpellBook book = player.GetSpellBook(); // apply data bool changed = false; book.Mutate(data => { // free spell point if (data.FreePoints <= 0) { int freePoints = pair.Value.FreePoints; if (freePoints > 0) { data.FreePoints = Math.Max(0, freePoints); changed = true; } } // known spells if (!data.KnownSpells.Any()) { PreparedSpell[] knownSpells = (pair.Value.SpellBook?.KnownSpells ?? new()) .Select(p => new PreparedSpell(p.Key, p.Value)) .ToArray(); if (knownSpells.Any()) { data.KnownSpells = knownSpells.ToDictionary(p => p.SpellId); changed = true; } } // prepared spells if (!data.Prepared.Any()) { var preparedSpells = (pair.Value.SpellBook?.Prepared ?? new PreparedSpell[][] { }) .Select(spells => new PreparedSpellBar { Spells = spells.ToList() }) .ToArray(); if (preparedSpells.Any(bar => bar.Spells.Any(spell => spell != null))) { data.Prepared = preparedSpells; changed = true; } } // prepared index if (data.SelectedPrepared <= 0) { int index = pair.Value.SpellBook?.SelectedPrepared ?? 0; if (index > 0) { data.SelectedPrepared = index; changed = true; } } }); if (changed) { yield return(pair.Key); } } }