コード例 #1
0
        public static bool DetectAndSkipMiniGameSeqRefs(ExportEntry exp, RandomizationOption option)
        {
            if (!CanApplySkip(exp, out var miniGameType))
            {
                return(false);
            }

            if (miniGameType == EMinigameSkipType.SeqRef)
            {
                // Update the credits
                var minigameVarLinks = SeqTools.GetVariableLinksOfNode(exp);
                // Update the Out: Value Remaining to something random.
                var ovrNode = minigameVarLinks.FirstOrDefault(x => x.LinkDesc == "OUT: Value Remaining")?.LinkedNodes.FirstOrDefault();
                if (ovrNode is ExportEntry ovr)
                {
                    ovr.WriteProperty(new IntProperty(ThreadSafeRandom.Next(1, 2400), "IntValue"));
                }
            }
            else if (miniGameType == EMinigameSkipType.SeqAct)
            {
                // Update the credits
                var minigameVarLinks = SeqTools.GetVariableLinksOfNode(exp);
                // Update the Remaining Remaining to something random.
                var ovrNode = minigameVarLinks.FirstOrDefault(x => x.LinkDesc == "Remaining Resources")?.LinkedNodes.FirstOrDefault();
                if (ovrNode is ExportEntry ovr)
                {
                    ovr.WriteProperty(new IntProperty(ThreadSafeRandom.Next(1, 2400), "IntValue"));
                }
            }

            SeqTools.SkipSequenceElement(exp, "Success"); //Success is link 0
            return(true);
        }
コード例 #2
0
        public static bool RandomizeExport(ExportEntry export, RandomizationOption option)
        {
            if (!CanRandomize(export))
            {
                return(false);
            }
            var seqLinks = SeqTools.GetVariableLinksOfNode(export);

            // Make a new list of sequence vars based on the originals. We will null out items in this list to prevent randomization of them
            // before we merge back in.
            var randomizationMask = new List <SeqTools.VarLinkInfo>(seqLinks);

            for (int i = 0; i < randomizationMask.Count; i++)
            {
                var info = randomizationMask[i];
                if (!info.LinkedNodes.Any() || !CanLinkBeRandomized(info))
                {
                    // blacklisted or no nodes
                    randomizationMask[i] = null; // Null this object
                    continue;
                }
            }

            // Get total amount of nodes that can be randomized.
            var allNodes = randomizationMask.Where(x => x != null).SelectMany(x => x.LinkedNodes).ToList();

            if (allNodes.Count() < 2)
            {
                return(false); // Can't do anything as there is not enough nodes to randomize.
            }

            // Shuffle the list of nodes that will be installed
            allNodes.Shuffle();

            // Iterate over the randomization mask and use it to determine which varlinks to update
            for (int i = 0; i < randomizationMask.Count; i++)
            {
                var info = randomizationMask[i];
                if (info != null)
                {
                    // Can be changed. As they are just references we can update this list directly
                    info.LinkedNodes.Clear();
                    info.LinkedNodes.Add(allNodes.PullFirstItem());
                }
            }

            // Write the updated links out.
            SeqTools.WriteVariableLinksToNode(export, seqLinks);

            return(true);
        }
コード例 #3
0
        public bool ApplyUpdate(IMEPackage package, ExportEntry targetExport, Mod installingMod)
        {
            if (Utilities.CalculateMD5(new MemoryStream(targetExport.Data)) == EntryMD5)
            {
                Log.Information($@"Applying sequence skip: Skipping {targetExport.InstancedFullPath} through on link {OutboundLinkNameToUse}");
                SeqTools.SkipSequenceElement(targetExport, outboundLinkName: OutboundLinkNameToUse);
            }
            else
            {
                Log.Warning(@"Target export MD5 is incorrect. This may be the wrong target export, or it may be already patched. We are reporting that the mod installed, in the event the target was updated.");
            }

            return(true);
        }
