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); }
public static void CreateReachSpec(ExportEntry startNode, bool createTwoWay, ExportEntry destinationNode, string reachSpecClass, ReachSpecSize size, PropertyCollection externalGUIDProperties = null) { IMEPackage Pcc = startNode.FileRef; ExportEntry reachSpectoClone = Pcc.Exports.FirstOrDefault(x => x.ClassName == "ReachSpec"); if (externalGUIDProperties != null) //EXTERNAL { //external node //Debug.WriteLine("Num Exports: " + pcc.Exports.Count); if (reachSpectoClone != null) { ExportEntry outgoingSpec = reachSpectoClone.Clone(); Pcc.addExport(outgoingSpec); IEntry reachSpecClassImp = GetEntryOrAddImport(Pcc, reachSpecClass); //new class type. outgoingSpec.idxClass = reachSpecClassImp.UIndex; outgoingSpec.idxObjectName = reachSpecClassImp.idxObjectName; var properties = outgoingSpec.GetProperties(); ObjectProperty outgoingSpecStartProp = properties.GetProp <ObjectProperty>("Start"); //START StructProperty outgoingEndStructProp = properties.GetProp <StructProperty>("End"); //Embeds END ObjectProperty outgoingSpecEndProp = outgoingEndStructProp.Properties.GetProp <ObjectProperty>(SharedPathfinding.GetReachSpecEndName(outgoingSpec)); //END outgoingSpecStartProp.Value = startNode.UIndex; outgoingSpecEndProp.Value = 0; var endGuid = outgoingEndStructProp.GetProp <StructProperty>("Guid"); endGuid.Properties = externalGUIDProperties; //set the other guid values to our guid values //Add to source node prop ArrayProperty <ObjectProperty> PathList = startNode.GetProperty <ArrayProperty <ObjectProperty> >("PathList"); PathList.Add(new ObjectProperty(outgoingSpec.UIndex)); startNode.WriteProperty(PathList); outgoingSpec.WriteProperties(properties); //Write Spec Size SharedPathfinding.SetReachSpecSize(outgoingSpec, size.SpecRadius, size.SpecHeight); //Reindex reachspecs. SharedPathfinding.ReindexMatchingObjects(outgoingSpec); } } else { //Debug.WriteLine("Source Node: " + startNode.Index); //Debug.WriteLine("Num Exports: " + pcc.Exports.Count); //int outgoingSpec = pcc.ExportCount; //int incomingSpec = pcc.ExportCount + 1; if (reachSpectoClone != null) { ExportEntry outgoingSpec = reachSpectoClone.Clone(); Pcc.addExport(outgoingSpec); ExportEntry incomingSpec = null; if (createTwoWay) { incomingSpec = reachSpectoClone.Clone(); Pcc.addExport(incomingSpec); } IEntry reachSpecClassImp = GetEntryOrAddImport(Pcc, reachSpecClass); //new class type. outgoingSpec.idxClass = reachSpecClassImp.UIndex; outgoingSpec.idxObjectName = reachSpecClassImp.idxObjectName; var outgoingSpecProperties = outgoingSpec.GetProperties(); if (reachSpecClass == "Engine.SlotToSlotReachSpec") { outgoingSpecProperties.Add(new ByteProperty(1, "SpecDirection")); //We might need to find a way to support this edit } //Debug.WriteLine("Outgoing UIndex: " + outgoingSpecExp.UIndex); ObjectProperty outgoingSpecStartProp = outgoingSpecProperties.GetProp <ObjectProperty>("Start"); //START StructProperty outgoingEndStructProp = outgoingSpecProperties.GetProp <StructProperty>("End"); //Embeds END ObjectProperty outgoingSpecEndProp = outgoingEndStructProp.Properties.GetProp <ObjectProperty>(SharedPathfinding.GetReachSpecEndName(outgoingSpec)); //END outgoingSpecStartProp.Value = startNode.UIndex; outgoingSpecEndProp.Value = destinationNode.UIndex; //Add to source node prop var PathList = startNode.GetProperty <ArrayProperty <ObjectProperty> >("PathList"); PathList.Add(new ObjectProperty(outgoingSpec.UIndex)); startNode.WriteProperty(PathList); //Write Spec Size SetReachSpecSize(outgoingSpecProperties, size.SpecRadius, size.SpecHeight); outgoingSpec.WriteProperties(outgoingSpecProperties); if (createTwoWay) { incomingSpec.idxClass = reachSpecClassImp.UIndex; incomingSpec.idxObjectName = reachSpecClassImp.idxObjectName; var incomingSpecProperties = incomingSpec.GetProperties(); if (reachSpecClass == "Engine.SlotToSlotReachSpec") { incomingSpecProperties.Add(new ByteProperty(2, "SpecDirection")); } ObjectProperty incomingSpecStartProp = incomingSpecProperties.GetProp <ObjectProperty>("Start"); //START StructProperty incomingEndStructProp = incomingSpecProperties.GetProp <StructProperty>("End"); //Embeds END ObjectProperty incomingSpecEndProp = incomingEndStructProp.Properties.GetProp <ObjectProperty>(SharedPathfinding.GetReachSpecEndName(incomingSpec)); //END incomingSpecStartProp.Value = destinationNode.UIndex; //Uindex incomingSpecEndProp.Value = startNode.UIndex; //Add reachspec to destination node's path list (returning) var DestPathList = destinationNode.GetProperty <ArrayProperty <ObjectProperty> >("PathList"); DestPathList.Add(new ObjectProperty(incomingSpec.UIndex)); destinationNode.WriteProperty(DestPathList); //destNode.WriteProperty(DestPathList); SetReachSpecSize(incomingSpecProperties, size.SpecRadius, size.SpecHeight); incomingSpec.WriteProperties(incomingSpecProperties); } //Reindex reachspecs. SharedPathfinding.ReindexMatchingObjects(outgoingSpec); } } }
public static string GenerateUDKFileForLevel(string udkPath, IMEPackage pcc) { #region AssetPackage string meshPackageName = $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}Meshes"; string meshFile = Path.Combine(udkPath, @"UDKGame\Content\Shared\", $"{meshPackageName}.upk"); MEPackageHandler.CreateAndSavePackage(meshFile, MEGame.UDK); using IMEPackage meshPackage = MEPackageHandler.OpenUDKPackage(meshFile); meshPackage.getEntryOrAddImport("Core.Package"); IEntry defMat = meshPackage.getEntryOrAddImport("EngineMaterials.DefaultMaterial", "Material", "Engine"); var allMats = new HashSet <int>(); var relinkMap = new Dictionary <IEntry, IEntry>(); #region StaticMeshes List <ExportEntry> staticMeshes = pcc.Exports.Where(exp => exp.ClassName == "StaticMesh").ToList(); foreach (ExportEntry mesh in staticMeshes) { var mats = new Queue <int>(); StaticMesh stm = ObjectBinary.From <StaticMesh>(mesh); foreach (StaticMeshRenderData lodModel in stm.LODModels) { foreach (StaticMeshElement meshElement in lodModel.Elements) { mats.Enqueue(meshElement.Material); allMats.Add(meshElement.Material); meshElement.Material = 0; } } if (pcc.GetEntry(stm.BodySetup) is ExportEntry rbBodySetup) { rbBodySetup.RemoveProperty("PhysMaterial"); } mesh.WriteBinary(stm); IEntry newParent = EntryImporter.GetOrAddCrossImportOrPackage(mesh.ParentFullPath, pcc, meshPackage); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, mesh, meshPackage, newParent, false, out IEntry ent, relinkMap); ExportEntry portedMesh = (ExportEntry)ent; stm = ObjectBinary.From <StaticMesh>(portedMesh); foreach (StaticMeshRenderData lodModel in stm.LODModels) { foreach (StaticMeshElement meshElement in lodModel.Elements) { meshElement.Material = mats.Dequeue(); } } portedMesh.WriteBinary(stm); } #endregion #region Materials using (IMEPackage udkResources = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetCustomAppResourceStream(MEGame.UDK))) { ExportEntry normDiffMat = udkResources.Exports.First(exp => exp.ObjectName == "NormDiffMat"); foreach (int matUIndex in allMats) { if (pcc.GetEntry(matUIndex) is ExportEntry matExp) { List <IEntry> textures = new MaterialInstanceConstant(matExp).Textures; ExportEntry diff = null; ExportEntry norm = null; foreach (IEntry texEntry in textures) { if (texEntry is ExportEntry texport) { if (texport.ObjectName.Name.ToLower().Contains("diff")) { diff = texport; } else if (texport.ObjectName.Name.ToLower().Contains("norm")) { norm = texport; } } } if (diff == null) { relinkMap[matExp] = defMat; continue; } else { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, diff, meshPackage, null, false, out IEntry ent); diff = (ExportEntry)ent; diff.RemoveProperty("TextureFileCacheName"); diff.RemoveProperty("TFCFileGuid"); diff.RemoveProperty("LODGroup"); } if (norm != null) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, norm, meshPackage, null, false, out IEntry ent); norm = (ExportEntry)ent; norm.RemoveProperty("TextureFileCacheName"); norm.RemoveProperty("TFCFileGuid"); norm.RemoveProperty("LODGroup"); } EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, normDiffMat, meshPackage, null, true, out IEntry matEnt); ExportEntry newMat = (ExportEntry)matEnt; newMat.ObjectName = matExp.ObjectName; Material matBin = ObjectBinary.From <Material>(newMat); matBin.SM3MaterialResource.UniformExpressionTextures = new UIndex[] { norm?.UIndex ?? 0, diff.UIndex }; newMat.WriteBinary(matBin); relinkMap[matExp] = newMat; if (newMat.GetProperty <ArrayProperty <ObjectProperty> >("Expressions") is {} expressionsProp&& expressionsProp.Count >= 2) { ExportEntry diffExpression = meshPackage.GetUExport(expressionsProp[0].Value); ExportEntry normExpression = meshPackage.GetUExport(expressionsProp[1].Value); diffExpression.WriteProperty(new ObjectProperty(diff.UIndex, "Texture")); normExpression.WriteProperty(new ObjectProperty(norm?.UIndex ?? 0, "Texture")); } } else if (pcc.GetEntry(matUIndex) is ImportEntry matImp) { relinkMap[matImp] = defMat; } } var relinkMapping = new OrderedMultiValueDictionary <IEntry, IEntry>(relinkMap); foreach (ExportEntry stmExport in staticMeshes) { if (relinkMap.TryGetValue(stmExport, out IEntry destEnt) && destEnt is ExportEntry destExp) { Relinker.Relink(stmExport, destExp, relinkMapping); } } } #endregion meshPackage.Save(); #endregion var staticMeshActors = new List <ExportEntry>(); var lightActors = new List <ExportEntry>(); string tempPackagePath = Path.Combine(App.ExecFolder, $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}.udk"); File.Copy(Path.Combine(App.ExecFolder, "empty.udk"), tempPackagePath, true); using IMEPackage udkPackage = MEPackageHandler.OpenUDKPackage(tempPackagePath); { var topLevelMeshPackages = new List <IEntry>(); foreach (ExportEntry exportEntry in staticMeshes) { IEntry imp = udkPackage.getEntryOrAddImport($"{exportEntry.FullPath}", "StaticMesh", "Engine", exportEntry.ObjectName.Number); while (imp.Parent != null) { imp = imp.Parent; } if (!topLevelMeshPackages.Contains(imp)) { topLevelMeshPackages.Add(imp); } } ExportEntry levelExport = udkPackage.Exports.First(exp => exp.ClassName == "Level"); List <int> actorsInLevel = ObjectBinary.From <Level>(pcc.Exports.First(exp => exp.ClassName == "Level")).Actors.Select(u => u.value).ToList(); var componentToMatrixMap = new Dictionary <int, Matrix>(); foreach (int uIndex in actorsInLevel) { if (pcc.GetEntry(uIndex) is ExportEntry stcExp) { if (stcExp.ClassName == "StaticMeshCollectionActor") { StaticMeshCollectionActor stmc = ObjectBinary.From <StaticMeshCollectionActor>(stcExp); var components = stcExp.GetProperty <ArrayProperty <ObjectProperty> >("StaticMeshComponents"); for (int i = 0; i < components.Count; i++) { componentToMatrixMap[components[i].Value] = stmc.LocalToWorldTransforms[i]; } } else if (stcExp.ClassName == "StaticLightCollectionActor") { StaticLightCollectionActor stlc = ObjectBinary.From <StaticLightCollectionActor>(stcExp); var components = stcExp.GetProperty <ArrayProperty <ObjectProperty> >("LightComponents"); for (int i = 0; i < components.Count; i++) { componentToMatrixMap[components[i].Value] = stlc.LocalToWorldTransforms[i]; } } } } #region StaticMeshActors { var emptySMCBin = new StaticMeshComponent(); IEntry staticMeshActorClass = udkPackage.getEntryOrAddImport("Engine.StaticMeshActor"); udkPackage.getEntryOrAddImport("Engine.Default__StaticMeshActor", "StaticMeshActor", "Engine"); IEntry staticMeshComponentArchetype = udkPackage.getEntryOrAddImport("Engine.Default__StaticMeshActor.StaticMeshComponent0", "StaticMeshComponent", "Engine"); int smaIndex = 2; int smcIndex = 2; foreach (ExportEntry smc in pcc.Exports.Where(exp => exp.ClassName == "StaticMeshComponent")) { if (smc.Parent is ExportEntry parent && actorsInLevel.Contains(parent.UIndex) && parent.IsA("StaticMeshActorBase")) { StructProperty locationProp; StructProperty rotationProp; StructProperty scaleProp = null; smc.CondenseArchetypes(); if (!(smc.GetProperty <ObjectProperty>("StaticMesh") is { } meshProp) || !pcc.IsUExport(meshProp.Value)) { continue; } smc.WriteBinary(emptySMCBin); smc.RemoveProperty("bBioIsReceivingDecals"); smc.RemoveProperty("bBioForcePrecomputedShadows"); //smc.RemoveProperty("bUsePreComputedShadows"); smc.RemoveProperty("bAcceptsLights"); smc.RemoveProperty("IrrelevantLights"); smc.RemoveProperty("Materials"); //should make use of this? smc.ObjectName = new NameReference("StaticMeshComponent", smcIndex++); if (parent.ClassName == "StaticMeshCollectionActor") { if (!componentToMatrixMap.TryGetValue(smc.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); //smc.WriteProperty(CommonStructs.Matrix(m, "CachedParentToWorld")); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry sma = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, staticMeshActorClass.UIndex)) { ObjectName = new NameReference("StaticMeshActor", smaIndex++), Class = staticMeshActorClass, Parent = levelExport }; sma.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(sma); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, smc, udkPackage, sma, true, out IEntry result); var props = new PropertyCollection { new ObjectProperty(result.UIndex, "StaticMeshComponent"), new NameProperty(new NameReference(Path.GetFileNameWithoutExtension(smc.FileRef.FilePath), smc.UIndex), "Tag"), new ObjectProperty(result.UIndex, "CollisionComponent") }; if (locationProp != null) { props.Add(locationProp); } if (rotationProp != null) { props.Add(rotationProp); } if (scaleProp != null) { props.Add(scaleProp); } sma.WriteProperties(props); staticMeshActors.Add(sma); } } IEntry topMeshPackageImport = udkPackage.getEntryOrAddImport(meshPackageName, "Package"); foreach (IEntry mp in topLevelMeshPackages) { mp.Parent = topMeshPackageImport; } } #endregion #region LightActors { IEntry pointLightClass = udkPackage.getEntryOrAddImport("Engine.PointLight"); IEntry spotLightClass = udkPackage.getEntryOrAddImport("Engine.SpotLight"); IEntry directionalLightClass = udkPackage.getEntryOrAddImport("Engine.DirectionalLight"); int plaIndex = 1; int plcIndex = 1; int slaIndex = 1; int slcIndex = 1; int dlaIndex = 1; int dlcIndex = 1; foreach (ExportEntry lightComponent in pcc.Exports) { if (!(lightComponent.Parent is ExportEntry parent && actorsInLevel.Contains(parent.UIndex))) { continue; } StructProperty locationProp; StructProperty rotationProp; StructProperty scaleProp; switch (lightComponent.ClassName) { case "PointLightComponent": lightComponent.CondenseArchetypes(); lightComponent.ObjectName = new NameReference("PointLightComponent", plcIndex++); if (parent.ClassName == "StaticLightCollectionActor") { if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry pla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, pointLightClass.UIndex)) { ObjectName = new NameReference("PointLight", plaIndex++), Class = pointLightClass, Parent = levelExport }; pla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(pla); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, pla, true, out IEntry portedPLC); var plsProps = new PropertyCollection { new ObjectProperty(portedPLC.UIndex, "LightComponent"), new NameProperty("PointLight", "Tag"), }; if (locationProp != null) { plsProps.Add(locationProp); } if (rotationProp != null) { plsProps.Add(rotationProp); } if (scaleProp != null) { plsProps.Add(scaleProp); } pla.WriteProperties(plsProps); lightActors.Add(pla); break; case "SpotLightComponent": lightComponent.CondenseArchetypes(); lightComponent.ObjectName = new NameReference("SpotLightComponent", slcIndex++); if (parent.ClassName == "StaticLightCollectionActor") { if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry sla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, spotLightClass.UIndex)) { ObjectName = new NameReference("SpotLight", slaIndex++), Class = spotLightClass, Parent = levelExport }; sla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(sla); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, sla, true, out IEntry portedSLC); var slaProps = new PropertyCollection { new ObjectProperty(portedSLC.UIndex, "LightComponent"), new NameProperty("SpotLight", "Tag"), }; if (locationProp != null) { slaProps.Add(locationProp); } if (rotationProp != null) { slaProps.Add(rotationProp); } if (scaleProp != null) { slaProps.Add(scaleProp); } sla.WriteProperties(slaProps); lightActors.Add(sla); break; case "DirectionalLightComponent": lightComponent.CondenseArchetypes(); lightComponent.ObjectName = new NameReference("DirectionalLightComponent", dlcIndex++); if (parent.ClassName == "StaticLightCollectionActor") { if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry dla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, directionalLightClass.UIndex)) { ObjectName = new NameReference("DirectionalLight", dlaIndex++), Class = directionalLightClass, Parent = levelExport }; dla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(dla); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, dla, true, out IEntry portedDLC); var dlaProps = new PropertyCollection { new ObjectProperty(portedDLC.UIndex, "LightComponent"), new NameProperty("DirectionalLight", "Tag"), }; if (locationProp != null) { dlaProps.Add(locationProp); } if (rotationProp != null) { dlaProps.Add(rotationProp); } if (scaleProp != null) { dlaProps.Add(scaleProp); } dla.WriteProperties(dlaProps); lightActors.Add(dla); break; } } } UDKifyLights(udkPackage); #endregion Level level = ObjectBinary.From <Level>(levelExport); level.Actors = levelExport.GetChildren().Where(ent => ent.IsA("Actor")).Select(ent => new UIndex(ent.UIndex)).ToList(); levelExport.WriteBinary(level); udkPackage.Save(); } string resultFilePath = Path.Combine(udkPath, @"UDKGame\Content\Maps\", $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}.udk"); using (IMEPackage udkPackage2 = MEPackageHandler.OpenUDKPackage(Path.Combine(App.ExecFolder, "empty.udk"))) { ExportEntry levelExport = udkPackage2.Exports.First(exp => exp.ClassName == "Level"); Level levelBin = ObjectBinary.From <Level>(levelExport); foreach (ExportEntry actor in staticMeshActors) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, actor, udkPackage2, levelExport, true, out IEntry result); levelBin.Actors.Add(result.UIndex); } foreach (ExportEntry actor in lightActors) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, actor, udkPackage2, levelExport, true, out IEntry result); levelBin.Actors.Add(result.UIndex); } levelExport.WriteBinary(levelBin); udkPackage2.Save(resultFilePath); } File.Delete(tempPackagePath); return(resultFilePath); }
private static void UDKifySpotLights(IMEPackage pcc, IEnumerable <ExportEntry> spotLightComponents) { var drawLightRadiusComponentClass = pcc.getEntryOrAddImport("Engine.DrawLightRadiusComponent"); var drawLightConeComponentClass = pcc.getEntryOrAddImport("Engine.DrawLightConeComponent"); var drawLightRadiusArchetype = pcc.getEntryOrAddImport("Engine.Default__SpotLight.DrawLightRadius0", packageFile: "Engine", className: "DrawLightRadiusComponent"); var drawLightSourceRadiusArchetype = pcc.getEntryOrAddImport("Engine.Default__SpotLight.DrawLightSourceRadius0", packageFile: "Engine", className: "DrawLightRadiusComponent"); var drawInnerConeArchetype = pcc.getEntryOrAddImport("Engine.Default__SpotLight.DrawInnerCone0", packageFile: "Engine", className: "DrawLightConeComponent"); var drawOuterConeArchetype = pcc.getEntryOrAddImport("Engine.Default__SpotLight.DrawOuterCone0", packageFile: "Engine", className: "DrawLightConeComponent"); int dlcIndex = 1; int dlrIndex = 1; foreach (ExportEntry slc in spotLightComponents) { var lightingChannels = slc.GetProperty <StructProperty>("LightingChannels"); var innerConeAngle = slc.GetProperty <FloatProperty>("InnerConeAngle")?.Value ?? 0f; var outerConeAngle = slc.GetProperty <FloatProperty>("OuterConeAngle")?.Value ?? 44f; float radius = slc.GetProperty <FloatProperty>("Radius")?.Value ?? 1024f; var drawInnerCone = new ExportEntry(pcc, new byte[8], new PropertyCollection { new FloatProperty(radius, "ConeRadius"), new FloatProperty(innerConeAngle, "ConeAngle") }) { ObjectName = new NameReference("DrawLightConeComponent", dlcIndex++), Class = drawLightConeComponentClass, Parent = slc.Parent, Archetype = drawInnerConeArchetype }; var drawOuterCone = new ExportEntry(pcc, new byte[8], new PropertyCollection { new FloatProperty(radius, "ConeRadius"), new FloatProperty(outerConeAngle, "ConeAngle") }) { ObjectName = new NameReference("DrawLightConeComponent", dlcIndex++), Class = drawLightConeComponentClass, Parent = slc.Parent, Archetype = drawOuterConeArchetype }; var drawLightRadius = new ExportEntry(pcc, new byte[8], new PropertyCollection { new FloatProperty(radius, "SphereRadius") }) { ObjectName = new NameReference("DrawLightRadiusComponent", dlrIndex++), Class = drawLightRadiusComponentClass, Parent = slc.Parent, Archetype = drawLightRadiusArchetype }; var drawLightSourceRadius = new ExportEntry(pcc, new byte[8], new PropertyCollection { new FloatProperty(32f, "SphereRadius") }) { ObjectName = new NameReference("DrawLightRadiusComponent", dlrIndex++), Class = drawLightRadiusComponentClass, Parent = slc.Parent, Archetype = drawLightSourceRadiusArchetype }; if (lightingChannels != null) { drawInnerCone.WriteProperty(lightingChannels); drawOuterCone.WriteProperty(lightingChannels); drawLightRadius.WriteProperty(lightingChannels); drawLightSourceRadius.WriteProperty(lightingChannels); } pcc.AddExport(drawInnerCone); pcc.AddExport(drawOuterCone); pcc.AddExport(drawLightRadius); pcc.AddExport(drawLightSourceRadius); slc.WriteProperty(new ObjectProperty(drawInnerCone.UIndex, "PreviewInnerCone")); slc.WriteProperty(new ObjectProperty(drawOuterCone.UIndex, "PreviewOuterCone")); slc.WriteProperty(new ObjectProperty(drawLightRadius.UIndex, "PreviewLightRadius")); slc.WriteProperty(new ObjectProperty(drawLightSourceRadius.UIndex, "PreviewLightSourceRadius")); } }
private static ActorLookup GetLookupInfo(ExportEntry entry, SeqTools.VarLinkInfo varilink) { ActorLookup lookupInfo = new ActorLookup(); if (entry.ClassName == "SeqVar_Player") { // We now look for 'Player' lookupInfo.FindActor = "Player"; entry.WriteProperty(new BoolProperty(true, "bReturnPawns")); // This is required for moved links to work. So just always add it } else { if (varilink.LinkDesc.StartsWith("Node")) { if (entry.ClassName == "BioSeqVar_ObjectFindByTag") { var tag = entry.GetProperty <StrProperty>("m_sObjectTagToFind"); if (tag != null) { lookupInfo.FindActor = tag.Value; } else { // ?? Debug.WriteLine("Could not find object by tag, tag was missing!"); } } else if (entry.ClassName == "SeqVar_Object") { // Pinned object. var resolvedEntry = entry.GetProperty <ObjectProperty>("ObjValue")?.ResolveToEntry(entry.FileRef) as ExportEntry; if (resolvedEntry != null) { // Look for it's tag and use that cause it's what will probably be used in the // conversation var tag = resolvedEntry.GetProperty <NameProperty>("Tag"); if (tag != null) { lookupInfo.FindActor = tag.Value; } else { //Debug.WriteLine("No tag on resolved object! Is it dynamic?"); //lookupInfo.FindActor = entry.GetProperty<NameProperty>("m_nmFindActor").Value; // keep the original value, I guess lookupInfo.CouldNotResolve = true; } } else { //Debug.WriteLine("Could not resolve object! Is it dynamic?"); //lookupInfo.FindActor = entry.GetProperty<NameProperty>("m_nmFindActor").Value; // keep the original value, I guess lookupInfo.CouldNotResolve = true; } } else if (entry.ClassName == "SeqVar_ScopedNamed") { // We have to find an object in the sequence that has the VarName // What a dumb system var findVarName = entry.GetProperty <NameProperty>("FindVarName"); if (findVarName == null) { Debugger.Break(); } var seqObjs = SeqTools.GetAllSequenceElements(entry).OfType <ExportEntry>(); foreach (var seqObj in seqObjs) { var props = seqObj.GetProperties(); var varname = props.GetProp <NameProperty>("VarName"); if (varname != null && varname.Value == findVarName.Value) { // Pinned object. var resolvedEntry = props.GetProp <ObjectProperty>("ObjValue")?.ResolveToEntry(entry.FileRef) as ExportEntry; if (resolvedEntry != null) { // Look for it's tag and use that cause it's what will probably be used in the // conversation var tag = resolvedEntry.GetProperty <NameProperty>("Tag"); if (tag != null) { lookupInfo.FindActor = tag.Value; } else { //Debug.WriteLine("No tag on resolved object! Is it dynamic?"); //lookupInfo.FindActor = entry.GetProperty<NameProperty>("m_nmFindActor").Value; // keep the original value, I guess lookupInfo.CouldNotResolve = true; } } else { //Debug.WriteLine("Could not resolve object! Is it dynamic?"); //lookupInfo.FindActor = entry.GetProperty<NameProperty>("m_nmFindActor").Value; // keep the original value, I guess lookupInfo.CouldNotResolve = true; } break; } } } else { Debug.WriteLine($"Unknown type on Node convo item: {entry.ClassName}"); } } else { switch (varilink.LinkDesc) { // We don't need to do owner as everything for Owner seems to be always pointing // to the input of the SfxSeqAct_StartConversation // So if we change it there, it... in theory should change case "Owner": lookupInfo.FindActor = "Owner"; // Special case. We should not change owner to something else. We should only update what owner now points to // or something... this shit is so confusing break; case "Puppet1_1": lookupInfo.FindActor = new NameReference("Pup1", 2); lookupInfo.FindMode = EActorTrackFindActorMode.ActorTrack_FindActorByNode; break; case "Puppet1_2": lookupInfo.FindActor = new NameReference("Pup1", 3); lookupInfo.FindMode = EActorTrackFindActorMode.ActorTrack_FindActorByNode; break; case "Puppet2_1": lookupInfo.FindActor = new NameReference("Pup2", 2); lookupInfo.FindMode = EActorTrackFindActorMode.ActorTrack_FindActorByNode; break; case "Puppet2_2": lookupInfo.FindActor = new NameReference("Pup2", 3); lookupInfo.FindMode = EActorTrackFindActorMode.ActorTrack_FindActorByNode; break; default: Debugger.Break(); break; } } } return(lookupInfo); }
public void CreateLiveEditFile() { string filePath = LiveFaceFxEditorFilePath; File.Copy(Path.Combine(App.ExecFolder, "ME3EmptyLevel.pcc"), filePath); LiveFile = MEPackageHandler.OpenMEPackage(filePath); for (int i = 0; i < LiveFile.Names.Count; i++) { if (LiveFile.Names[i].Equals("ME3EmptyLevel")) { LiveFile.replaceName(i, Path.GetFileNameWithoutExtension(filePath)); } } var packguid = Guid.NewGuid(); var package = LiveFile.GetUExport(1); package.PackageGUID = packguid; LiveFile.PackageGuid = packguid; EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, SourceAnimSet.Export.Parent, LiveFile, null, true, out _); var gender = SourceAnimSet.Export.ObjectNameString.Last() switch { 'F' => FaceFXGender.Female, 'M' => FaceFXGender.Male, _ => FaceFXGender.NonSpkr }; ActorTag = null; try { if (gender is not FaceFXGender.NonSpkr) { bool isFemale = gender is FaceFXGender.Female; var bioConv = LiveFile.Exports.First(exp => exp.Class.ObjectName == "BioConversation"); var LiveFileAnimSet = LiveFile.FindExport(SourceAnimSet.Export.InstancedFullPath); var propName = isFemale ? "m_aMaleFaceSets" : "m_aFemaleFaceSets"; int idx = bioConv.GetProperty <ArrayProperty <ObjectProperty> >(propName).FindIndex(objProp => objProp.Value == LiveFileAnimSet.UIndex); if (idx is 0) { //player ActorTag = $"Player_{(isFemale ? 'F' : 'M')}"; IEntry ent; using (IMEPackage soldierFile = MEPackageHandler.OpenME3Package(Path.Combine(ME3Directory.CookedPCPath, "SFXCharacterClass_Soldier.pcc"))) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, soldierFile.GetUExport(isFemale ? 10327 : 10330), LiveFile, LiveFile.GetUExport(3), true, out ent); } ExportEntry actor = (ExportEntry)ent; LiveFile.AddToLevelActorsIfNotThere(actor); actor.WriteProperty(new NameProperty(ActorTag, "Tag")); using (IMEPackage clothingFile = MEPackageHandler.OpenME3Package(Path.Combine(ME3Directory.CookedPCPath, $"BIOG_HM{(isFemale ? "F" : "M")}_ARM_CTH_R.pcc"))) { var clothingPackage = EntryImporter.GetOrAddCrossImportOrPackage("CTHa", clothingFile, LiveFile); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, clothingFile.GetUExport(isFemale ? 1625 : 1966), LiveFile, clothingPackage, true, out ent); } ExportEntry bodyComponent = LiveFile.GetUExport(actor.GetProperty <ObjectProperty>("BodyMesh").Value); bodyComponent.WriteProperty(new ObjectProperty(ent.UIndex, "SkeletalMesh")); } else if (idx is 1) { //owner using IMEPackage parentFile = getParentFile(); foreach (ExportEntry export in parentFile.Exports.Where(exp => exp.ClassName is "SFXSeqAct_StartConversation" or "SFXSeqAct_StartAmbientConv")) { if (export.GetProperty <ObjectProperty>("Conv") is ObjectProperty convProp && parentFile.TryGetImport(convProp.Value, out var convImport) && convImport.ObjectName == bioConv.ObjectName) { ExportEntry seqVar = parentFile.GetUExport(export.GetProperty <ArrayProperty <StructProperty> >("VariableLinks")[0].GetProp <ArrayProperty <ObjectProperty> >("LinkedVariables")[0].Value); if (seqVar.ClassName == "BioSeqVar_ObjectFindByTag") { ActorTag = seqVar.GetProperty <NameProperty>("m_sObjectTagToFind").Value; if (!ActorTag.StartsWith("hench_", StringComparison.OrdinalIgnoreCase)) { importTaggedActor(parentFile); } } else { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, parentFile.GetUExport(seqVar.GetProperty <ObjectProperty>("ObjValue").Value), LiveFile, LiveFile.GetUExport(3), true, out var ent); ExportEntry actor = (ExportEntry)ent; LiveFile.AddToLevelActorsIfNotThere(actor); ActorTag = actor.GetProperty <NameProperty>("Tag")?.Value.Name; if (ActorTag is null) { ActorTag = "ConvoOwner"; actor.WriteProperty(new NameProperty(ActorTag, "Tag")); } } break; } } } else { ActorTag = bioConv.GetProperty <ArrayProperty <NameProperty> >("m_aSpeakerList")[idx - 2].Value; if (!ActorTag.StartsWith("hench_", StringComparison.OrdinalIgnoreCase)) { using IMEPackage parentFile = getParentFile(); importTaggedActor(parentFile); } } } else { //find nonspkr linkage somehow } }
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 void WriteObjValue(ExportEntry export, IEntry objValue) { export.WriteProperty(new ObjectProperty(objValue.UIndex, "ObjValue")); }
/* * private static void VerifyGesturesWork(ExportEntry trackExport) * { * var gestures = RBioEvtSysTrackGesture.GetGestures(trackExport); * var defaultPose = RBioEvtSysTrackGesture.GetDefaultPose(trackExport); * * var gesturesToCheck = gestures.Append(defaultPose).ToList(); * * // Get the containing sequence * var owningSequence = SeqTools.GetParentSequence(trackExport); * while (owningSequence.ClassName != "Sequence") * { * owningSequence = owningSequence.Parent as ExportEntry; * var parSeq = SeqTools.GetParentSequence(owningSequence); * if (parSeq != null) * { * owningSequence = parSeq; * } * } * * var kismetBioDynamicAnimSets = owningSequence.GetProperty<ArrayProperty<ObjectProperty>>("m_aBioDynAnimSets"); * if (kismetBioDynamicAnimSets == null) * { * // We don't have any animsets! * throw new Exception("Track's sequence is missing animsets property!"); * } * * // Get a list of all supported animations * List<Gesture> supportedGestures = new List<Gesture>(); * foreach (var kbdas in kismetBioDynamicAnimSets) * { * var sequenceBioDynamicAnimSet = kbdas.ResolveToEntry(trackExport.FileRef) as ExportEntry; // I don't think these can be imports as they're part of the seq * var associatedset = sequenceBioDynamicAnimSet.GetProperty<ObjectProperty>("m_pBioAnimSetData").ResolveToEntry(trackExport.FileRef); * * } * * // Check all gestures * foreach (var gesture in gesturesToCheck) * { * var bioAnimSet = gesture.GetBioAnimSet(trackExport.FileRef); * * } * * * * } * * internal class TestingBioDynamicAnimSet * { * public NameReference OrigSetName { get; } * public List<string> SupportedGesturesFullPaths { get; } * public IEntry BioAnimSetData { get; } * * internal TestingBioDynamicAnimSet(ExportEntry export) * { * var props = export.GetProperties(); * OrigSetName = props.GetProp<NameProperty>("m_nmOrigSetName").Value; * BioAnimSetData = props.GetProp<ObjectProperty>("m_pBioAnimSetData").ResolveToEntry(export.FileRef); * SupportedGesturesFullPaths = props.GetProp<ArrayProperty<ObjectProperty>>("Sequences").Select(x => x.ResolveToEntry(export.FileRef).InstancedFullPath).ToList(); * } * } */ private static void InstallDynamicAnimSetRefForSeq(ref ExportEntry owningSequence, ExportEntry export, Gesture gesture) { // Find owning sequence if (owningSequence == null) { owningSequence = export; } while (owningSequence.ClassName != "Sequence") { owningSequence = owningSequence.Parent as ExportEntry; var parSeq = SeqTools.GetParentSequence(owningSequence); if (parSeq != null) { owningSequence = parSeq; } } // We have parent sequence data var kismetBioDynamicAnimSets = owningSequence.GetProperty <ArrayProperty <ObjectProperty> >("m_aBioDynAnimSets") ?? new ArrayProperty <ObjectProperty>("m_aBioDynamicAnimSets"); // Check to see if there is any item that uses our bioanimset var bioAnimSet = gesture.GetBioAnimSet(export.FileRef); if (bioAnimSet != null) { ExportEntry kismetBDAS = null; foreach (var kbdas in kismetBioDynamicAnimSets) { var kEntry = kbdas.ResolveToEntry(export.FileRef) as ExportEntry; // I don't think these can be imports as they're part of the seq var associatedset = kEntry.GetProperty <ObjectProperty>("m_pBioAnimSetData").ResolveToEntry(export.FileRef); if (associatedset == bioAnimSet) { // It's this one kismetBDAS = kEntry; break; } } if (kismetBDAS == null) { // We need to generate a new one PropertyCollection props = new PropertyCollection(); props.Add(new NameProperty(gesture.GestureSet, "m_nmOrigSetName")); props.Add(new ArrayProperty <ObjectProperty>("Sequences")); props.Add(new ObjectProperty(bioAnimSet, "m_pBioAnimSetData")); kismetBDAS = ExportCreator.CreateExport(export.FileRef, $"KIS_DYN_{gesture.GestureSet}", "BioDynamicAnimSet", owningSequence); kismetBDAS.indexValue = 0; // Write a blank count of 0 - we will update this in subsequent call // This must be here to ensure parser can read it kismetBDAS.WritePropertiesAndBinary(props, new byte[4]); kismetBioDynamicAnimSets.Add(new ObjectProperty(kismetBDAS)); // Add new export to sequence's list of biodynamicanimsets owningSequence.WriteProperty(kismetBioDynamicAnimSets); } var currentObjs = kismetBDAS.GetProperty <ArrayProperty <ObjectProperty> >("Sequences"); if (currentObjs.All(x => x.Value != gesture.Entry.UIndex)) { // We need to add our item to it currentObjs.Add(new ObjectProperty(gesture.Entry)); var bin = ObjectBinary.From <BioDynamicAnimSet>(kismetBDAS); bin.SequenceNamesToUnkMap[gesture.GestureAnim] = 1; // Not sure what the value should be, or if game actually reads this // FIX IT IF WE EVER FIGURE IT OUT! kismetBDAS.WriteProperty(currentObjs); kismetBDAS.WriteBinary(bin); } } }
public static void SetLocation(ExportEntry export, float x, float y, float z) { export.WriteProperty(CommonStructs.Vector3(x, y, z, "location")); }
public static void RunGame2EmailMerge(GameTarget target) { M3MergeDLC.RemoveMergeDLC(target); var loadedFiles = MELoadedFiles.GetFilesLoadedInGame(target.Game, gameRootOverride: target.TargetPath); // File to base modifications on using IMEPackage pcc = MEPackageHandler.OpenMEPackage(loadedFiles[@"BioD_Nor103_Messages.pcc"]); // Path to Message templates file - different files for ME2/LE2 string ResourcesFilePath = $@"MassEffectModManagerCore.modmanager.emailmerge.{target.Game}.103Message_Template_{target.Game}"; using IMEPackage resources = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetResourceStream(ResourcesFilePath)); // Startup file to place conditionals and transitions into using IMEPackage startup = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetResourceStream( $@"MassEffectModManagerCore.modmanager.mergedlc.{target.Game}.Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.pcc")); var emailInfos = new List <ME2EmailMergeFile>(); var jsonSupercedances = M3Directories.GetFileSupercedances(target, new[] { @".json" }); if (jsonSupercedances.TryGetValue(EMAIL_MERGE_MANIFEST_FILE, out var jsonList)) { jsonList.Reverse(); foreach (var dlc in jsonList) { var jsonFile = Path.Combine(M3Directories.GetDLCPath(target), dlc, target.Game.CookedDirName(), EMAIL_MERGE_MANIFEST_FILE); emailInfos.Add(JsonConvert.DeserializeObject <ME2EmailMergeFile>(File.ReadAllText(jsonFile))); } } // Sanity checks if (!emailInfos.Any() || !emailInfos.SelectMany(e => e.Emails).Any()) { return; } if (emailInfos.Any(e => e.Game != target.Game)) { throw new Exception("ME2 email merge manifest targets incorrect game"); } // Startup File // Could replace this with full instanced path in M3 implementation ExportEntry stateEventMapExport = startup.Exports .First(e => e.ClassName == "BioStateEventMap" && e.ObjectName == "StateTransitionMap"); BioStateEventMap StateEventMap = stateEventMapExport.GetBinaryData <BioStateEventMap>(); ExportEntry ConditionalClass = startup.FindExport($@"PlotManager{M3MergeDLC.MERGE_DLC_FOLDERNAME}.BioAutoConditionals"); #region Sequence Exports // Send message - All email conditionals are checked and emails transitions are triggered ExportEntry SendMessageContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Send_Messages"); ExportEntry LastSendMessage = KismetHelper.GetSequenceObjects(SendMessageContainer).OfType <ExportEntry>() .FirstOrDefault(e => { var outbound = KismetHelper.GetOutboundLinksOfNode(e); return(outbound.Count == 1 && outbound[0].Count == 0); }); ExportEntry TemplateSendMessage = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Send_MessageTemplate"); ExportEntry TemplateSendMessageBoolCheck = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Send_MessageTemplate_BoolCheck"); // Mark Read - email ints are set to read // This is the only section that does not gracefully handle different DLC installations - DLC_CER is required atm ExportEntry MarkReadContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_Read"); ExportEntry LastMarkRead = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_Read.DLC_CER"); ExportEntry MarkReadOutLink = KismetHelper.GetOutboundLinksOfNode(LastMarkRead)[0][0].LinkedOp as ExportEntry; KismetHelper.RemoveOutputLinks(LastMarkRead); ExportEntry TemplateMarkRead = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_ReadTemplate"); ExportEntry TemplateMarkReadTransition = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_Read_Transition"); // Display Messages - Str refs are passed through to GUI ExportEntry DisplayMessageContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Display_Messages"); ExportEntry DisplayMessageOutLink = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Display_Messages.SeqCond_CompareBool_0"); ExportEntry LastDisplayMessage = SeqTools.FindOutboundConnectionsToNode(DisplayMessageOutLink, KismetHelper.GetSequenceObjects(DisplayMessageContainer).OfType <ExportEntry>())[0]; KismetHelper.RemoveOutputLinks(LastDisplayMessage); var DisplayMessageVariableLinks = LastDisplayMessage.GetProperty <ArrayProperty <StructProperty> >("VariableLinks"); ExportEntry TemplateDisplayMessage = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Display_MessageTemplate"); // Archive Messages - Message ints are set to 3 ExportEntry ArchiveContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Archive_Message"); ExportEntry ArchiveSwitch = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Archive_Message.SeqAct_Switch_0"); ExportEntry ArchiveOutLink = pcc.FindExport( @"TheWorld.PersistentLevel.Main_Sequence.Archive_Message.BioSeqAct_PMCheckConditional_1"); ExportEntry ExampleSetInt = KismetHelper.GetOutboundLinksOfNode(ArchiveSwitch)[0][0].LinkedOp as ExportEntry; ExportEntry ExamplePlotInt = SeqTools.GetVariableLinksOfNode(ExampleSetInt)[0].LinkedNodes[0] as ExportEntry; #endregion int messageID = KismetHelper.GetOutboundLinksOfNode(ArchiveSwitch).Count + 1; int currentSwCount = ArchiveSwitch.GetProperty <IntProperty>("LinkCount").Value; foreach (var emailMod in emailInfos) { string modName = "DLC_MOD_" + emailMod.ModName; foreach (var email in emailMod.Emails) { string emailName = modName + "_" + email.EmailName; // Create send transition int transitionId = WriteTransition(StateEventMap, email.StatusPlotInt); int conditionalId = WriteConditional(email.TriggerConditional); #region SendMessage ////////////// // SendMessage ////////////// // Create seq object var SMTemp = emailMod.InMemoryBool.HasValue ? TemplateSendMessageBoolCheck : TemplateSendMessage; EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, SMTemp, pcc, SendMessageContainer, true, new RelinkerOptionsPackage(), out var outSendEntry); var newSend = outSendEntry as ExportEntry; // Set name, comment, add to sequence newSend.ObjectName = new NameReference(emailName); KismetHelper.AddObjectToSequence(newSend, SendMessageContainer); KismetHelper.SetComment(newSend, emailName); if (target.Game == MEGame.ME2) { newSend.WriteProperty(new StrProperty(emailName, "ObjName")); } // Set Trigger Conditional var pmCheckConditionalSM = newSend.GetChildren() .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMCheckConditional" && e is ExportEntry); if (pmCheckConditionalSM is ExportEntry conditional) { conditional.WriteProperty(new IntProperty(conditionalId, "m_nIndex")); KismetHelper.SetComment(conditional, "Time for " + email.EmailName + "?"); } // Set Send Transition var pmExecuteTransitionSM = newSend.GetChildren() .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMExecuteTransition" && e is ExportEntry); if (pmExecuteTransitionSM is ExportEntry transition) { transition.WriteProperty(new IntProperty(transitionId, "m_nIndex")); KismetHelper.SetComment(transition, "Send " + email.EmailName + " message."); } // Set Send Transition if (emailMod.InMemoryBool.HasValue) { var pmCheckStateSM = newSend.GetChildren() .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMCheckState" && e is ExportEntry); if (pmCheckStateSM is ExportEntry checkState) { checkState.WriteProperty(new IntProperty(emailMod.InMemoryBool.Value, "m_nIndex")); KismetHelper.SetComment(checkState, "Is " + emailMod.ModName + " installed?"); } } // Hook up output links KismetHelper.CreateOutputLink(LastSendMessage, "Out", newSend); LastSendMessage = newSend; #endregion #region MarkRead /////////// // MarkRead /////////// // Create seq object var MRTemp = email.ReadTransition.HasValue ? TemplateMarkReadTransition : TemplateMarkRead; EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, MRTemp, pcc, MarkReadContainer, true, new RelinkerOptionsPackage(), out var outMarkReadEntry); var newMarkRead = outMarkReadEntry as ExportEntry; // Set name, comment, add to sequence newMarkRead.ObjectName = new NameReference(emailName); KismetHelper.AddObjectToSequence(newMarkRead, MarkReadContainer); KismetHelper.SetComment(newMarkRead, emailName); if (target.Game == MEGame.ME2) { newMarkRead.WriteProperty(new StrProperty(emailName, "ObjName")); } // Set Plot Int var storyManagerIntMR = newMarkRead.GetChildren() .FirstOrDefault(e => e.ClassName == "BioSeqVar_StoryManagerInt" && e is ExportEntry); if (storyManagerIntMR is ExportEntry plotIntMR) { plotIntMR.WriteProperty(new IntProperty(email.StatusPlotInt, "m_nIndex")); KismetHelper.SetComment(plotIntMR, email.EmailName); } if (email.ReadTransition.HasValue) { var pmExecuteTransitionMR = newMarkRead.GetChildren() .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMExecuteTransition" && e is ExportEntry); if (pmExecuteTransitionMR is ExportEntry transitionMR) { transitionMR.WriteProperty(new IntProperty(email.ReadTransition.Value, "m_nIndex")); KismetHelper.SetComment(transitionMR, "Trigger " + email.EmailName + " read transition"); } } // Hook up output links KismetHelper.CreateOutputLink(LastMarkRead, "Out", newMarkRead); LastMarkRead = newMarkRead; #endregion #region DisplayEmail //////////////// // Display Email //////////////// // Create seq object EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, TemplateDisplayMessage, pcc, DisplayMessageContainer, true, new RelinkerOptionsPackage(), out var outDisplayMessage); var newDisplayMessage = outDisplayMessage as ExportEntry; // Set name, comment, variable links, add to sequence newDisplayMessage.ObjectName = new NameReference(emailName); KismetHelper.AddObjectToSequence(newDisplayMessage, DisplayMessageContainer); newDisplayMessage.WriteProperty(DisplayMessageVariableLinks); KismetHelper.SetComment(newDisplayMessage, emailName); if (target.Game == MEGame.ME2) { newDisplayMessage.WriteProperty(new StrProperty(emailName, "ObjName")); } var displayChildren = newDisplayMessage.GetChildren(); // Set Plot Int var storyManagerIntDE = displayChildren.FirstOrDefault(e => e.ClassName == "BioSeqVar_StoryManagerInt" && e is ExportEntry); if (storyManagerIntDE is ExportEntry plotIntDE) { plotIntDE.WriteProperty(new IntProperty(email.StatusPlotInt, "m_nIndex")); } // Set Email ID var emailIdDE = displayChildren.FirstOrDefault(e => e.ClassName == "SeqVar_Int" && e is ExportEntry); if (emailIdDE is ExportEntry EmailIDDE) { EmailIDDE.WriteProperty(new IntProperty(messageID, "IntValue")); } // Set Title StrRef var titleStrRef = displayChildren.FirstOrDefault(e => e.ClassName == "BioSeqVar_StrRef" && e is ExportEntry ee && ee.GetProperty <NameProperty>("VarName").Value == "Title StrRef"); if (titleStrRef is ExportEntry Title) { Title.WriteProperty(new StringRefProperty(email.TitleStrRef, "m_srValue")); } // Set Description StrRef var descStrRef = displayChildren.FirstOrDefault(e => e.ClassName == "BioSeqVar_StrRef" && e is ExportEntry ee && ee.GetProperty <NameProperty>("VarName").Value == "Desc StrRef"); if (descStrRef is ExportEntry Desc) { Desc.WriteProperty(new StringRefProperty(email.DescStrRef, "m_srValue")); } // Hook up output links KismetHelper.CreateOutputLink(LastDisplayMessage, "Out", newDisplayMessage); LastDisplayMessage = newDisplayMessage; #endregion #region ArchiveEmail //////////////// // Archive Email //////////////// var NewSetInt = EntryCloner.CloneEntry(ExampleSetInt); KismetHelper.AddObjectToSequence(NewSetInt, ArchiveContainer); KismetHelper.CreateOutputLink(NewSetInt, "Out", ArchiveOutLink); KismetHelper.CreateNewOutputLink(ArchiveSwitch, "Link " + (messageID - 1), NewSetInt); var NewPlotInt = EntryCloner.CloneEntry(ExamplePlotInt); KismetHelper.AddObjectToSequence(NewPlotInt, ArchiveContainer); NewPlotInt.WriteProperty(new IntProperty(email.StatusPlotInt, "m_nIndex")); NewPlotInt.WriteProperty(new StrProperty(emailName, "m_sRefName")); var linkedVars = SeqTools.GetVariableLinksOfNode(NewSetInt); linkedVars[0].LinkedNodes = new List <IEntry>() { NewPlotInt }; SeqTools.WriteVariableLinksToNode(NewSetInt, linkedVars); messageID++; currentSwCount++; #endregion } } KismetHelper.CreateOutputLink(LastMarkRead, "Out", MarkReadOutLink); KismetHelper.CreateOutputLink(LastDisplayMessage, "Out", DisplayMessageOutLink); ArchiveSwitch.WriteProperty(new IntProperty(currentSwCount, "LinkCount")); stateEventMapExport.WriteBinary(StateEventMap); // Save Messages file into DLC var cookedDir = Path.Combine(M3Directories.GetDLCPath(target), M3MergeDLC.MERGE_DLC_FOLDERNAME, target.Game.CookedDirName()); var outMessages = Path.Combine(cookedDir, @"BioD_Nor103_Messages.pcc"); pcc.Save(outMessages); // Save Startup file into DLC var startupF = Path.Combine(cookedDir, $@"Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.pcc"); startup.Save(startupF); }
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); }
private static void RandSeqVarInt(ExportEntry export, int min, int max) { export.WriteProperty(new IntProperty(ThreadSafeRandom.Next(min, max), "IntValue")); }
public static bool RandomizeExport(ExportEntry export, RandomizationOption option) { if (!CanRandomize(export)) { return(false); } //build groups //var props = export.GetProperties(); var trackBoneNames = export.GetProperty <ArrayProperty <NameProperty> >("TrackBoneNames"); if (trackBoneNames != null) { // Test first if we should randomize based on the option. //int max = option.SliderValue switch //{ // MODERATE_RANDOM => 8, // HIGH_RANDOM => 4, // VERY_RANDOM => 3, // ALL_RANDOM => 1, // _ => 10 //}; //if (ThreadSafeRandom.Next(max) != 0) return false; // Skip this pass! // We should randomize this pass if (option.SliderValue == ALL_RANDOM) { // Randomize everything. This will probably entirely ruin it, but why not. trackBoneNames.Shuffle(); } else if (option.SliderValue == VERY_RANDOM) { // Index => New Value at that index SwapRandomBones(10, trackBoneNames); } else { var randomizationGroups = new Dictionary <string, List <string> >(); List <string> shuffledItems = new List <string>(); // Items that cannot be added to the > Basic Randomizer pass. var groups = basicBoneGroups; if (option.SliderValue >= MODERATE_RANDOM) { groups = advancedBoneGroups; } // GROUP RANDOMIZER ------------------- foreach (var key in groups) { var keysP1 = trackBoneNames.Where(x => x.Value.Name.Contains(key, StringComparison.InvariantCultureIgnoreCase)).ToList(); randomizationGroups[key] = keysP1.Select(x => x.Value.Name).ToList(); shuffledItems.AddRange(randomizationGroups[key]); randomizationGroups[key].Shuffle(); } if (shuffledItems.Any()) { // For every bone in the track bones... foreach (var prop in trackBoneNames) { // Get the name of the bone var propname = prop.Value.Name; // Get the key of the bone to the list of keys it can map to that contain the key... (?) var randoKey = randomizationGroups.Keys.FirstOrDefault(x => propname.Contains(x, StringComparison.InvariantCultureIgnoreCase)); //Debug.WriteLine(propname); if (randoKey != null) { var randoKeyList = randomizationGroups[randoKey]; prop.Value = randoKeyList[0]; randoKeyList.RemoveAt(0); } } // ---------------------------------- } // > BASIC RANDOMIZER int numAdditionalToSwap = option.SliderValue switch { HIGH_RANDOM => 3, _ => 0 // The other cases are handled above in a diff if statement block }; SwapRandomBones(numAdditionalToSwap, trackBoneNames); } export.WriteProperty(trackBoneNames); return(true); } return(false); }
/// <summary> /// Shuffler from ME1 Randomizer /// </summary> /// <param name="export"></param> /// <param name="option"></param> /// <returns></returns> public static bool ShuffleCutscenePawns(ExportEntry export, RandomizationOption option) { if (!CanRandomize(export, out var cutsceneName)) { return(false); } if (acceptableTagsForPawnShuffling == null) { LoadAsset(); } var variableLinks = export.GetProperty <ArrayProperty <StructProperty> >("VariableLinks"); List <ObjectProperty> pawnsToShuffle = new List <ObjectProperty>(); var playerRefs = new List <ExportEntry>(); foreach (var variableLink in variableLinks) { var expectedType = variableLink.GetProp <ObjectProperty>("ExpectedType"); var expectedTypeStr = export.FileRef.GetEntry(expectedType.Value).ObjectName; var DEBUG = variableLink.GetProp <StrProperty>("LinkDesc"); if (expectedTypeStr == "SeqVar_Object" || expectedTypeStr == "SeqVar_Player" || expectedTypeStr == "BioSeqVar_ObjectFindByTag") { //Investigate the links var linkedVariables = variableLink.GetProp <ArrayProperty <ObjectProperty> >("LinkedVariables"); foreach (var objRef in linkedVariables) { var linkedObj = export.FileRef.GetUExport(objRef.Value).GetProperty <ObjectProperty>("ObjValue"); if (linkedObj != null) { //This is the data the node is referencing var linkedObjectEntry = export.FileRef.GetEntry(linkedObj.Value); var linkedObjName = linkedObjectEntry.ObjectName; if (linkedObjName == "BioPawn" && linkedObjectEntry is ExportEntry bioPawnExport) { var flyingpawn = bioPawnExport.GetProperty <BoolProperty>("bCanFly")?.Value; if (flyingpawn == null || flyingpawn == false) { pawnsToShuffle.Add(objRef); //pointer to this node } } } else if (expectedTypeStr == "SeqVar_Object") { //We might be assigned to. We need to look at the parent sequence //and find what assigns me var node = export.FileRef.GetUExport(objRef.Value); var parentRef = node.GetProperty <ObjectProperty>("ParentSequence"); if (parentRef != null) { var parent = export.FileRef.GetUExport(parentRef.Value); var sequenceObjects = parent.GetProperty <ArrayProperty <ObjectProperty> >("SequenceObjects"); if (sequenceObjects != null) { foreach (var obj in sequenceObjects) { if (obj.Value <= 0) { continue; } var sequenceObject = export.FileRef.GetUExport(obj.Value); if (sequenceObject.InheritsFrom("SequenceAction") && sequenceObject.ClassName == "SeqAct_SetObject" && sequenceObject != export) { //check if target is my node var varlinqs = sequenceObject.GetProperty <ArrayProperty <StructProperty> >("VariableLinks"); if (varlinqs != null) { var targetLink = varlinqs.FirstOrDefault(x => { var linkdesc = x.GetProp <StrProperty>("LinkDesc"); return(linkdesc != null && linkdesc == "Target"); }); var targetLinkedVariables = targetLink?.GetProp <ArrayProperty <ObjectProperty> >("LinkedVariables"); if (targetLinkedVariables != null) { //see if target is node we are investigating for setting. foreach (var targetLinkedVariable in targetLinkedVariables) { var potentialTarget = export.FileRef.GetUExport(targetLinkedVariable.Value); if (potentialTarget == node) { Debug.WriteLine("FOUND TARGET!"); //See what value this is set to. If it inherits from BioPawn we can use it in the shuffling. var valueLink = varlinqs.FirstOrDefault(x => { var linkdesc = x.GetProp <StrProperty>("LinkDesc"); return(linkdesc != null && linkdesc == "Value"); }); var valueLinkedVariables = valueLink?.GetProp <ArrayProperty <ObjectProperty> >("LinkedVariables"); if (valueLinkedVariables != null && valueLinkedVariables.Count == 1) { var linkedNode = export.FileRef.GetUExport(valueLinkedVariables[0].Value); var linkedNodeType = linkedNode.GetProperty <ObjectProperty>("ObjValue"); if (linkedNodeType != null) { var linkedNodeData = export.FileRef.GetUExport(linkedNodeType.Value); if (linkedNodeData.InheritsFrom("BioPawn")) { //We can shuffle this item. Debug.WriteLine("Adding shuffle item: " + objRef.Value); pawnsToShuffle.Add(objRef); //pointer to this node } } } } } } } } } } } } string className = export.FileRef.GetUExport(objRef.Value).ClassName; if (className == "SeqVar_Player") { playerRefs.Add(export.FileRef.GetUExport(objRef.Value)); pawnsToShuffle.Add(objRef); //pointer to this node } else if (className == "BioSeqVar_ObjectFindByTag") { var tagToFind = export.FileRef.GetUExport(objRef.Value).GetProperty <StrProperty>("m_sObjectTagToFind")?.Value; if (tagToFind != null && acceptableTagsForPawnShuffling.Contains(tagToFind)) { pawnsToShuffle.Add(objRef); //pointer to this node } } } } } if (pawnsToShuffle.Count > 1) { int reshuffleAttemptsRemaining = 3; while (reshuffleAttemptsRemaining > 0) { reshuffleAttemptsRemaining--; MERLog.Information("Randomizing pawns in interp: " + export.FullPath); foreach (var refx in playerRefs) { refx.WriteProperty(new BoolProperty(true, "bReturnsPawns")); //Ensure the object returns pawns. It should, but maybe it doesn't. } var newAssignedValues = pawnsToShuffle.Select(x => x.Value).ToList(); newAssignedValues.Shuffle(); for (int i = 0; i < pawnsToShuffle.Count; i++) { pawnsToShuffle[i].Value = newAssignedValues[i]; } export.WriteProperty(variableLinks); if (export.EntryHasPendingChanges) { break; } } return(true); } return(false); }
public void serializeTalkfileToExport(ExportEntry export, bool savePackage = false) { /* converts Huffmann Tree to binary form */ byte[] treeBuffer = ConvertHuffmanTreeToBuffer(); var encodedStrings = new List <EncodedString>(); int i = 0; foreach (var entry in _inputData) { if (entry.Flags == 0) { if (entry.StringID > 0) { entry.Index = -1; } else { entry.Index = 0; } } else { entry.Index = i; i++; var binaryData = new List <BitArray>(); int binaryLength = 0; /* for every character in a string, put it's binary code into data array */ foreach (char c in entry.ASCIIData) { binaryData.Add(_huffmanCodes[c]); binaryLength += _huffmanCodes[c].Count; } byte[] buffer = BitArrayListToByteArray(binaryData, binaryLength); encodedStrings.Add(new EncodedString(entry.ASCIIData.Length, buffer.Length, buffer)); } } /* writing properties */ export.WriteProperty(new IntProperty(_inputData.Count, "m_nHashTableSize")); MemoryStream m = new MemoryStream(); /* writing entries */ m.Write(BitConverter.GetBytes(_inputData.Count), 0, 4); foreach (TLKStringRef entry in _inputData) { m.Write(BitConverter.GetBytes(entry.StringID), 0, 4); m.Write(BitConverter.GetBytes(entry.Flags), 0, 4); m.Write(BitConverter.GetBytes(entry.Index), 0, 4); } /* writing HuffmanTree */ m.Write(treeBuffer, 0, treeBuffer.Length); /* writing data */ m.Write(BitConverter.GetBytes(encodedStrings.Count), 0, 4); foreach (EncodedString enc in encodedStrings) { m.Write(BitConverter.GetBytes(enc.stringLength), 0, 4); m.Write(BitConverter.GetBytes(enc.encodedLength), 0, 4); m.Write(enc.binaryData, 0, enc.encodedLength); } byte[] buff = m.ToArray(); export.WriteBinary(buff); if (savePackage) { export.FileRef.Save(export.FileRef.FilePath); } }
public static void WriteOriginator(ExportEntry export, IEntry originator) { export.WriteProperty(new ObjectProperty(originator.UIndex, "Originator")); }