Facebook
From 박희성, 3 Years ago, written in C#.
This paste is a reply to Wasteland 3 Save Edit Script from JitterJohn - go back
Embed
void Main()
{
        //Be carefull when editing quicksaves, the backup can be out-of-date if you never delete it.
        var saveName = "Quicksave 1";
        
        //The names of the characters that will be edited.
        //Unique characters like Marshal Kwon don't have a display name
        var pcNames = new HashSet { "William", "Li-Tsing" };
        
        var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), @"My Games\Wasteland3\Save Games\", GamesWasteland3Save Games", saveName, saveName + ".bak");
        if(!File.Exists(path))
        {
                File.Copy(Path.ChangeExtension(path, ".bak"), path);
        }
        var saveData = Load(path);
        var xml = saveData.SaveState;
        //Dumps the xml part of the save to a new output panel with syntax highlighting.
        //PanelManager.DisplaySyntaxColoredText(xml.ToString(), SyntaxLanguageStyle.XML); return;
        
        var pcs =
                xml.Root.Descendants("pc")
                                .Where(pc => pc.Element("displayName") != null)
                                .Where(pc => pcNames.Contains((string)pc.Element("displayName")))
                                .Dump()
                                ;
                                
        var attributes = new (string attribute, int value)[]
        {
                ("coordination",        10),
                ("luck",                        10),
                ("awareness",                 10),
                ("strength",                 10),
                ("speed",                         10),
                ("intelligence",    10),
                ("charisma",        10),
                
                //("xp",                         103),
5999),
                //Current hitpoints
                //("hitpoints",        491),
                ("money", 0),
99999),
                
                ("availableAttributePoints",    0),
                ("availableSkillPoints",        0),
100),
                ("perkPoints",                  0),
        };

        var newPerks = new (string perkname, int count)[]
        {
                //-1 is skip, 0 will remove 1+ will add it that many times.
                //Custom Ranger Backgrounds
                ("BCK_Bookworm",                        -1),        //5% Experience
                ("BCK_DesertCat",                        5),        //1 Perception
                ("BCK_DiscipleOfTheMetal",        1),        //15% Fire Damage
                ("BCK_Explodomaniac",                1),        //15% Explosive Damage
                ("BCK_GoatKiller",                        1),        //5% Critical Chance
                ("BCK_GreaseMonkey",                1),        //10% Damage to Robots & Vehicles
                ("BCK_LethalWeapon",                1),        //10% Melee Damage
                ("BCK_Mannerite",                        -1),        //1 Kick Ass
                ("BCK_Moneybags",                        -1),        //1 Barter
                ("BCK_MopeyPoet",                        10),        //5% Evasion
                ("BCK_Paladin",                                10),        //10% Crit Resistance
                ("BCK_RaiderHater",                        1),        //10% Damage to Humans
                ("BCK_SexMachine",                        10),        //0.2 Combat Speed
                ("BCK_Stoner",                                10),        //10% Status Effect Resistance
                ("BCK_TheBoss",                                -1),        //1 Hard Ass
                ("BCK_ViciousAvenger",                10),        //2 Penetration
                
                //Premade Ranger Backgrounds
                ("BCK_Scout",       1),                //10% Sneak Attack Damage
                ("BCK_Farmer",      1),                //1 HP/Level
                ("BCK_Technician",        -1),        //???
                ("BCK_Nomad",                -1),        //???
                ("BCK_Bouncer",     1),                //10% Melee Damage
                ("BCK_Thief",       10),        //1 Second Detection Time
                ("BCK_Hacker",      1),                //10% Damage to Robots & Synths
                ("BCK_Evangelical", 1),                //3m Leadership Range
                ("BCK_Drifter",     10),        //4 Armor
                ("BCK_Miner",       10),        //15% Explosive Resistance
                ("BCK_Mercenary",   1),                //10% Crit Resistance
                ("BCK_Gearhead",        1),                //10% Damage to Vehicles
                ("BCK_ConArtist",   1),                //5% Initiative
                ("BCK_Academic",        -1),        //10% Experience
                
                //Unique Ranger Backgrounds
                ("BCK_Yuri",        1),                //5% Ranged Damage
                ("BCK_Spence",        1),                //3% Evasion
                ("BCK_Bronco",        1),                //5% Melee Damage
                ("BCK_Kickboy",        1),                //5% Initiative
                ("BCK_William",        1),                //0.2 Combat Speed
                ("BCK_LiTsing", 1),                //10% Sneak Attack Damage
                ("BCK_Dusty",        1),                //5% Crit Resistance
                ("BCK_Marie",        -1),        //5% Experience
                ("BCK_Chris",        1),                //1 Perception
                ("BCK_Kris",        10),        //10% Energy Resistance, 5% Energy Damage
                
                //Companion Backgrounds
                ("BCK_MarshalKwon",                1),                //30% Initiative
                ("BCK_LuciaWesson",                5),                //5% Strike Rate
                ("BCK_JodieBell",                10),        //1 Quick Slot
                ("BCK_Fishlips",                1),                //0.4 Critical Damage
                ("BCK_IroncladCordite",        1),                //4 Armor
                ("BCK_Scotchmo",                1),                //10% Status Effect Resistance
                
                //Quirks
                ("QRK_None",                        -1),        //
                ("QRK_DeathWish",                1),        //+3 AP, +3 AP (Max); Cannot Wear Any Kind of Armor
                ("QRK_DoomsdayPrepper",        -1),        //+35% Status Effect Resistance; Cannot Read Skill Books
                ("QRK_Prospector",                1),        //Occasionally Find Gold Nuggets When Digging for Buried Items; -1 Quick Slot
                ("QRK_SerialKiller",        1),        //-1 AP; +3 AP Per Kill (Once Per Turn)
                ("QRK_WasteRoamer",                1),        //100% Resistance to Bleeding, Poisoned, Shocked, Burning, Frozen; -15% Experience
                
                //Special Perks
                ("PRK_CyborgTech",          1),        //Equip Cyborg Tech
                //("PRK_MarshalTraining",        1),        //2m Leadership Range
                
                //Generic Perks
                ("PRK_Generic_DeepPockets",                1),        //1 Quick Slot
                ("PRK_Generic_Hardened",                1),        //2 Armor
                ("PRK_Generic_Healthy",                        10),        //35 HP
                ("PRK_Generic_QuickReflexes",         5),        //5% Evasion
                ("PRK_Generic_Weathered",                1),        //10% Crit Resistance
                
                //("PRK_DuckAndCover",                5),        //20% Fire & Explosive Resistance
        };

        foreach (var pc in pcs)
        {
                foreach (var attr in attributes)
                {
                        pc.SetElementValue(attr.attribute, attr.value);
                }
                var perks = pc.Element("perks");
                foreach (var newPerk in newPerks)
                {
                        perks.EnsurePerkCount(newPerk.perkname, newPerk.count);
                }
                perks.Add(new XElement("perk", new XElement("perkname", "BCK_JorenPizepi")));
        }
        
        DuplicateMods(saveData, 5);
        
        Save(Path.ChangeExtension(saveData.Path, ".xml"), saveData);
}