コード例 #4
0
        public static bool SetupFastStartup(RandomizationOption option)
        {
            var entrymenuF = MERFileSystem.GetPackageFile("EntryMenu.pcc");
            var entrymenuP = MEPackageHandler.OpenMEPackage(entrymenuF);
            var skipElem   = entrymenuP.GetUExport(86); // should show splash

            if (!skipElem.ObjectFlags.HasFlag(UnrealFlags.EObjectFlags.DebugPostLoad))
            {
                skipElem.ObjectFlags |= UnrealFlags.EObjectFlags.DebugPostLoad; // mark as modified so subsequent passes don't operate on this
                SeqTools.SkipSequenceElement(skipElem, outboundLinkIdx: 1);
                MERFileSystem.SavePackage(entrymenuP);
            }


            return(true);
        }
コード例 #5
0
        private static void MakeGarrusDeadly()
        {
            // Relay at the end of the DLC
            var garrusShootSeqFile = MERFileSystem.GetPackageFile(@"BioD_OmgGrA_100Leadup.pcc");

            if (garrusShootSeqFile != null && File.Exists(garrusShootSeqFile))
            {
                var garrusSeqP = MEPackageHandler.OpenMEPackage(garrusShootSeqFile);

                // Chance to shoot Shepard
                RandSeqVarInt(garrusSeqP.GetUExport(1043), 60, 100); //60 to 100 percent chance

                // Make garrus shoot faster so he can actually kill the player
                garrusSeqP.GetUExport(974).WriteProperty(new FloatProperty(3, "PlayRate"));

                // Lower the damage so its not instant kill
                garrusSeqP.GetUExport(34).WriteProperty(new FloatProperty(550, "DamageAmount"));
                garrusSeqP.GetUExport(34).WriteProperty(new FloatProperty(2, "MomentumScale"));

                // Do not reset the chance to shoot shepard again
                SeqTools.ChangeOutlink(garrusSeqP.GetUExport(33), 0, 0, 982);

                // Make garrus damage type very deadly
                var garrusDamageTypeProps = garrusSeqP.GetUExport(8).GetProperties();
                garrusDamageTypeProps.Clear(); // Remove the old props
                garrusDamageTypeProps.AddOrReplaceProp(new BoolProperty(true, "bImmediateDeath"));
                garrusDamageTypeProps.AddOrReplaceProp(new BoolProperty(true, "bIgnoreShieldHitLimit"));
                garrusDamageTypeProps.AddOrReplaceProp(new BoolProperty(true, "bIgnoreShields"));
                garrusDamageTypeProps.AddOrReplaceProp(new BoolProperty(true, "bIgnoresBleedout"));
                garrusSeqP.GetUExport(8).WriteProperties(garrusDamageTypeProps);

                // Make shepard more vulnerable
                garrusSeqP.GetUExport(1005).WriteProperty(new IntProperty(60, "ValueB"));


                // Make garrus stand more often
                garrusSeqP.GetUExport(1039).WriteProperty(new IntProperty(60, "IntValue"));

                // Make shoot extra free bullets
                garrusSeqP.GetUExport(1037).WriteProperty(new IntProperty(75, "IntValue"));
                RandSeqVarInt(garrusSeqP.GetUExport(1042), 1, 4);

                MERFileSystem.SavePackage(garrusSeqP);
            }
        }
コード例 #6
0
        /// <summary>
        /// Finds variable connections that come to this node.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="sequenceElements"></param>
        /// <returns></returns>
        public static List <ExportEntry> FindVariableConnectionsToNode(ExportEntry node, List <ExportEntry> sequenceElements)
        {
            List <ExportEntry> referencingNodes = new List <ExportEntry>();

            foreach (var seqObj in sequenceElements)
            {
                if (seqObj == node)
                {
                    continue;                 // Skip node pointing to itself
                }
                var linkSet = SeqTools.GetVariableLinksOfNode(seqObj);
                if (linkSet.Any(x => x.LinkedNodes.Any(y => y == node)))
                {
                    referencingNodes.Add(seqObj);
                }
            }

            return(referencingNodes.Distinct().ToList());
        }
