public static Gesture InstallRandomGestureAsset(IMEPackage package, float minSequenceLength = 0, MERPackageCache cache = null) { var gestureFiles = MERUtilities.ListStaticAssets("binary.gestures"); var randGestureFile = gestureFiles.RandomElement(); cache ??= new MERPackageCache(); var gPackage = cache.GetCachedPackageEmbedded(randGestureFile, isFullPath: true); var options = gPackage.Exports.Where(x => x.ClassName == "AnimSequence").ToList(); // Pick a random element, make sure it's long enough var randomGestureExport = options.RandomElement(); var seqLength = randomGestureExport.GetProperty <FloatProperty>("SequenceLength"); int numRetries = 5; while (seqLength < minSequenceLength) { randomGestureExport = options.RandomElement(); seqLength = randomGestureExport.GetProperty <FloatProperty>("SequenceLength"); numRetries--; } var portedInExp = PackageTools.PortExportIntoPackage(package, randomGestureExport); return(new Gesture(portedInExp)); }
private static void InstallBorger() { var endGame3F = MERFileSystem.GetPackageFile("BioP_EndGm3.pcc"); if (endGame3F != null && File.Exists(endGame3F)) { var biopEndGm3 = MEPackageHandler.OpenMEPackage(endGame3F); var packageBin = MERUtilities.GetEmbeddedStaticFilesBinaryFile("Delux2go_Edmonton_Burger.pcc"); var burgerPackage = MEPackageHandler.OpenMEPackageFromStream(new MemoryStream(packageBin)); // 1. Add the burger package var burgerMDL = PackageTools.PortExportIntoPackage(biopEndGm3, burgerPackage.FindExport("Edmonton_Burger_Delux2go.Burger_MDL")); // 2. Link up the textures TFCBuilder.RandomizeExport(biopEndGm3.FindExport("Edmonton_Burger_Delux2go.Textures.Burger_Diff"), null); TFCBuilder.RandomizeExport(biopEndGm3.FindExport("Edmonton_Burger_Delux2go.Textures.Burger_Norm"), null); TFCBuilder.RandomizeExport(biopEndGm3.FindExport("Edmonton_Burger_Delux2go.Textures.Burger_Spec"), null); // 3. Convert the collector base into lunch or possibly early dinner // It's early dinner cause that thing will keep you full all night long biopEndGm3.GetUExport(11276).WriteProperty(new ObjectProperty(burgerMDL.UIndex, "SkeletalMesh")); biopEndGm3.GetUExport(11282).WriteProperty(new ObjectProperty(burgerMDL.UIndex, "SkeletalMesh")); MERFileSystem.SavePackage(biopEndGm3); } }
private async System.Threading.Tasks.Task InitialzeEnvAndPage() { LoadingText.Text = "加载广告"; PrepareAds(); if (IsAppUpdated || SystemInformation.IsFirstRun) { LoadingText.Text = "解压 AriaNg"; await PackageTools.UnZip(new Uri("ms-appx:///webui.zip")); ContentDialog contentDialog = new ContentDialog() { IsPrimaryButtonEnabled = true, Title = "还差一步", Content = "激活Aria2,本次启动可能无法正常连接,如果连接失败请重新启动程序", PrimaryButtonText = "激活" }; contentDialog.PrimaryButtonClick += async(e, a) => { await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync(); }; await contentDialog.ShowAsync(); } else { LoadingText.Text = "加载Aria2"; await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync(); } LoadingText.Text = "处理Session"; await GetSessionAndStartAriaNG(); }
private static void RandomizeDancer(ExportEntry skeletalMeshActorMatArchetype) { // Install new head and body assets var newInfo = IlliumHub.DancerOptions.RandomElement(); while (newInfo.Location != null || newInfo.Rotation != null || (newInfo.BodyAsset != null && !newInfo.BodyAsset.IsAssetFileAvailable()) || (newInfo.HeadAsset != null && !newInfo.HeadAsset.IsAssetFileAvailable())) { // Make sure assets are available, if not, repick // I don't want anything that requires specific positioning data newInfo = IlliumHub.DancerOptions.RandomElement(); } var newBody = PackageTools.PortExportIntoPackage(skeletalMeshActorMatArchetype.FileRef, newInfo.BodyAsset.GetAsset()); var bodySM = skeletalMeshActorMatArchetype.GetProperty <ObjectProperty>("SkeletalMeshComponent").ResolveToEntry(skeletalMeshActorMatArchetype.FileRef) as ExportEntry; var headSM = skeletalMeshActorMatArchetype.GetProperty <ObjectProperty>("HeadMesh").ResolveToEntry(skeletalMeshActorMatArchetype.FileRef) as ExportEntry; bodySM.WriteProperty(new ObjectProperty(newBody.UIndex, "SkeletalMesh")); if (newInfo.HeadAsset != null) { var newHead = PackageTools.PortExportIntoPackage(skeletalMeshActorMatArchetype.FileRef, newInfo.HeadAsset.GetAsset()); headSM.WriteProperty(new ObjectProperty(newHead.UIndex, "SkeletalMesh")); } else if (!newInfo.KeepHead) { headSM.RemoveProperty("SkeletalMesh"); } if (newInfo.DrawScale != 1) { // Install DS3D on the archetype. It works. Not gonna question it var ds = new CFVector3() { X = newInfo.DrawScale, Y = newInfo.DrawScale, Z = newInfo.DrawScale, }; skeletalMeshActorMatArchetype.WriteProperty(ds.ToLocationStructProperty("DrawScale3D")); //hack } if (newInfo.MorphFace != null) { var newHead = PackageTools.PortExportIntoPackage(skeletalMeshActorMatArchetype.FileRef, newInfo.MorphFace.GetAsset()); headSM.WriteProperty(new ObjectProperty(newHead.UIndex, "MorphHead")); } }
private static void RandomizeALDancers() { { var denBar = MERFileSystem.GetPackageFile(@"BioD_OmgHub_220DenBar.pcc"); if (denBar != null) { var denBarP = MEPackageHandler.OpenMEPackage(denBar); RandomizeDancer(denBarP.GetUExport(1287)); RandomizeDancer(denBarP.GetUExport(1288)); RandomizeDancer(denBarP.GetUExport(1289)); RandomizeDancer(denBarP.GetUExport(1292)); RandomizeDancer(denBarP.GetUExport(1293)); MERFileSystem.SavePackage(denBarP); } } var denDance = MERFileSystem.GetPackageFile(@"BioD_OmgHub_230DenDance.pcc"); if (denDance != null) { var denDanceP = MEPackageHandler.OpenMEPackage(denDance); RandomizeDancer(denDanceP.GetUExport(1257)); //sit RandomizeDancer(denDanceP.GetUExport(1250)); RandomizeDancer(denDanceP.GetUExport(1251)); // shep sits at dancer. it uses different pawn. var entertainerBPSKM = denDanceP.GetUExport(4322); var newInfo = IlliumHub.DancerOptions.RandomElement(); while (newInfo.Location != null || newInfo.Rotation != null || newInfo.KeepHead == false || (newInfo.BodyAsset != null && !newInfo.BodyAsset.IsAssetFileAvailable()) || (newInfo.HeadAsset != null && !newInfo.HeadAsset.IsAssetFileAvailable())) { // I don't want anything that requires specific positioning data, and I want to keep the head. newInfo = IlliumHub.DancerOptions.RandomElement(); } var newDancerMDL = PackageTools.PortExportIntoPackage(denDanceP, newInfo.BodyAsset.GetAsset()); entertainerBPSKM.WriteProperty(new ObjectProperty(newDancerMDL, "SkeletalMesh")); MERFileSystem.SavePackage(denDanceP); } }
internal static bool RandomizeExport(ExportEntry export, RandomizationOption option) { if (!CanRandomize(export)) { return(false); } #if DEBUG //if (!export.ObjectName.Name.Contains("HeavyWeaponMech")) // return false; #endif var powers = export.GetProperty <ArrayProperty <ObjectProperty> >("Powers"); if (powers == null) { // This loadout has no powers! // Randomly give them some powers. if (ThreadSafeRandom.Next(1) == 0) { // unlimited power List <ObjectProperty> blankPows = new List <ObjectProperty>(); // Add two blanks. We'll strip blanks before writing it blankPows.Add(new ObjectProperty(int.MinValue)); blankPows.Add(new ObjectProperty(int.MinValue)); powers = new ArrayProperty <ObjectProperty>(blankPows, "Powers"); } else { // Sorry mate no powers for you return(false); } } var originalPowerUIndexes = powers.Where(x => x.Value > 0).Select(x => x.Value).ToList(); foreach (var power in powers.ToList()) { if (power.Value == 0) { return(false); // Null entry in weapons list } IEntry existingPowerEntry = null; if (power.Value != int.MinValue) { // Husk AI kinda depends on melee or they just kinda breath on you all creepy like // We'll give them a chance to change it up though existingPowerEntry = power.ResolveToEntry(export.FileRef); if (existingPowerEntry.ObjectName.Name.Contains("Melee", StringComparison.InvariantCultureIgnoreCase) && ThreadSafeRandom.Next(2) == 0) { MERLog.Information($"Not changing melee power {existingPowerEntry.ObjectName.Name}"); continue; // Don't randomize power } if (PowersToNotSwap.Contains(existingPowerEntry.ObjectName.Name)) { MERLog.Information($"Not changing power {existingPowerEntry.ObjectName.Name}"); continue; // Do not change this power } } // DEBUG PowerInfo randomNewPower = Powers.RandomElement(); //if (option.SliderValue < 0) //{ // randomNewPower = Powers.RandomElement(); //} //else //{ // randomNewPower = Powers[(int)option.SliderValue]; //} // Prevent krogan from getting a death power while (export.ObjectName.Name.Contains("Krogan", StringComparison.InvariantCultureIgnoreCase) && randomNewPower.Type == EPowerCapabilityType.Death) { MERLog.Information(@"Re-roll no-death-power on krogan"); // Reroll. Krogan AI has something weird about it randomNewPower = Powers.RandomElement(); } // Prevent powerful enemies from getting super stat boosters while (randomNewPower.Type == EPowerCapabilityType.Buff && ( export.ObjectName.Name.Contains("Praetorian", StringComparison.InvariantCultureIgnoreCase) || export.ObjectName.Name.Contains("ShadowBroker", StringComparison.InvariantCultureIgnoreCase))) { MERLog.Information(@"Re-roll no-buffs for powerful enemy"); randomNewPower = Powers.RandomElement(); } #region YMIR MECH fixes if (export.ObjectName.Name.Contains("HeavyWeaponMech")) { // Heavy weapon mech chooses named death powers so we cannot change these // HeavyMechDeathExplosion is checked for existence. NormalExplosion for some reason isn't if ((existingPowerEntry.ObjectName.Name == "SFXPower_HeavyMechNormalExplosion")) { MERLog.Information($@"YMIR mech power HeavyMechNormalExplosion cannot be randomized, skipping"); continue; } // Do not add buff powers to YMIR while (randomNewPower.Type == EPowerCapabilityType.Buff) { MERLog.Information($@"Re-roll YMIR mech power to prevent potential enemy too difficult to kill softlock. Incompatible power: {randomNewPower.PowerName}"); randomNewPower = Powers.RandomElement(); } } #endregion // CHANGE THE POWER if (existingPowerEntry == null || randomNewPower.PowerName != existingPowerEntry.ObjectName) { if (powers.Any(x => power.Value != int.MinValue && power.ResolveToEntry(export.FileRef).ObjectName == randomNewPower.PowerName)) { continue; // Duplicate powers crash the game. It seems this code is not bulletproof here and needs changed a bit... } MERLog.Information($@"Changing power {export.ObjectName} {existingPowerEntry?.ObjectName ?? "(+New Power)"} => {randomNewPower.PowerName }"); // It's a different power. // See if we need to port this in var fullName = randomNewPower.PackageName + "." + randomNewPower.PowerName; // SFXGameContent_Powers.SFXPower_Hoops var existingVersionOfPower = export.FileRef.FindEntry(fullName); if (existingVersionOfPower != null) { // Power does not need ported in power.Value = existingVersionOfPower.UIndex; } else { // Power needs ported in power.Value = PortPowerIntoPackage(export.FileRef, randomNewPower, out _)?.UIndex ?? int.MinValue; } if (existingPowerEntry != null && existingPowerEntry.UIndex > 0 && PackageTools.IsPersistentPackage(export.FileRef.FilePath)) { // Make sure we add the original power to the list of referenced memory objects // so subfiles that depend on this power existing don't crash the game! var world = export.FileRef.FindExport("TheWorld"); var worldBin = ObjectBinary.From <World>(world); var extraRefs = worldBin.ExtraReferencedObjects.ToList(); extraRefs.Add(new UIndex(existingPowerEntry.UIndex)); worldBin.ExtraReferencedObjects = extraRefs.Distinct().ToArray(); // Filter out duplicates that may have already been in package world.WriteBinary(worldBin); } foreach (var addlPow in randomNewPower.AdditionalRequiredPowers) { var existingPow = export.FileRef.FindEntry(addlPow); //if (existingPow == null && randomNewPower.ImportOnly && sourcePackage != null) //{ // existingPow = PackageTools.CreateImportForClass(sourcePackage.FindExport(randomNewPower.PackageName + "." + randomNewPower.PowerName), export.FileRef); //} if (existingPow == null) { Debugger.Break(); } powers.Add(new ObjectProperty(existingPow)); } } } // Strip any blank powers we might have added, remove any duplicates powers.RemoveAll(x => x.Value == int.MinValue); powers.ReplaceAll(powers.ToList().Distinct()); //tolist prevents concurrent modification in nested linq // DEBUG #if DEBUG var duplicates = powers .GroupBy(i => i.Value) .Where(g => g.Count() > 1) .Select(g => g.Key).ToList(); if (duplicates.Any()) { foreach (var dup in duplicates) { Debug.WriteLine($"DUPLICATE POWER IN LOADOUT {export.FileRef.GetEntry(dup).ObjectName}"); } Debugger.Break(); } #endif export.WriteProperty(powers); // Our precalculated map should have accounted for imports already, so we odn't need to worry about missing imports upstream // If this is not a master or localization file (which are often used for imports) // Change the number around so it will work across packages. // May need disabled if game becomes unstable. // We check if less than 10 as it's very unlikely there will be more than 10 loadouts in a non-persistent package // if it's > 10 it's likely already a memory-changed item by MER var pName = Path.GetFileName(export.FileRef.FilePath); if (export.indexValue < 10 && !PackageTools.IsPersistentPackage(pName) && !PackageTools.IsLocalizationPackage(pName)) { export.ObjectName = new NameReference(export.ObjectName, ThreadSafeRandom.Next(2000)); } if (originalPowerUIndexes.Any()) { // We should ensure the original objects are still referenced so shared objects they have (vfx?) are kept in memory // Dunno if this actually fixes the problems... PackageTools.AddReferencesToWorld(export.FileRef, originalPowerUIndexes.Select(x => export.FileRef.GetUExport(x))); } return(true); }
/// <summary> /// Ports a power into a package /// </summary> /// <param name="targetPackage"></param> /// <param name="powerInfo"></param> /// <param name="additionalPowers">A list of additioanl powers that are referenced when this powerinfo is an import only power (prevent re-opening package)</param> /// <returns></returns> public static IEntry PortPowerIntoPackage(IMEPackage targetPackage, PowerInfo powerInfo, out IMEPackage sourcePackage) { if (powerInfo.IsCorrectedPackage) { var sourceData = MERUtilities.GetEmbeddedStaticFilesBinaryFile("correctedloadouts.powers." + powerInfo.PackageFileName); sourcePackage = MEPackageHandler.OpenMEPackageFromStream(new MemoryStream(sourceData)); } else { sourcePackage = NonSharedPackageCache.Cache.GetCachedPackage(powerInfo.PackageFileName); } if (sourcePackage != null) { var sourceExport = sourcePackage.GetUExport(powerInfo.SourceUIndex); if (!sourceExport.InheritsFrom("SFXPower") || sourceExport.IsDefaultObject) { throw new Exception("Wrong setup!"); } if (sourceExport.Parent != null && sourceExport.Parent.ClassName != "Package") { throw new Exception("Cannot port power - parent object is not Package!"); } var newParent = EntryExporter.PortParents(sourceExport, targetPackage); IEntry newEntry; if (powerInfo.ImportOnly) { //Debug.WriteLine($"ImportOnly in file {targetPackage.FilePath}"); if (powerInfo.RequiresStartupPackage) { ThreadSafeDLCStartupPackage.AddStartupPackage(Path.GetFileNameWithoutExtension(powerInfo.PackageFileName)); if (powerInfo.IsCorrectedPackage) { // File must be added to MERFS DLC var outP = Path.Combine(MERFileSystem.DLCModCookedPath, powerInfo.PackageFileName); if (!File.Exists(outP)) { sourcePackage.Save(outP, true); } } } newEntry = PackageTools.CreateImportForClass(sourceExport, targetPackage, newParent); // Port in extra imports so the calling class can reference them as necessary. foreach (var addlPow in powerInfo.AdditionalRequiredPowers) { var addlSourceExp = sourcePackage.FindExport(addlPow); PackageTools.CreateImportForClass(addlSourceExp, targetPackage, EntryExporter.PortParents(addlSourceExp, targetPackage)); } } else { #if DEBUG // DEBUG ONLY----------------------------------- //var defaults = sourceExport.GetDefaults(); //defaults.RemoveProperty("VFX"); //var vfx = defaults.GetProperty<ObjectProperty>("VFX").ResolveToEntry(sourcePackage) as ExportEntry; //vxx.RemoveProperty("PlayerCrust"); //vfx.FileRef.GetUExport(1544).RemoveProperty("oPrefab"); ////vfx = defaults.FileRef.GetUExport(6211); // Prefab ////vfx.RemoveProperty("WorldImpactVisualEffect"); //MERPackageCache cached = new MERPackageCache(); //EntryExporter.ExportExportToPackage(vfx, targetPackage, out newEntry, cached); //PackageTools.AddReferencesToWorld(targetPackage, new [] {newEntry as ExportEntry}); //return null; // END DEBUG ONLY-------------------------------- #endif List <EntryStringPair> relinkResults = null; if ((powerInfo.IsCorrectedPackage || (PackageTools.IsPersistentPackage(powerInfo.PackageFileName) && MERFileSystem.GetPackageFile(powerInfo.PackageFileName.ToLocalizedFilename()) == null))) { // Faster this way, without having to check imports Dictionary <IEntry, IEntry> crossPCCObjectMap = new Dictionary <IEntry, IEntry>(); // Not sure what this is used for these days. SHould probably just be part of the method relinkResults = EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, sourceExport, targetPackage, newParent, true, out newEntry, crossPCCObjectMap); } else { // MEMORY SAFE (resolve imports to exports) MERPackageCache cache = new MERPackageCache(); relinkResults = EntryExporter.ExportExportToPackage(sourceExport, targetPackage, out newEntry, cache); } if (relinkResults.Any()) { Debugger.Break(); } } return(newEntry); } return(null); // No package was found }
public static bool RandomizeBasicGestures(ExportEntry export, RandomizationOption option) { if (!CanRandomize(export)) { return(false); } if (export.GetProperty <ObjectProperty>("SkeletalMeshComponent")?.ResolveToEntry(export.FileRef) is ExportEntry smc) { //Debug.WriteLine($"Installing new lite animations for {export.InstancedFullPath}"); var animsets = smc.GetProperty <ArrayProperty <ObjectProperty> >("AnimSets"); var animTreeTemplate = smc.GetProperty <ObjectProperty>("AnimTreeTemplate")?.ResolveToEntry(export.FileRef) as ExportEntry; if (animsets != null && animTreeTemplate != null) { int numAnimationsSupported = 0; foreach (var animsetO in animsets) { var animset = animsetO.ResolveToEntry(export.FileRef) as ExportEntry; var sequences = animset.GetProperty <ArrayProperty <ObjectProperty> >("Sequences"); numAnimationsSupported += sequences.Count; } smc.RemoveProperty("AnimSets"); // We want to force new animations. we'll waste a bit of memory doing this but oh well List <RBioEvtSysTrackGesture.Gesture> installedGestures = new List <RBioEvtSysTrackGesture.Gesture>(); var animationPackagesCache = new MERPackageCache(); while (numAnimationsSupported > 0) { // should we make sure they're unique? var randGest = RBioEvtSysTrackGesture.InstallRandomFilteredGestureAsset(export.FileRef, 2, smaKeywords, null, null, true); InstallDynamicAnimSetRefForSkeletalMesh(smc, randGest); installedGestures.Add(randGest); numAnimationsSupported--; } animationPackagesCache.ReleasePackages(); var isSubfile = PackageTools.IsLevelSubfile(Path.GetFileName(export.FileRef.FilePath)); if (isSubfile) { var newName = export.ObjectName + "_MER"; export.ObjectName = new NameReference(newName, ThreadSafeRandom.Next(25685462)); } // Update the anim tree to use the new animations // Too lazy to properly trace to find nodes. Just take children of this node that are AnimNodeSequences // Add blend times to nodes so they 'blend' together a bit more, look a bit less jank SetupChildrenBlend(animTreeTemplate); // If the animtree has 'DebugPostLoad' flag, it means MER already is using this for something else // We need to generate a new tree so the animations work properly if (animTreeTemplate.ObjectFlags.Has(UnrealFlags.EObjectFlags.DebugPostLoad)) { animTreeTemplate = EntryCloner.CloneTree(animTreeTemplate, true); animTreeTemplate.ObjectName = export.FileRef.GetNextIndexedName("MER_AnimTree"); // New name smc.WriteProperty(new ObjectProperty(animTreeTemplate, "AnimTreeTemplate")); // Write the template back } else if (isSubfile) { // if it's a subfile it won't be used as an import // Let's rename this object animTreeTemplate.ObjectName = new NameReference("MER_AnimTree", ThreadSafeRandom.Next(200000000)); // New name } var animNodeSequences = export.FileRef.Exports.Where(x => x.idxLink == animTreeTemplate.UIndex && x.IsA("AnimNodeSequence")).ToList(); for (int i = 0; i < installedGestures.Count; i++) { var installedG = installedGestures[i]; var ans = animNodeSequences[i]; ans.WriteProperty(new NameProperty(installedG.GestureAnim, "AnimSeqName")); } animTreeTemplate.ObjectFlags |= UnrealFlags.EObjectFlags.DebugPostLoad; // Set as used return(true); } } return(false); }
private static void RandomizeDancer() { var loungeF = MERFileSystem.GetPackageFile("BioD_TwrHub_202Lounge.pcc"); if (loungeF != null && File.Exists(loungeF)) { var package = MEPackageHandler.OpenMEPackage(loungeF); var bodySM = package.GetUExport(4509); var headSM = package.GetUExport(2778); // Install new head and body assets var newInfo = DancerOptions.RandomElement(); while (newInfo.BodyAsset != null && !newInfo.BodyAsset.IsAssetFileAvailable()) { // Find another asset that is available MERLog.Information($@"Asset {newInfo.BodyAsset.AssetPath} in {newInfo.BodyAsset.PackageFile} not available, repicking..."); newInfo = DancerOptions.RandomElement(); } var newBody = PackageTools.PortExportIntoPackage(package, newInfo.BodyAsset.GetAsset()); bodySM.WriteProperty(new ObjectProperty(newBody.UIndex, "SkeletalMesh")); if (newInfo.HeadAsset != null) { var newHead = PackageTools.PortExportIntoPackage(package, newInfo.HeadAsset.GetAsset()); headSM.WriteProperty(new ObjectProperty(newHead.UIndex, "SkeletalMesh")); } else if (!newInfo.KeepHead) { headSM.RemoveProperty("SkeletalMesh"); } if (newInfo.DrawScale != 1) { // Install DS3D on the archetype. It works. Not gonna question it var ds = new CFVector3() { X = newInfo.DrawScale, Y = newInfo.DrawScale, Z = newInfo.DrawScale, }; package.GetUExport(619).WriteProperty(ds.ToLocationStructProperty("DrawScale3D")); //hack } // Install any updates to locations/rotations var dancerInstance = package.GetUExport(4510); // contains location data for dancer which may need to be slightly adjusted if (newInfo.Location != null) { dancerInstance.WriteProperty(newInfo.Location.ToLocationStructProperty("Location")); } if (newInfo.Rotation != null) { dancerInstance.WriteProperty(newInfo.Rotation.ToRotatorStructProperty("Rotation")); } if (newInfo.MorphFace != null) { var newHead = PackageTools.PortExportIntoPackage(package, newInfo.MorphFace.GetAsset()); headSM.WriteProperty(new ObjectProperty(newHead.UIndex, "MorphHead")); } MERFileSystem.SavePackage(package); } }
public static bool RandomizeNPCExport2(ExportEntry export, RandomizationOption randOption) { if (!CanRandomizeNPCExport2(export)) { return(false); } var props = export.GetProperties(); var isIconic = props.GetProp <BoolProperty>("bIconicAppearance"); if (isIconic != null && isIconic) { return(false); // Don't modify an iconic look as it has a bunch fo stuff in it that can totally break it like scalp seams. } Dictionary <string, CFVector4> vectorValues = new(); Dictionary <string, float> scalarValues = new(); if (export.IsA("BioPawn")) { ChangeColorsInSubObjects(export, vectorValues, scalarValues, props); } else { // It's a SFXSkeletalMeshActorMAT, a basic type of NPC. var parms = VectorParameter.GetVectorParameters(export); if (parms != null) { foreach (var parm in parms) { vectorValues[parm.ParameterName] = parm.ParameterValue; RStructs.RandomizeTint(parm.ParameterValue, false); } VectorParameter.WriteVectorParameters(export, parms, "VectorParameters"); // Get submaterials and write out their properties too ChangeColorsInSubObjects(export, vectorValues, scalarValues, props); } // Should we try to randomize things that don't have a skin tone...? if (export.ObjectFlags.Has(UnrealFlags.EObjectFlags.ArchetypeObject) && PackageTools.IsLevelSubfile(Path.GetFileName(export.FileRef.FilePath))) { export.indexValue = ThreadSafeRandom.Next(); } } return(true); }
private static bool RandomizeWeaponLoadout(ExportEntry export, RandomizationOption option) { // Check for blacklisted changes // HACK FOR NOW until we have better solution in place if (export.ObjectName.Name.Contains("HammerHead", StringComparison.InvariantCultureIgnoreCase)) { return(false); // Do not randomize hammerhead } var guns = export.GetProperty <ArrayProperty <ObjectProperty> >("Weapons"); if (guns.Count == 1) //Randomizing multiple guns could be difficult and I'm not sure enemies ever change their weapons. { var gun = guns[0]; if (gun.Value == 0) { return(false); // Null entry in weapons list } var allowedGuns = GetAllowedWeaponsForLoadout(export); if (allowedGuns.Any()) { var randomNewGun = allowedGuns.RandomElementByWeight(x => x.Weight); if (option.HasSliderOption && option.SliderValue >= 0) { randomNewGun = AllAvailableWeapons[(int)option.SliderValue]; } //if (ThreadSafeRandom.Next(1) == 0) //{ // randomNewGun = allowedGuns.FirstOrDefault(x => x.GunName.Contains("GrenadeLauncher")); //} var originalGun = gun.ResolveToEntry(export.FileRef); if (randomNewGun.GunName != originalGun.ObjectName) { var gunInfo = randomNewGun; MERLog.Information($@"Changing gun {export.ObjectName} => {randomNewGun.GunName}"); // It's a different gun. // See if we need to port this in var fullName = gunInfo.PackageName + "." + randomNewGun.GunName; var repoint = export.FileRef.FindEntry(fullName); if (repoint != null) { // Gun does not need ported in gun.Value = repoint.UIndex; } else { // Gun needs ported in var newEntry = PortWeaponIntoPackage(export.FileRef, randomNewGun); gun.Value = newEntry.UIndex; } //if (!tried) export.WriteProperty(guns); var pName = Path.GetFileName(export.FileRef.FilePath); // If this is not a master or localization file (which are often used for imports) // Change the number around so it will work across packages. // May need disabled if game becomes unstable. // We check if less than 10 as it's very unlikely there will be more than 10 loadouts in a non-persistent package // if it's > 10 it's likely already a memory-changed item by MER var isPersistentPackage = PackageTools.IsPersistentPackage(pName); if (export.indexValue < 10 && !isPersistentPackage && !PackageTools.IsLocalizationPackage(pName)) { export.ObjectName = new NameReference(export.ObjectName, ThreadSafeRandom.Next(2000) + 10); } else if (isPersistentPackage && originalGun.UIndex > 0) { // Make sure we add the original gun to the list of referenced memory objects // so subfiles that depend on this gun existing don't crash the game! var world = export.FileRef.FindExport("TheWorld"); var worldBin = ObjectBinary.From <World>(world); var extraRefs = worldBin.ExtraReferencedObjects.ToList(); extraRefs.Add(new UIndex(originalGun.UIndex)); worldBin.ExtraReferencedObjects = extraRefs.Distinct().ToArray(); // Filter out duplicates that may have already been in package world.WriteBinary(worldBin); } tried = true; } } return(true); } return(false); }
public static IEntry PortWeaponIntoPackage(IMEPackage targetPackage, GunInfo gunInfo) { IMEPackage sourcePackage; if (gunInfo.IsCorrectedPackage) { var sourceData = MERUtilities.GetEmbeddedStaticFilesBinaryFile("correctedloadouts.weapons." + gunInfo.PackageFileName); sourcePackage = MEPackageHandler.OpenMEPackageFromStream(new MemoryStream(sourceData)); if (gunInfo.ImportOnly) { // We need to install this file var outfname = Path.Combine(MERFileSystem.DLCModCookedPath, gunInfo.PackageFileName); if (!File.Exists(outfname)) { sourcePackage.Save(outfname, true); ThreadSafeDLCStartupPackage.AddStartupPackage(Path.GetFileNameWithoutExtension(gunInfo.PackageFileName)); } } } else { sourcePackage = NonSharedPackageCache.Cache.GetCachedPackage(gunInfo.PackageFileName); } if (sourcePackage != null) { var sourceExport = sourcePackage.GetUExport(gunInfo.SourceUIndex); if (!sourceExport.InheritsFrom("SFXWeapon") || sourceExport.IsDefaultObject) { throw new Exception("Wrong setup!"); } if (sourceExport.Parent != null && sourceExport.Parent.ClassName != "Package") { throw new Exception("Cannot port weapon - parent object is not Package!"); } // 1. Setup the link that will be used. //var newParent = EntryExporter.PortParents(sourceExport, targetPackage); var newParent = EntryExporter.PortParents(sourceExport, targetPackage, gunInfo.ImportOnly); void errorOccuredCB(string s) { Debugger.Break(); } IEntry newEntry = null; if (gunInfo.ImportOnly) { Debug.WriteLine($"Gun ImportOnly in file {targetPackage.FilePath}"); if (gunInfo.RequiresStartupPackage) { ThreadSafeDLCStartupPackage.AddStartupPackage(Path.GetFileNameWithoutExtension(gunInfo.PackageFileName)); } newEntry = PackageTools.CreateImportForClass(sourceExport, targetPackage, newParent); } else { List <EntryStringPair> relinkResults = null; if (gunInfo.IsCorrectedPackage || (PackageTools.IsPersistentPackage(gunInfo.PackageFileName) && MERFileSystem.GetPackageFile(gunInfo.PackageFileName.ToLocalizedFilename()) == null)) { // Faster this way, without having to check imports Dictionary <IEntry, IEntry> crossPCCObjectMap = new Dictionary <IEntry, IEntry>(); // Not sure what this is used for these days. SHould probably just be part of the method relinkResults = EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, sourceExport, targetPackage, newParent, true, out newEntry, crossPCCObjectMap); } else { // MEMORY SAFE (resolve imports to exports) MERPackageCache cache = new MERPackageCache(); relinkResults = EntryExporter.ExportExportToPackage(sourceExport, targetPackage, out newEntry, cache); } if (relinkResults.Any()) { Debugger.Break(); } } return(newEntry); } else { Debug.WriteLine($"Package for gun porting not found: {gunInfo.PackageFileName}"); } return(null); // No package was found }
public static Gesture InstallRandomFilteredGestureAsset(IMEPackage targetPackage, float minLength = 0, string[] filterKeywords = null, string[] blacklistedKeywords = null, string[] mainPackagesAllowed = null, bool includeSpecial = false, MERPackageCache cache = null) { var gestureFiles = MERUtilities.ListStaticAssets("binary.gestures", includeSpecial); // Special and package file filtering if (mainPackagesAllowed != null) { var newList = new List <string>(); foreach (var gf in gestureFiles) { if (includeSpecial && gf.Contains("gestures.special.")) { newList.Add(gf); continue; } var packageName = Path.GetFileNameWithoutExtension(MERUtilities.GetFilenameFromAssetName(gf)); if (mainPackagesAllowed.Contains(packageName)) { newList.Add(gf); continue; } } gestureFiles = newList; } // Pick a random package var randGestureFile = gestureFiles.RandomElement(); var hasCache = cache != null; cache ??= new MERPackageCache(); var gPackage = cache.GetCachedPackageEmbedded(randGestureFile, isFullPath: true); List <ExportEntry> options; if (filterKeywords != null && blacklistedKeywords != null) { options = gPackage.Exports.Where(x => x.ClassName == "AnimSequence" && x.ObjectName.Name.ContainsAny(StringComparison.InvariantCultureIgnoreCase, filterKeywords) && !x.ObjectName.Name.ContainsAny(blacklistedKeywords)).ToList(); } else if (filterKeywords != null) { options = gPackage.Exports.Where(x => x.ClassName == "AnimSequence" && x.ObjectName.Name.ContainsAny(StringComparison.InvariantCultureIgnoreCase, filterKeywords)).ToList(); } else if (blacklistedKeywords != null) { options = gPackage.Exports.Where(x => x.ClassName == "AnimSequence" && !x.ObjectName.Name.ContainsAny(blacklistedKeywords)).ToList(); } else { options = gPackage.Exports.Where(x => x.ClassName == "AnimSequence").ToList(); } if (options.Any()) { // Pick a random element var randomGestureExport = options.RandomElement(); // Filter it out if we cannot use it var seqLength = randomGestureExport.GetProperty <FloatProperty>("SequenceLength"); int numRetries = 7; while (seqLength < minLength && numRetries >= 0) { randomGestureExport = options.RandomElement(); seqLength = randomGestureExport.GetProperty <FloatProperty>("SequenceLength"); numRetries--; } var portedInExp = PackageTools.PortExportIntoPackage(targetPackage, randomGestureExport); if (!hasCache) { cache.ReleasePackages(); } return(new Gesture(portedInExp)); } return(null); }
private static bool ForcedRun(ExportEntry hairMeshExport) { if (hairMeshExport.GetProperty <ObjectProperty>("SkeletalMesh") is ObjectProperty obj && obj.Value != 0 && obj.ResolveToEntry(hairMeshExport.FileRef) is IEntry entry) { var isfemaleHair = entry.ObjectName.Name.StartsWith("HMF_HIR_"); var newHair = isfemaleHair ? HairListFemale.RandomElement() : HairListMale.RandomElement(); if (newHair.ObjectName.Name == entry.ObjectName.Name) { return(false); // We are not changing this } MERLog.Information($"{Path.GetFileName(hairMeshExport.FileRef.FilePath)} Changing hair mesh: {entry.ObjectName} -> {newHair.ObjectName}, object {hairMeshExport.FullPath}, class {entry.ClassName}"); var newHairMdl = PackageTools.PortExportIntoPackage(hairMeshExport.FileRef, newHair); var mdlBin = ObjectBinary.From <SkeletalMesh>(newHairMdl); obj.Value = newHairMdl.UIndex; hairMeshExport.WriteProperty(obj); // Update the materials var materials = hairMeshExport.GetProperty <ArrayProperty <ObjectProperty> >("Materials"); if (materials != null && materials.Any()) { //if (materials.Count() != 1) // Debugger.Break(); var mat = materials[0].ResolveToEntry(hairMeshExport.FileRef) as ExportEntry; if (mat != null) { mat.WriteProperty(new ObjectProperty(mdlBin.Materials[0], "Parent")); var parentMat = mdlBin.Materials[0] > 0 ? hairMeshExport.FileRef.GetUExport(mdlBin.Materials[0]) : EntryImporter.ResolveImport(hairMeshExport.FileRef.GetImport(mdlBin.Materials[0])); // Need to match child to parent params that start with HAIR var parentMatTextureParms = parentMat.GetProperty <ArrayProperty <StructProperty> >("TextureParameterValues"); if (parentMatTextureParms != null) { var parentMatHairParms = parentMatTextureParms.Where(x => x.Properties.GetProp <NameProperty>("ParameterName").Value.Name.StartsWith("HAIR_")).ToList(); // Need to match child to parent params that start with HAIR var matTextureParms = mat.GetProperty <ArrayProperty <StructProperty> >("TextureParameterValues"); if (matTextureParms != null) { var matHairParms = matTextureParms.Where(x => x.Properties.GetProp <NameProperty>("ParameterName").Value.Name.StartsWith("HAIR_")).ToList(); // Map them foreach (var matHairParm in matHairParms) { var locName = matHairParm.Properties.GetProp <NameProperty>("ParameterName"); var matchingParent = parentMatHairParms.FirstOrDefault(x => x.Properties.GetProp <NameProperty>("ParameterName").Value == locName.Value); // Assign it if (matchingParent != null) { matHairParm.Properties.AddOrReplaceProp(matchingParent.GetProp <ObjectProperty>("ParameterValue")); } } mat.WriteProperty(matTextureParms); } } else { } } //foreach (var mat in materials) //{ // mdlBin. //} } return(true); } return(false); }
public static bool PortPawnIntoPackage(PortablePawn pawn, IMEPackage targetPackage) { if (IsPawnAssetInPackageAlready(pawn, targetPackage)) { return(true); // Pawn asset to port in already ported in } IMEPackage pawnPackage = null; if (pawn.IsCorrectedPackage) { // DEBUG //if (pawn.PackageFilename == "BioPawn_Collector_Batarian.pcc") //{ // pawnPackage = MEPackageHandler.OpenMEPackage(@"C:\Users\mgame\source\repos\ME2Randomizer\ME2Randomizer\staticfiles\binary\correctedpawns\" + pawn.PackageFilename); //} //else //{ var correctedPawnData = MERUtilities.GetEmbeddedStaticFilesBinaryFile($"correctedpawns.{pawn.PackageFilename}"); pawnPackage = MEPackageHandler.OpenMEPackageFromStream(new MemoryStream(correctedPawnData)); //} } else { var pF = MERFileSystem.GetPackageFile(pawn.PackageFilename); if (pF != null) { pawnPackage = MERFileSystem.OpenMEPackage(pF); } else { Debug.WriteLine("Pawn package not found: {pawn.PackageFilename}"); } } if (pawnPackage != null) { PackageTools.PortExportIntoPackage(targetPackage, pawnPackage.FindExport(pawn.AssetToPortIn), useMemorySafeImport: !pawn.IsCorrectedPackage); // Ensure the assets are too as they may not be directly referenced except in the level instance foreach (var asset in pawn.AssetPaths) { if (targetPackage.FindExport(asset) == null) { PackageTools.PortExportIntoPackage(targetPackage, pawnPackage.FindExport(asset), useMemorySafeImport: !pawn.IsCorrectedPackage); } } if (pawn.TextureUpdates != null) { foreach (var tu in pawn.TextureUpdates) { var targetTextureExp = targetPackage.FindExport(tu.TextureInstancedFullPath); TFCBuilder.InstallTexture(tu, targetTextureExp); } } return(true); } return(false); }