public static void DuplicateMods(SaveData saveData, int duplicateCount)
{
        var items = saveData.SaveState.Root.Element("hostInventory");
        foreach (var item in items.Descendants("item").Where(itm => itm.Element("templateName").Value.Contains("Mod_", StringComparison.Ordinal)).ToList())
        {
                var templateName = item.Element("templateName").Value;
                while (items.Descendants("item").Where(itm => itm.Element("templateName").Value.Equals(templateName, StringComparison.Ordinal)).Count() < duplicateCount)
                {
                        var copy = new XElement(item);
                        copy.SetElementValue("uid", Guid.NewGuid());
                        items.Add(copy);
                }
        }
}


public sealed class SaveData 
{
        public string Path {get;}
        public XDocument SaveState {get;}
        public IReadOnlyList Header {get;}
        
        public SaveData(string path, XDocument saveState, IEnumerable header)
        {
                Path = path;
                SaveState = saveState;
                Header = header.ToList().AsReadOnly();
        }
}

public static SaveData Load(string path)
{
        var contents = File.ReadAllBytes(path);
        var index = 0;
        var header = new List();
        for (int lfFound = 0; lfFound < 11; lfFound++)
        {
                var next = Array.FindIndex(contents, index, b => b == (byte)'\n') (byte)'n') + 1;
                header.Add(Encoding.UTF8.GetString(contents, index, next - index));
                index = next;
        }
        
        var compressed = new byte[contents.Length - index];
        Array.Copy(contents, index, compressed, 0, contents.Length - index);
        var result = CLZF2.Decompress(compressed);
        var xml = XDocument.Load(new MemoryStream(result));
        
        return new SaveData(path, xml, header);
}

public static void Save(string path, SaveData saveData)
{
        var tempStream = new MemoryStream();
        var xmlSettings = new XmlWriterSettings
        {
                OmitXmlDeclaration = true,
                Indent = false,
        };
        using (var writer = XmlWriter.Create(tempStream, xmlSettings))
        {
                saveData.SaveState.Save(writer);
        }
        var changedData = tempStream.ToArray();
        var compressedChangedData = CLZF2.Compress(changedData);
        using (var newSave = File.Create(path))
        {
                for (int i = 0; i < saveData.Header.Count; i++)
                {
                        var line = saveData.Header[i];
                        if (i == 4 || i == 5)
                        {
                                line = Regex.Replace(line, @"(\d+)", @"(d+)", i == 4 ? changedData.Length.ToString() : compressedChangedData.Length.ToString());
                        }
                        var lineBytes = Encoding.UTF8.GetBytes(line);
                        newSave.Write(lineBytes, 0, lineBytes.Length);
                }
                newSave.Write(compressedChangedData, 0, compressedChangedData.Length);
        }
}

public static class LinqToXmlExtensions
{
        public static void EnsurePerkCount(this XContainer perksContainer, string perkname, int count)
        {
                if(count < 0)
                        return;
                
                var chosenPerkEntries =
                        perksContainer.Elements("perk")
                                                  .Where(p => string.Equals(p.Element("perkname").Value, perkname, StringComparison.Ordinal));
                
                chosenPerkEntries.Skip(count).Remove();
                var perksToAdd = count - chosenPerkEntries.Count();
                for (int i = 0; i < perksToAdd; i++)
                {
                        perksContainer.AddFirst(new XElement("perk", new XElement("perkname", perkname)));
                }
        }
        
        public static void AddFirstUntilCount(this XContainer container, Func factory, Func predicate, int count)
        {
                while (container.Elements().Count(predicate) < count)
                {
                        container.AddFirst(factory());
                }
        }
}

Replies to Re: Wasteland 3 Save Edit Script rss

Title Name Language When
Re: Re: Wasteland 3 Save Edit Script Mammoth Agouti csharp 2 Years ago.