コード例 #7
0
        /// <summary>
        /// Removes a sequence element from the graph, by repointing incoming references to the ones referenced by outgoing items on this export. This is a very basic utility, only use it for items with one input and potentially multiple outputs.
        /// </summary>
        /// <param name="elementToSkip">Th sequence object to skip</param>
        /// <param name="outboundLinkIdx">The 0-indexed outbound link that should be attached the preceding entry element, as if this one had fired that link.</param>
        public static void SkipSequenceElement(ExportEntry elementToSkip, string outboundLinkName = null, int outboundLinkIdx = -1)
        {
            if (outboundLinkIdx == -1 && outboundLinkName == null)
            {
                throw new Exception(@"SkipSequenceElement() must have an outboundLinkName or an outboundLinkIdx!");
            }

            if (outboundLinkIdx == -1)
            {
                var outboundLinkNames = KismetHelper.GetOutboundLinkNames(elementToSkip);
                outboundLinkIdx = outboundLinkNames.IndexOf(outboundLinkName);
            }


            // List of outbound link elements on the specified item we want to skip. These will be placed into the inbound item
            Debug.WriteLine($@"Attempting to skip {elementToSkip.UIndex} in {elementToSkip.FileRef.FilePath}");
            var outboundLinkLists    = SeqTools.GetOutboundLinksOfNode(elementToSkip);
            var inboundToSkippedNode = SeqTools.FindOutboundConnectionsToNode(elementToSkip, SeqTools.GetAllSequenceElements(elementToSkip).OfType <ExportEntry>());

            var newTargetNodes = outboundLinkLists[outboundLinkIdx];

            foreach (var preNode in inboundToSkippedNode)
            {
                // For every node that links to the one we want to skip...
                var preNodeLinks = GetOutboundLinksOfNode(preNode);

                foreach (var ol in preNodeLinks)
                {
                    var numRemoved = ol.RemoveAll(x => x.LinkedOp == elementToSkip);

                    if (numRemoved > 0)
                    {
                        // At least one was removed. Repoint it
                        ol.AddRange(newTargetNodes);
                    }
                }

                WriteOutboundLinksToNode(preNode, preNodeLinks);
            }
        }
