Facebook
From 박희성, 1 Year ago, written in C#.
This paste is a reply to Wasteland 3 Save Edit Script from JitterJohn - view diff
Embed
Download Paste or View Raw
Hits: 409
  1. void Main()
  2. {
  3.         //Be carefull when editing quicksaves, the backup can be out-of-date if you never delete it.
  4.         var saveName = "Quicksave 1";
  5.        
  6.         //The names of the characters that will be edited.
  7.         //Unique characters like Marshal Kwon don't have a display name
  8.         var pcNames = new HashSet<string> { "William", "Li-Tsing" };
  9.        
  10.         var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), @"My GamesWasteland3Save Games", saveName, saveName + ".bak");
  11.         if(!File.Exists(path))
  12.         {
  13.                 File.Copy(Path.ChangeExtension(path, ".bak"), path);
  14.         }
  15.         var saveData = Load(path);
  16.         var xml = saveData.SaveState;
  17.         //Dumps the xml part of the save to a new output panel with syntax highlighting.
  18.         //PanelManager.DisplaySyntaxColoredText(xml.ToString(), SyntaxLanguageStyle.XML); return;
  19.        
  20.         var pcs =
  21.                 xml.Root.Descendants("pc")
  22.                                 .Where(pc => pc.Element("displayName") != null)
  23.                                 .Where(pc => pcNames.Contains((string)pc.Element("displayName")))
  24.                                 .Dump()
  25.                                 ;
  26.                                
  27.         var attributes = new (string attribute, int value)[]
  28.         {
  29.                 ("coordination",        10),
  30.                 ("luck",                        10),
  31.                 ("awareness",           10),
  32.                 ("strength",            10),
  33.                 ("speed",                       10),
  34.                 ("intelligence",    10),
  35.                 ("charisma",        10),
  36.                
  37.                 //("xp",                        5999),
  38.                 //Current hitpoints
  39.                 //("hitpoints", 491),
  40.                 ("money", 99999),
  41.                
  42.                 ("availableAttributePoints",    0),
  43.                 ("availableSkillPoints",        100),
  44.                 ("perkPoints",                  0),
  45.         };
  46.  
  47.         var newPerks = new (string perkname, int count)[]
  48.         {
  49.                 //-1 is skip, 0 will remove 1+ will add it that many times.
  50.                 //Custom Ranger Backgrounds
  51.                 ("BCK_Bookworm",                        -1),    //5% Experience
  52.                 ("BCK_DesertCat",                       5),     //1 Perception
  53.                 ("BCK_DiscipleOfTheMetal",      1),     //15% Fire Damage
  54.                 ("BCK_Explodomaniac",           1),     //15% Explosive Damage
  55.                 ("BCK_GoatKiller",                      1),     //5% Critical Chance
  56.                 ("BCK_GreaseMonkey",            1),     //10% Damage to Robots & Vehicles
  57.                 ("BCK_LethalWeapon",            1),     //10% Melee Damage
  58.                 ("BCK_Mannerite",                       -1),    //1 Kick Ass
  59.                 ("BCK_Moneybags",                       -1),    //1 Barter
  60.                 ("BCK_MopeyPoet",                       10),    //5% Evasion
  61.                 ("BCK_Paladin",                         10),    //10% Crit Resistance
  62.                 ("BCK_RaiderHater",                     1),     //10% Damage to Humans
  63.                 ("BCK_SexMachine",                      10),    //0.2 Combat Speed
  64.                 ("BCK_Stoner",                          10),    //10% Status Effect Resistance
  65.                 ("BCK_TheBoss",                         -1),    //1 Hard Ass
  66.                 ("BCK_ViciousAvenger",          10),    //2 Penetration
  67.                
  68.                 //Premade Ranger Backgrounds
  69.                 ("BCK_Scout",       1),         //10% Sneak Attack Damage
  70.                 ("BCK_Farmer",      1),         //1 HP/Level
  71.                 ("BCK_Technician",      -1),    //???
  72.                 ("BCK_Nomad",           -1),    //???
  73.                 ("BCK_Bouncer",     1),         //10% Melee Damage
  74.                 ("BCK_Thief",       10),        //1 Second Detection Time
  75.                 ("BCK_Hacker",      1),         //10% Damage to Robots & Synths
  76.                 ("BCK_Evangelical", 1),         //3m Leadership Range
  77.                 ("BCK_Drifter",     10),        //4 Armor
  78.                 ("BCK_Miner",       10),        //15% Explosive Resistance
  79.                 ("BCK_Mercenary",   1),         //10% Crit Resistance
  80.                 ("BCK_Gearhead",        1),             //10% Damage to Vehicles
  81.                 ("BCK_ConArtist",   1),         //5% Initiative
  82.                 ("BCK_Academic",        -1),    //10% Experience
  83.                
  84.                 //Unique Ranger Backgrounds
  85.                 ("BCK_Yuri",    1),             //5% Ranged Damage
  86.                 ("BCK_Spence"1),             //3% Evasion
  87.                 ("BCK_Bronco"1),             //5% Melee Damage
  88.                 ("BCK_Kickboy", 1),             //5% Initiative
  89.                 ("BCK_William", 1),             //0.2 Combat Speed
  90.                 ("BCK_LiTsing", 1),             //10% Sneak Attack Damage
  91.                 ("BCK_Dusty",   1),             //5% Crit Resistance
  92.                 ("BCK_Marie",   -1),    //5% Experience
  93.                 ("BCK_Chris",   1),             //1 Perception
  94.                 ("BCK_Kris",    10),    //10% Energy Resistance, 5% Energy Damage
  95.                
  96.                 //Companion Backgrounds
  97.                 ("BCK_MarshalKwon",             1),             //30% Initiative
  98.                 ("BCK_LuciaWesson",             5),             //5% Strike Rate
  99.                 ("BCK_JodieBell",               10),    //1 Quick Slot
  100.                 ("BCK_Fishlips",                1),             //0.4 Critical Damage
  101.                 ("BCK_IroncladCordite", 1),             //4 Armor
  102.                 ("BCK_Scotchmo",                1),             //10% Status Effect Resistance
  103.                
  104.                 //Quirks
  105.                 ("QRK_None",                    -1),    //
  106.                 ("QRK_DeathWish",               1),     //+3 AP, +3 AP (Max); Cannot Wear Any Kind of Armor
  107.                 ("QRK_DoomsdayPrepper", -1),    //+35% Status Effect Resistance; Cannot Read Skill Books
  108.                 ("QRK_Prospector",              1),     //Occasionally Find Gold Nuggets When Digging for Buried Items; -1 Quick Slot
  109.                 ("QRK_SerialKiller",    1),     //-1 AP; +3 AP Per Kill (Once Per Turn)
  110.                 ("QRK_WasteRoamer",             1),     //100% Resistance to Bleeding, Poisoned, Shocked, Burning, Frozen; -15% Experience
  111.                
  112.                 //Special Perks
  113.                 ("PRK_CyborgTech",      1),     //Equip Cyborg Tech
  114.                 //("PRK_MarshalTraining",       1),     //2m Leadership Range
  115.                
  116.                 //Generic Perks
  117.                 ("PRK_Generic_DeepPockets",             1),     //1 Quick Slot
  118.                 ("PRK_Generic_Hardened",                1),     //2 Armor
  119.                 ("PRK_Generic_Healthy",                 10),    //35 HP
  120.                 ("PRK_Generic_QuickReflexes",   5),     //5% Evasion
  121.                 ("PRK_Generic_Weathered",               1),     //10% Crit Resistance
  122.                
  123.                 //("PRK_DuckAndCover",          5),     //20% Fire & Explosive Resistance
  124.         };
  125.  
  126.         foreach (var pc in pcs)
  127.         {
  128.                 foreach (var attr in attributes)
  129.                 {
  130.                         pc.SetElementValue(attr.attribute, attr.value);
  131.                 }
  132.                 var perks = pc.Element("perks");
  133.                 foreach (var newPerk in newPerks)
  134.                 {
  135.                         perks.EnsurePerkCount(newPerk.perkname, newPerk.count);
  136.                 }
  137.                 perks.Add(new XElement("perk", new XElement("perkname", "BCK_JorenPizepi")));
  138.         }
  139.        
  140.         DuplicateMods(saveData, 5);
  141.        
  142.         Save(Path.ChangeExtension(saveData.Path, ".xml"), saveData);
  143. }
  144.  
  145. public static void DuplicateMods(SaveData saveData, int duplicateCount)
  146. {
  147.         var items = saveData.SaveState.Root.Element("hostInventory");
  148.         foreach (var item in items.Descendants("item").Where(itm => itm.Element("templateName").Value.Contains("Mod_", StringComparison.Ordinal)).ToList())
  149.         {
  150.                 var templateName = item.Element("templateName").Value;
  151.                 while (items.Descendants("item").Where(itm => itm.Element("templateName").Value.Equals(templateName, StringComparison.Ordinal)).Count() < duplicateCount)
  152.                 {
  153.                         var copy = new XElement(item);
  154.                         copy.SetElementValue("uid", Guid.NewGuid());
  155.                         items.Add(copy);
  156.                 }
  157.         }
  158. }
  159.  
  160.  
  161. public sealed class SaveData
  162. {
  163.         public string Path {get;}
  164.         public XDocument SaveState {get;}
  165.         public IReadOnlyList<string> Header {get;}
  166.        
  167.         public SaveData(string path, XDocument saveState, IEnumerable<string> header)
  168.         {
  169.                 Path = path;
  170.                 SaveState = saveState;
  171.                 Header = header.ToList().AsReadOnly();
  172.         }
  173. }
  174.  
  175. public static SaveData Load(string path)
  176. {
  177.         var contents = File.ReadAllBytes(path);
  178.         var index = 0;
  179.         var header = new List<string>();
  180.         for (int lfFound = 0; lfFound < 11; lfFound++)
  181.         {
  182.                 var next = Array.FindIndex(contents, index, b => b == (byte)'n') + 1;
  183.                 header.Add(Encoding.UTF8.GetString(contents, index, next - index));
  184.                 index = next;
  185.         }
  186.        
  187.         var compressed = new byte[contents.Length - index];
  188.         Array.Copy(contents, index, compressed, 0, contents.Length - index);
  189.         var result = CLZF2.Decompress(compressed);
  190.         var xml = XDocument.Load(new MemoryStream(result));
  191.        
  192.         return new SaveData(path, xml, header);
  193. }
  194.  
  195. public static void Save(string path, SaveData saveData)
  196. {
  197.         var tempStream = new MemoryStream();
  198.         var xmlSettings = new XmlWriterSettings
  199.         {
  200.                 OmitXmlDeclaration = true,
  201.                 Indent = false,
  202.         };
  203.         using (var writer = XmlWriter.Create(tempStream, xmlSettings))
  204.         {
  205.                 saveData.SaveState.Save(writer);
  206.         }
  207.         var changedData = tempStream.ToArray();
  208.         var compressedChangedData = CLZF2.Compress(changedData);
  209.         using (var newSave = File.Create(path))
  210.         {
  211.                 for (int i = 0; i < saveData.Header.Count; i++)
  212.                 {
  213.                         var line = saveData.Header[i];
  214.                         if (i == 4 || i == 5)
  215.                         {
  216.                                 line = Regex.Replace(line, @"(d+)", i == 4 ? changedData.Length.ToString() : compressedChangedData.Length.ToString());
  217.                         }
  218.                         var lineBytes = Encoding.UTF8.GetBytes(line);
  219.                         newSave.Write(lineBytes, 0, lineBytes.Length);
  220.                 }
  221.                 newSave.Write(compressedChangedData, 0, compressedChangedData.Length);
  222.         }
  223. }
  224.  
  225. public static class LinqToXmlExtensions
  226. {
  227.         public static void EnsurePerkCount(this XContainer perksContainer, string perkname, int count)
  228.         {
  229.                 if(count < 0)
  230.                         return;
  231.                
  232.                 var chosenPerkEntries =
  233.                         perksContainer.Elements("perk")
  234.                                                   .Where(p => string.Equals(p.Element("perkname").Value, perkname, StringComparison.Ordinal));
  235.                
  236.                 chosenPerkEntries.Skip(count).Remove();
  237.                 var perksToAdd = count - chosenPerkEntries.Count();
  238.                 for (int i = 0; i < perksToAdd; i++)
  239.                 {
  240.                         perksContainer.AddFirst(new XElement("perk", new XElement("perkname", perkname)));
  241.                 }
  242.         }
  243.        
  244.         public static void AddFirstUntilCount(this XContainer container, Func<XElement> factory, Func<XElement, bool> predicate, int count)
  245.         {
  246.                 while (container.Elements().Count(predicate) < count)
  247.                 {
  248.                         container.AddFirst(factory());
  249.                 }
  250.         }
  251. }

Replies to Re: Wasteland 3 Save Edit Script rss

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