public static bool InstallShepardDanceGesture(ExportEntry danceTrackExp, MERPackageCache cache) { cache ??= new MERPackageCache(); var danceGestureData = RBioEvtSysTrackGesture.GetGestures(danceTrackExp); var newGestures = new List <RBioEvtSysTrackGesture.Gesture>(); int i = danceGestureData.Count + 1; // The default pose is the +1 while (i > 0) { newGestures.Add(RBioEvtSysTrackGesture.InstallRandomFilteredGestureAsset(danceTrackExp.FileRef, 6, danceKeywords, notDanceKeywords, null, true, cache)); i--; } for (int k = 0; k < danceGestureData.Count; k++) { danceGestureData[k] = newGestures.PullFirstItem(); } RBioEvtSysTrackGesture.WriteGestures(danceTrackExp, danceGestureData); // Update the default pose RBioEvtSysTrackGesture.WriteDefaultPose(danceTrackExp, newGestures.PullFirstItem()); return(true); }
public virtual ExportEntry GetAsset(MERPackageCache cache = null) { IMEPackage package = null; if (cache != null) { package = cache.GetCachedPackage(PackageFile); } else { var packageF = MERFileSystem.GetPackageFile(PackageFile); if (packageF != null) { package = MEPackageHandler.OpenMEPackage(packageF); } } return(package?.FindExport(AssetPath)); }
public static void TestAllImportsInMERFS() { var dlcModPath = Path.Combine(MEDirectories.GetDefaultGamePath(MERFileSystem.Game), "BioGame", "DLC", $"DLC_MOD_{MERFileSystem.Game}Randomizer", "CookedPC"); var packages = Directory.GetFiles(dlcModPath); var globalCache = MERFileSystem.GetGlobalCache(); //var globalP = Path.Combine(dlcModPath, "BioP_Global.pcc"); //if (File.Exists(globalP)) //{ // // This is used for animation lookups. // globalCache.InsertIntoCache(MEPackageHandler.OpenMEPackage(globalP)); //} foreach (var p in packages) { if (p.RepresentsPackageFilePath()) { MERPackageCache localCache = new MERPackageCache(); Debug.WriteLine($"Checking package file {p}"); var pack = MEPackageHandler.OpenMEPackage(p); foreach (var imp in pack.Imports) { if (imp.InstancedFullPath.StartsWith("Core.") || imp.InstancedFullPath.StartsWith("Engine.")) { continue; // These have some natives are always in same file. } if (imp.InstancedFullPath == "BioVFX_Z_TEXTURES.Generic.Glass_Shards_Norm" && MERFileSystem.Game == MEGame.ME2) { continue; // This is... native for some reason? } var resolvedExp = EntryImporter.ResolveImport(imp, globalCache, localCache); if (resolvedExp == null) { Debug.WriteLine($"Could not resolve import: {imp.InstancedFullPath}"); } } } } Debug.WriteLine("Done checking imports"); }
private static void RandomizeVIPShepDance() { var vipLoungeLF = MERFileSystem.GetPackageFile(@"BioD_OmgHub_500DenVIP_LOC_INT.pcc"); if (vipLoungeLF != null && File.Exists(vipLoungeLF)) { var vipLounge = MEPackageHandler.OpenMEPackage(vipLoungeLF); var playerDanceInterpData = vipLounge.GetUExport(547); var c = new MERPackageCache(); InstallShepardDanceGesture(playerDanceInterpData, c); // Paragon InstallShepardDanceGesture(vipLounge.GetUExport(559), c); // Stupid shep lol // Make able to dance again and again in convo var danceTalk = vipLounge.GetUExport(217); var bc = new ConversationExtended(danceTalk); bc.LoadConversation(null); bc.StartingList.Clear(); bc.StartingList.Add(0, 2); bc.SerializeNodes(); MERFileSystem.SavePackage(vipLounge); } // make able to always talk to dancer var vipLoungeF = MERFileSystem.GetPackageFile(@"BioD_OmgHub_500DenVIP.pcc"); if (vipLoungeF != null && File.Exists(vipLoungeF)) { var vipLounge = MEPackageHandler.OpenMEPackage(vipLoungeF); var selectableBool = vipLounge.GetUExport(8845); selectableBool.WriteProperty(new IntProperty(1, "bValue")); MERFileSystem.SavePackage(vipLounge); } }
/// <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 RandomizeAfterlifeShepDance() { var denDanceF = MERFileSystem.GetPackageFile(@"BioD_OmgHub_230DenDance.pcc"); if (denDanceF != null) { var loungeP = MEPackageHandler.OpenMEPackage(denDanceF); var sequence = loungeP.GetUExport(3924); MERPackageCache cache = new MERPackageCache(); List <InterpTools.InterpData> interpDatas = new List <InterpTools.InterpData>(); var interp1 = loungeP.GetUExport(3813); // Make 2 additional dance options by cloning the interp and the data tree var interp2 = MERSeqTools.CloneBasicSequenceObject(interp1); var interp3 = MERSeqTools.CloneBasicSequenceObject(interp1); // Clone the interp data for attaching to 2 and 3 var interpData1 = loungeP.GetUExport(1174); var interpData2 = EntryCloner.CloneTree(interpData1); var interpData3 = EntryCloner.CloneTree(interpData2); KismetHelper.AddObjectToSequence(interpData2, sequence); KismetHelper.AddObjectToSequence(interpData3, sequence); // Load ID for randomization interpDatas.Add(new InterpTools.InterpData(interpData1)); interpDatas.Add(new InterpTools.InterpData(interpData2)); interpDatas.Add(new InterpTools.InterpData(interpData3)); // Chance the data for interp2/3 var id2 = SeqTools.GetVariableLinksOfNode(interp2); id2[0].LinkedNodes[0] = interpData2; SeqTools.WriteVariableLinksToNode(interp2, id2); var id3 = SeqTools.GetVariableLinksOfNode(interp3); id3[0].LinkedNodes[0] = interpData3; SeqTools.WriteVariableLinksToNode(interp3, id3); // Add additional finished states for fadetoblack when done KismetHelper.CreateOutputLink(loungeP.GetUExport(958), "Finished", interp2, 2); KismetHelper.CreateOutputLink(loungeP.GetUExport(958), "Finished", interp3, 2); // Link up the random choice it makes var randSw = MERSeqTools.InstallRandomSwitchIntoSequence(sequence, 3); KismetHelper.CreateOutputLink(randSw, "Link 1", interp1); KismetHelper.CreateOutputLink(randSw, "Link 2", interp2); KismetHelper.CreateOutputLink(randSw, "Link 3", interp3); // Break the original output to start the interp, repoint it's output to the switch instead var sgm = loungeP.GetUExport(1003); //set gesture mode KismetHelper.RemoveOutputLinks(sgm); KismetHelper.CreateOutputLink(sgm, "Done", loungeP.GetUExport(960)); KismetHelper.CreateOutputLink(sgm, "Done", randSw); // Now install the dances foreach (var id in interpDatas) { var danceTrack = id.InterpGroups[0].Tracks[0]; OmegaHub.InstallShepardDanceGesture(danceTrack.Export, cache); } MERFileSystem.SavePackage(loungeP); } }
public static bool InstallOHKO(RandomizationOption arg) { MERLog.Information("Installing One-Hit KO"); var sfxgame = MERFileSystem.GetPackageFile("SFXGame.pcc"); if (sfxgame != null && File.Exists(sfxgame)) { var sfxgameP = MEPackageHandler.OpenMEPackage(sfxgame); // Blood on screen VFX sfxgameP.GetUExport(29336).RemoveProperty("BleedOutEventPair"); sfxgameP.GetUExport(29336).RemoveProperty("BleedOutVFXTemplate"); // Prevent weird landing glitch var fallingStateLanded = sfxgameP.GetUExport(11293); var landedData = fallingStateLanded.Data; NopRange(landedData, 0x2C, 0x14); fallingStateLanded.Data = landedData; MERFileSystem.SavePackage(sfxgameP); } else { } // ProCer tutorials setting min1Health SetupProCer(); // Player classes - Remove shields, set maxhealth to 1 string[] classes = new[] { "Adept", "Engineer", "Infiltrator", "Sentinel", "Soldier", "Vanguard" }; foreach (var c in classes) { var charClass = MERFileSystem.GetPackageFile($"SFXCharacterClass_{c}.pcc"); if (charClass != null && File.Exists(charClass)) { var charClassP = MEPackageHandler.OpenMEPackage(charClass); var ccLoadout = charClassP.FindExport($"BioChar_Loadouts.Player.PLY_{c}"); var ccProps = ccLoadout.GetProperties(); ccProps.GetProp <ArrayProperty <StructProperty> >("ShieldLoadouts").Clear(); // Remove shields // Set health to 1 var health = ccProps.GetProp <StructProperty>("MaxHealth"); health.GetProp <FloatProperty>("X").Value = 1; health.GetProp <FloatProperty>("Y").Value = 1; ccLoadout.WriteProperties(ccProps); // Remove any passive powers ZeroOutStatList(charClassP.FindExport($"SFXGameContent_Powers.SFXPower_{c}Passive"), "HealthBonus", false); ZeroOutStatList(charClassP.FindExport($"SFXGameContent_Powers.SFXPower_{c}Passive_Evolved1"), "HealthBonus", false); ZeroOutStatList(charClassP.FindExport($"SFXGameContent_Powers.SFXPower_{c}Passive_Evolved2"), "HealthBonus", false); if (c == "Vanguard") { // Patch the immunity effect var shieldEffectOnApplied = charClassP.GetUExport(530); var seData = shieldEffectOnApplied.Data; NopRange(seData, 0x2BF, 0x13); shieldEffectOnApplied.Data = seData; } MERFileSystem.SavePackage(charClassP); } } // Zero out stats in tables MERPackageCache cache = new MERPackageCache(); foreach (var asset in ZeroOutStatAssets) { var statClass = asset.GetAsset(cache); if (statClass != null) { foreach (var zos in asset.PropertiesToZeroOut) { ZeroOutStatList(statClass, zos, true); } } } foreach (var package in cache.GetPackages()) { MERFileSystem.SavePackage(package); } { //var reaveF = MERFileSystem.GetPackageFile("SFXPower_Reave_Player.pcc"); //if (reaveF != null && File.Exists(reaveF)) //{ // var reaveP = MEPackageHandler.OpenMEPackage(reaveF); // var defaultReave = reaveP.GetUExport(27); // var regen = defaultReave.GetProperty<ArrayProperty<FloatProperty>>("HealthRegenMult"); // regen[0].Value = 0; // regen[1].Value = 0; // regen[2].Value = 0; // regen[3].Value = 0; // defaultReave.WriteProperty(regen); // var bonusDuration = defaultReave.GetProperty<ArrayProperty<FloatProperty>>("HealthBonusDuration"); // bonusDuration[0].Value = 0; // bonusDuration[1].Value = 0; // bonusDuration[2].Value = 0; // bonusDuration[3].Value = 0; // defaultReave.WriteProperty(bonusDuration); // MERFileSystem.SavePackage(reaveP); //} } return(true); }
public static bool RandomizePackageActorsInConversation(IMEPackage package, RandomizationOption option) { var conversationStartExports = package.Exports.Where(CanRandomizeSeqActStartConvo).ToList(); if (!conversationStartExports.Any()) { return(false); } MERPackageCache localCache = new MERPackageCache(); foreach (var convStart in conversationStartExports) { //if (convStart.UIndex < 13638) // continue; if (!CanRandomizeConversationStart(convStart)) { continue; } var bioConvImportProp = convStart.GetProperty <ObjectProperty>("Conv"); if (bioConvImportProp == null) { continue; // Some conversation starts are just filler stubs and are missing data like Nor_340a/bZaeed } var bioConvImport = bioConvImportProp.ResolveToEntry(package) as ImportEntry; List <string> inputTags = new(); // Shuffle the inputs to the conversation. We will store these and then have to update the Owner and Player tags // I think...... var seqLinks = SeqTools.GetVariableLinksOfNode(convStart); // Dictionary of linked nodes, and var links that point to them. List <ExportEntry> shufflableNodes = new List <ExportEntry>(); List <SeqTools.VarLinkInfo> applicableLinks = new(); ExportEntry ownerEntry = null; ExportEntry playerEntry = null; var sequenceObjects = SeqTools.GetAllSequenceElements(convStart).OfType <ExportEntry>().ToList(); foreach (var varilink in seqLinks) { if (CanLinkBeRandomized(varilink, out _)) { var connectedItem = varilink.LinkedNodes[0] as ExportEntry; if (!shufflableNodes.Contains(connectedItem)) { // Check to make sure node has a value if (connectedItem.ClassName == "SeqVar_Object") { var objValue = connectedItem.GetProperty <ObjectProperty>("ObjValue"); if (objValue == null && !MERSeqTools.IsAssignedBioPawn(convStart, connectedItem, sequenceObjects)) { continue; // This is not a shufflable node, it is never assigned anything } } shufflableNodes.Add(connectedItem); } if (varilink.LinkedNodes[0].ClassName == "SeqVar_Player") { playerEntry = varilink.LinkedNodes[0] as ExportEntry; } else if (varilink.LinkDesc == "Owner") { ownerEntry = varilink.LinkedNodes[0] as ExportEntry; } applicableLinks.Add(varilink); } } if (shufflableNodes.Count <= 1) { continue; // We cannot edit this one } if (shufflableNodes.Count == 2 && playerEntry != null && ownerEntry != null) { continue; // We can't swap owner and player due to hardcoded owner and player tags } // Build shuffle map bool retry = true; Dictionary <int, int> shuffleMap = null; while (retry) { shuffleMap = new Dictionary <int, int>(); var reAssigned = shufflableNodes.ToList(); reAssigned.Shuffle(); for (int i = 0; i < shufflableNodes.Count; i++) { var sourceEntry = shufflableNodes[i]; var remapEntry = reAssigned.PullFirstItem(); int retryCount = reAssigned.Count; while (retryCount > 0 && (sourceEntry == remapEntry || (ownerEntry == sourceEntry && remapEntry == ownerEntry))) { reAssigned.Add(remapEntry); sourceEntry = reAssigned.PullFirstItem(); retryCount--; } if (sourceEntry == ownerEntry && remapEntry == playerEntry) { // Cannot set player to owner break; // We must force a retry } shuffleMap[sourceEntry.UIndex] = remapEntry.UIndex; } if (shuffleMap.Count != shufflableNodes.Count) { continue; // Try again } // We did it retry = false; } // Apply the shuffle map Dictionary <NameReference, ActorLookup> findActorMap = new(); foreach (var varilink in applicableLinks) { var repointedItem = shuffleMap[varilink.LinkedNodes[0].UIndex]; //if (varilink.LinkedNodes[0] == playerEntry || repointedItem == playerEntry.UIndex) //{ // Build FindActor mapping for player // it should be: // Player -> [Some actor, like Pup1_1] // Pup1_1 -> Player // When parsing the interpdatas, change the findactor's // by this methodology BuildActorMap(findActorMap, varilink, repointedItem); //} // Actor map for updating the bioconversation //Debug.WriteLine($"Shuffle actor on varilink in convo: {varilink.LinkedNodes[0].ObjectName.Instanced} => {package.GetUExport(repointedItem).ObjectName.Instanced}"); varilink.LinkedNodes[0] = package.GetUExport(repointedItem); } //SeqTools.PrintVarLinkInfo(seqLinks); // Write the updated links out. SeqTools.WriteVariableLinksToNode(convStart, seqLinks); // Update the localizations foreach (var loc in Localizations) { var bioConversation = EntryImporter.ResolveImport(bioConvImport, MERFileSystem.GetGlobalCache(), localCache, loc); var conv = new ConversationExtended(bioConversation); conv.LoadConversation(null, true); // Step 1. Update tags via map var allConvEntries = conv.ReplyList.ToList(); allConvEntries.AddRange(conv.EntryList); foreach (var convNode in allConvEntries) { // Update speaker if (convNode.IsReply) { // Player. We can't do anything (or can we?) } else { // Non-player node. We can change the tag var speakerTag = convNode.SpeakerTag; // Even though it is dictionary, since it is NameRef, it is considered case sensitive. We have to use case insensitive check var newName = findActorMap.FirstOrDefault(x => x.Key.Instanced.Equals(speakerTag.SpeakerName, StringComparison.InvariantCultureIgnoreCase)).Value; if (newName != null && newName.FindActor.Name != null) { var newTagIdx = conv.Speakers.FirstOrDefault(x => x.SpeakerName.Equals(newName.FindActor.Instanced, StringComparison.InvariantCultureIgnoreCase)); if (newTagIdx != null) { convNode.SpeakerIndex = newTagIdx.SpeakerID; } else { var newSpeaker = new SpeakerExtended(conv.Speakers.Count - 3, new NameReference(newName.FindActor.Name.ToLower(), newName.FindActor.Number)); newSpeaker.FaceFX_Male = speakerTag.FaceFX_Male; newSpeaker.FaceFX_Female = speakerTag.FaceFX_Male; conv.Speakers.Add(newSpeaker); convNode.SpeakerIndex = newSpeaker.SpeakerID; //Debugger.Break(); } } else { //Debugger.Break(); } } // Update interpolation data if (convNode.Interpdata == null) { continue; } var interpData = convNode.Interpdata; InterpData id = new InterpData(interpData); var convo = id.InterpGroups.FirstOrDefault(x => x.GroupName == "Conversation"); if (convo != null) { foreach (var it in convo.Tracks) { var props = it.Export.GetProperties(); var findActor = props.GetProp <NameProperty>("m_nmFindActor"); if (findActor != null && findActor.Value.Name != "Owner" && findActorMap.TryGetValue(findActor.Value, out var newInfo) && newInfo.FindActor.Name != null) { //Debug.WriteLine($"Updating find actor info: {findActor.Value.Instanced} -> {newInfo.FindActor.Instanced}"); findActor.Value = newInfo.FindActor; if (newInfo.FindMode != null) { props.AddOrReplaceProp(new EnumProperty(newInfo.FindMode.ToString(), "EActorTrackFindActorMode", MERFileSystem.Game, "m_eFindActorMode")); } else { props.RemoveNamedProperty("m_eFindActorMode"); } } var lookAtKeys = props.GetProp <ArrayProperty <StructProperty> >("m_aLookAtKeys"); if (lookAtKeys != null) { foreach (var lookAtS in lookAtKeys) { var lookAt = lookAtS.GetProp <NameProperty>("nmFindActor"); if (lookAt.Value.Name != "Owner" && findActorMap.TryGetValue(lookAt.Value, out var newInfoLA) && newInfoLA.FindActor.Name != null) { //Debug.WriteLine($"Updating lookat find actor info: {lookAt.Value.Instanced} -> {newInfoLA.FindActor.Instanced}"); if (newInfoLA.FindActor.Name == null) { Debugger.Break(); } lookAt.Value = newInfoLA.FindActor; var lookatFindMode = newInfoLA.FindMode?.ToString(); lookatFindMode ??= "ActorTrack_FindActorByTag"; // if it's null, set it to the default. As this is struct, the property must exist lookAtS.Properties.AddOrReplaceProp(new EnumProperty(lookatFindMode, "EActorTrackFindActorMode", MERFileSystem.Game, "eFindActorMode")); } } } it.Export.WriteProperties(props); //if (IsAllowedFindActor(findActor)) //{ // if (actorsToFind.All(x => x.FindActor.Instanced != findActor.Value.Instanced)) // { // ActorLookup al = new ActorLookup() { FindActor = findActor.Value }; // var lookupType = it.Export.GetProperty<EnumProperty>("m_eFindActorMode"); // if (lookupType != null && Enum.TryParse<EActorTrackFindActorMode>(lookupType.Value.Name, out var result)) // { // al.FindMode = result; // } // actorsToFind.Add(al); // } // // add track // tracksToUpdate.Add(it); //} } } } conv.SerializeNodes(); // Write the updated info back MERFileSystem.SavePackage(bioConversation.FileRef); } } //// Step 2. Build the remapping //if (actorsToFind.Count <= 1) // return false; // Nothing to randomize //// Instanced name to NameReference //Dictionary<string, ActorLookup> actorRemap = new Dictionary<string, ActorLookup>(); //var shuffledActors = actorsToFind.ToList(); //var unshuffledActors = actorsToFind.Select(x => x.FindActor.Instanced).ToList(); //shuffledActors.Shuffle(); //Debug.WriteLine(""); //while (shuffledActors.Count > 0) //{ // var sourceItem = unshuffledActors.PullFirstItem(); // var remappedItem = shuffledActors.PullFirstItem(); // actorRemap[sourceItem] = remappedItem; // Debug.WriteLine($"Remapping actor: {sourceItem} => {remappedItem.FindActor.Instanced}"); //} //// Step 3. Update all tracks with new remap //foreach (var track in tracksToUpdate) //{ // var props = track.Export.GetProperties(); // var findActor = props.GetProp<NameProperty>("m_nmFindActor"); // if (IsAllowedFindActor(findActor)) // { // if (actorRemap[findActor.Value.Instanced].FindMode != null) // { // props.AddOrReplaceProp(new EnumProperty(actorRemap[findActor.Value.Instanced].FindMode.ToString(), "EActorTrackFindActorMode", MERFileSystem.Game, "m_eFindActorMode")); // } // else // { // props.RemoveNamedProperty("m_eFindActorNode"); // } // findActor.Value = actorRemap[findActor.Value.Instanced].FindActor; // track.Export.WriteProperties(props); // } //} return(true); }
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); }
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)); }