コード例 #8
0
        private static void GenericRandomizeFlyerSpawns(IMEPackage package, int maxNumNewEnemies, EPortablePawnClassification minClassification = EPortablePawnClassification.Mook, EPortablePawnClassification maxClassification = EPortablePawnClassification.Boss)
        {
            var flyerPrepSequences = package.Exports.Where(x => x.ClassName == "Sequence" && x.GetProperty <StrProperty>("ObjName") is StrProperty objName && objName == "REF_SpawnPrep_Flyer").ToList();

            foreach (var flySeq in flyerPrepSequences)
            {
                var objectsInSeq = KismetHelper.GetSequenceObjects(flySeq).OfType <ExportEntry>().ToList();
                var preGate      = objectsInSeq.FirstOrDefault(x => x.ClassName == "SeqAct_Gate"); // should not be null
                var outbound     = SeqTools.GetOutboundLinksOfNode(preGate);
                var aifactoryObj = outbound[0][0].LinkedOp as ExportEntry;                         //First out on first link. Should point to AIFactory assuming these are all duplicated flyers

                var availablePawns = PawnPorting.PortablePawns.Where(x => x.Classification >= minClassification && x.Classification <= maxClassification).ToList();
                if (availablePawns.Any())
                {
                    // We can add new pawns to install
                    List <PortablePawn> newPawnsInThisSeq = new List <PortablePawn>();
                    int numEnemies = 1;            // 1 is the original amount.
                    List <ExportEntry> aiFactories = new List <ExportEntry>();
                    aiFactories.Add(aifactoryObj); // the original one
                    for (int i = 0; i < maxNumNewEnemies; i++)
                    {
                        var randPawn = availablePawns.RandomElement();
                        if (!newPawnsInThisSeq.Contains(randPawn))
                        {
                            numEnemies++;
                            PawnPorting.PortPawnIntoPackage(randPawn, package);
                            newPawnsInThisSeq.Add(randPawn);
                            // Clone the ai factory sequence object and add it to the sequence
                            var newAiFactorySeqObj = EntryCloner.CloneTree(aifactoryObj);
                            aiFactories.Add(newAiFactorySeqObj);
                            KismetHelper.AddObjectToSequence(newAiFactorySeqObj, flySeq, false);

                            // Update the backing factory object
                            var backingFactory = newAiFactorySeqObj.GetProperty <ObjectProperty>("Factory").ResolveToEntry(package) as ExportEntry;
                            backingFactory.WriteProperty(new ObjectProperty(package.FindExport(randPawn.ChallengeTypeFullPath), "ActorType"));
                            var collection = backingFactory.GetProperty <ArrayProperty <ObjectProperty> >("ActorResourceCollection");
                            collection.Clear();
                            foreach (var asset in randPawn.AssetPaths)
                            {
                                collection.Add(new ObjectProperty(package.FindExport(asset)));
                            }
                            backingFactory.WriteProperty(collection);
                        }
                    }

                    if (newPawnsInThisSeq.Any())
                    {
                        // install the switch
                        var randSw = MERSeqTools.InstallRandomSwitchIntoSequence(flySeq, numEnemies);
                        outbound[0][0].LinkedOp = randSw;
                        SeqTools.WriteOutboundLinksToNode(preGate, outbound);

                        // Hook up switch to ai factories
                        for (int i = 0; i < numEnemies; i++)
                        {
                            var linkDesc = $"Link {i + 1}";
                            KismetHelper.CreateOutputLink(randSw, linkDesc, aiFactories[i]); // switches are indexed at 1
                        }
                    }
                }
            }
        }
コード例 #9
0
        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);
            }
        }
コード例 #10
0
        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);
        }
コード例 #11
0
        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);
        }
コード例 #12
0
        public static bool ShuffleCutscenePawns2(ExportEntry export, RandomizationOption option)
        {
            if (!CanRandomize(export, out var cutsceneName))
            {
                return(false);
            }
            if (acceptableTagsForPawnShuffling == null)
            {
                LoadAsset();
            }
            var variableLinks = SeqTools.GetVariableLinksOfNode(export);

            // Entries that can be shuffled.
            // This list must not to have items removed!
            List <ExportEntry> pawnsToShuffleDirectAttached = new List <ExportEntry>();
            List <ExportEntry> pawnsToShuffleDynamicSet     = new List <ExportEntry>();

            var sequenceElements = SeqTools.GetAllSequenceElements(export).OfType <ExportEntry>().ToList();;

            foreach (var vl in variableLinks)
            {
                if (!BlackListedLinkDesc(vl) && (vl.ExpectedTypeName == "SeqVar_Object" || vl.ExpectedTypeName == "SeqVar_Player" || vl.ExpectedTypeName == "BioSeqVar_ObjectFindByTag"))
                {
                    // It's a canidate for randomization

                    // Some ObjectFindByTag have an attached ObjValue for some reason.
                    // It's findobjectbytag but in same file
                    // some leftover development thing
                    foreach (var variableLinkNode in vl.LinkedNodes.OfType <ExportEntry>())
                    {
                        bool addedToShuffler = false;
                        if (variableLinkNode.GetProperty <ObjectProperty>("ObjValue")?.ResolveToEntry(export.FileRef) is ExportEntry linkedObjectEntry)
                        {
                            // It's a SeqVar_Object with a linked param
                            if (linkedObjectEntry.IsA("BioPawn"))
                            {
                                // This might be leftover from ME1
                                var flyingpawn = linkedObjectEntry.GetProperty <BoolProperty>("bCanFly")?.Value;
                                if (flyingpawn == null || flyingpawn == false)
                                {
                                    pawnsToShuffleDirectAttached.Add(variableLinkNode); //can be shuffled. This is a var link so it must be added
                                    addedToShuffler = true;
                                }
                            }
                        }
                        // It's not a directly set object
                        // WORK ON CODE BELOW
                        else if (vl.ExpectedTypeName == "SeqVar_Object" && !addedToShuffler)
                        {
                            // Find what assigns
                            if (MERSeqTools.IsAssignedBioPawn(export, variableLinkNode, sequenceElements))
                            {
                                pawnsToShuffleDirectAttached.Add(variableLinkNode);
                                addedToShuffler = true;
                            }
                        }



                        if (!addedToShuffler)
                        {
                            // Check if is SeqVar_Player. We can always shuffle this one
                            string className = variableLinkNode.ClassName;
                            if (className == "SeqVar_Player")
                            {
                                variableLinkNode.WriteProperty(new BoolProperty(true, "bReturnsPawns")); // This is in ME1R, not sure it's needed, but let's just make sure it's true
                                pawnsToShuffleDirectAttached.Add(variableLinkNode);                      //pointer to this node
                            }
                            else if (className == "BioSeqVar_ObjectFindByTag")
                            {
                                var tagToFind = variableLinkNode.GetProperty <StrProperty>("m_sObjectTagToFind")?.Value;
                                //if (tagToFind == "hench_pilot")
                                //    Debugger.Break();
                                if (tagToFind != null && acceptableTagsForPawnShuffling.Contains(tagToFind))
                                {
                                    pawnsToShuffleDynamicSet.Add(variableLinkNode); //pointer to this node
                                }
                                else
                                {
                                    //Debug.WriteLine($"Cannot shuffle tag: {tagToFind}");
                                }
                            }
                        }
                    }
                }
            }

            var pawnsToShuffle = pawnsToShuffleDirectAttached;

            // If it's a dynamic object we only want to add it if it's not already attached somewhere else
            // This can occur if the value to set is also the value used directly
            foreach (var dp in pawnsToShuffleDynamicSet)
            {
                //if (!pawnsToShuffle.Contains(dp))
                //{
                pawnsToShuffle.Add(dp);
                //}
            }

            if (pawnsToShuffle.Count > 1)
            {
                // Now we have a list of all exports that can be shuffled
                var shufflerList = pawnsToShuffle.ToList();
                shufflerList.Shuffle();

                // Now we go through the list of var links and look if the linked node is in the list of pawnsToShuffle.
                // If it is we pull one off the shufflerList and replace the value with that one instead

                foreach (var vl in variableLinks)
                {
                    for (int i = 0; i < vl.LinkedNodes.Count; i++)
                    {
                        var existingItem = vl.LinkedNodes[i];
                        if (pawnsToShuffle.Contains(existingItem))
                        {
                            var newItem = shufflerList.PullFirstItem();

                            if (newItem == existingItem)
                            {
                                var prepickCount = shufflerList.Count;
                                newItem = AttemptRepick(shufflerList, newItem, 4, pawnsToShuffle);
                                var postpickCount = shufflerList.Count;
                                if (prepickCount != postpickCount)
                                {
                                    // Should not be any count change as we already drew an item
                                    Debugger.Break();
                                }
                            }

                            vl.LinkedNodes[i] = newItem;
                        }
                    }
                }

                // The linked nodes are now randomized
                // Write out the values

                if (shufflerList.Count != 0)
                {
                    // This can occur in sequences that have weird duplicates
                    //Debugger.Break();
                }

                SeqTools.WriteVariableLinksToNode(export, variableLinks);
                Debug.WriteLine($"Randomized {pawnsToShuffle.Count} links in animcutscene in {cutsceneName}, file {Path.GetFileName(export.FileRef.FilePath)}");
                return(true);
            }

            return(false);
        }
コード例 #13
0
        /*
         * 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);
                }
            }
        }
コード例 #14
0
        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);
        }
コード例 #15
0
        /// <summary>
        /// Is the specified node assigned to a BioPawn or subclass of?
        /// </summary>
        /// <param name="sequenceObj">The object that we are checking that should be ignored for references (like conversation start)</param>
        /// <param name="vlNode">The node to check</param>
        /// <param name="sequenceElements">List of nodes in the sequence</param>
        /// <returns></returns>
        public static bool IsAssignedBioPawn(ExportEntry sequenceObj, ExportEntry vlNode, List <ExportEntry> sequenceElements)
        {
            var inboundConnections = SeqTools.FindVariableConnectionsToNode(vlNode, sequenceElements);

            foreach (var sequenceObject in inboundConnections)
            {
                if (sequenceObject == sequenceObj)
                {
                    continue; // Obviously we reference this node
                }
                // Is this a 'SetObject' that is assigning the value?
                if (sequenceObject.InheritsFrom("SequenceAction") && sequenceObject.ClassName == "SeqAct_SetObject" && sequenceObject != sequenceObj)
                {
                    //check if target is my node
                    var referencingVarLinks = SeqTools.GetVariableLinksOfNode(sequenceObject);
                    var targetLink          = referencingVarLinks.FirstOrDefault(x => x.LinkDesc == "Target"); // What is the target node?
                    if (targetLink != null)
                    {
                        //see if target is node we are investigating for setting.
                        foreach (var potentialTarget in targetLink.LinkedNodes.OfType <ExportEntry>())
                        {
                            if (potentialTarget == vlNode)
                            {
                                // There's a 'SetObject' with Target of the attached variable to our interp cutscene
                                // That means something is 'setting' the value of this
                                // We need to inspect what it is to see if we can shuffle it

                                //Debug.WriteLine("Found a setobject to variable linked item on a sequence");

                                //See what value this is set to. If it inherits from BioPawn we can use it in the shuffling.
                                var pointedAtValueLink = referencingVarLinks.FirstOrDefault(x => x.LinkDesc == "Value");
                                if (pointedAtValueLink != null && pointedAtValueLink.LinkedNodes.Count == 1) // Only 1 item being set. More is too complicated
                                {
                                    var linkedNode     = pointedAtValueLink.LinkedNodes[0] as ExportEntry;
                                    var linkedNodeType = linkedNode.GetProperty <ObjectProperty>("ObjValue");
                                    if (linkedNodeType != null)
                                    {
                                        var linkedNodeData = sequenceObj.FileRef.GetUExport(linkedNodeType.Value);
                                        if (linkedNodeData.IsA("BioPawn"))
                                        {
                                            //We can shuffle this item.

                                            // We write the property to the node so if it's not assigned at runtime (like on gender check) it still can show something.
                                            // Cutscene will still be goofed up but will instead show something instead of nothing

                                            linkedNode.WriteProperty(linkedNodeType);

                                            //Debug.WriteLine("Adding shuffle item: " + objRef.Value);
                                            // Original value below was 'linkedNode' which i'm pretty sure is the value that would be assigned, not the actual object that holds that value oncea assigned
                                            return(true);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(false);
        }
コード例 #16
0
        /// <summary>
        /// Technically this is not part of Nor (It's EndGm). But it takes place on normandy so users
        /// will think it is part of the normandy.
        /// </summary>
        /// <param name="random"></param>
        private static void RandomizeRomance()
        {
            // Romance is 2 pass:

            // Pass 1: The initial chances that are not ME1 or Miranda
            {
                var romChooserPackage = MEPackageHandler.OpenMEPackage(MERFileSystem.GetPackageFile("BioD_EndGm1_110Romance.pcc"));
                var romSeq            = romChooserPackage.GetUExport(88);
                var outToRepoint      = romChooserPackage.GetUExport(65); //repoint to our switch

                // Install random switch and point it at the romance log culminations for each
                // Miranda gets 2 as she has a 50/50 of miranda or lonely shep.
                var randomSwitch = MERSeqTools.InstallRandomSwitchIntoSequence(romSeq, 7);
                var outLinks     = SeqTools.GetOutboundLinksOfNode(randomSwitch);

                outLinks[0].Add(new SeqTools.OutboundLink()
                {
                    InputLinkIdx = 0, LinkedOp = romChooserPackage.GetUExport(28)
                });                                                                                                             // JACOB
                outLinks[1].Add(new SeqTools.OutboundLink()
                {
                    InputLinkIdx = 0, LinkedOp = romChooserPackage.GetUExport(20)
                });                                                                                                             // GARRUS
                outLinks[2].Add(new SeqTools.OutboundLink()
                {
                    InputLinkIdx = 0, LinkedOp = romChooserPackage.GetUExport(30)
                });                                                                                                             // TALI
                outLinks[3].Add(new SeqTools.OutboundLink()
                {
                    InputLinkIdx = 0, LinkedOp = romChooserPackage.GetUExport(29)
                });                                                                                                             // THANE
                outLinks[4].Add(new SeqTools.OutboundLink()
                {
                    InputLinkIdx = 0, LinkedOp = romChooserPackage.GetUExport(27)
                });                                                                                                             // JACK
                outLinks[5].Add(new SeqTools.OutboundLink()
                {
                    InputLinkIdx = 0, LinkedOp = romChooserPackage.GetUExport(52)
                });                                                                                                             // MIRANDA--| -> Delay into teleport
                outLinks[6].Add(new SeqTools.OutboundLink()
                {
                    InputLinkIdx = 0, LinkedOp = romChooserPackage.GetUExport(52)
                });                                                                                                             // ME1------| -> Delay into teleport

                SeqTools.WriteOutboundLinksToNode(randomSwitch, outLinks);

                // Repoint to our randomswitch
                var penultimateOutbound = SeqTools.GetOutboundLinksOfNode(outToRepoint);
                penultimateOutbound[0].Clear();
                penultimateOutbound[0].Add(new SeqTools.OutboundLink()
                {
                    InputLinkIdx = 0, LinkedOp = randomSwitch
                });

                // DEBUG ONLY: FORCE LINK
                //penultimateOutbound[0].Add(new SeqTools.OutboundLink() { InputLinkIdx = 0, LinkedOp = romChooserPackage.GetUExport(27) });
                SeqTools.WriteOutboundLinksToNode(outToRepoint, penultimateOutbound);

                MERFileSystem.SavePackage(romChooserPackage);
            }

            // Pass 2: ME1 or Miranda if Pass 1 fell through at runtime
            {
                var romChooserPackage = MEPackageHandler.OpenMEPackage(MERFileSystem.GetPackageFile("BioD_EndGm1_110ROMMirranda.pcc"));
                var romSeq            = romChooserPackage.GetUExport(8468);
                var outToRepoint      = romChooserPackage.GetUExport(2354); //repoint to our switch

                // Install random switch and point it at the romance log culminations for each
                // Miranda gets 2 as she has a 50/50 of miranda or lonely shep.
                var randomSwitch = MERSeqTools.InstallRandomSwitchIntoSequence(romSeq, 2);
                var outLinks     = SeqTools.GetOutboundLinksOfNode(randomSwitch);

                outLinks[0].Add(new SeqTools.OutboundLink()
                {
                    InputLinkIdx = 0, LinkedOp = romChooserPackage.GetUExport(8432)
                });                                                                                                             // MIRANDA
                outLinks[1].Add(new SeqTools.OutboundLink()
                {
                    InputLinkIdx = 0, LinkedOp = romChooserPackage.GetUExport(8413)
                });                                                                                                             // ME1

                SeqTools.WriteOutboundLinksToNode(randomSwitch, outLinks);

                // Repoint to our randomswitch
                var penultimateOutbound = SeqTools.GetOutboundLinksOfNode(outToRepoint);
                penultimateOutbound[0].Clear();
                penultimateOutbound[0].Add(new SeqTools.OutboundLink()
                {
                    InputLinkIdx = 0, LinkedOp = randomSwitch
                });
                SeqTools.WriteOutboundLinksToNode(outToRepoint, penultimateOutbound);

                MERFileSystem.SavePackage(romChooserPackage);
            }
        }