Goz3rr / SatisfactorySaveEditor

297 stars 78 forks source link

Undo Delete Enemies doesn't work #224

Open d35r0n opened 3 years ago

d35r0n commented 3 years ago

I tried using the Delete Enemies option thinking I could always revert back to enemies with this option but it doesn't seem to work for me.

asundrus commented 3 years ago

I just eexperienced the same thing. Tried on 3 different saves, same outcome.

ardenee commented 2 years ago

same here. Read somewhere that the spawn points get moved below the map, and when reset, the ememies should spawn from the new (orignal) spawn points, Bt I think they need to die first inorder to respawn? Maybe editing their location to above the map would work? or is ther an option got set them as dead? so they will forcce a respawn? But it would be a pain to to this for all the ememies manually!

ardenee commented 2 years ago

BP_CreatureSpawner.BP_CreatureSpawner_C has a load of 'ememies', and their positions are all in the negative for the 'Z' field.

Also, 'KilledOnDayNr' is set to 0.

So I opened a basic new map in the editor to compare values. I put -1 in the 'KilledOnDayNr' field and set the 'Z' field to the 'Z' field fomr the new save file. Loaded up the level and the ememies are there again ;)

If only we could export the 'BP_CreatureSpawner.BP_CreatureSpawner_C' from one map and copy it into another map, There is no text export/import either for faster editing, current way is only to manually do the above for all entries in the 'BP_CreatureSpawner.BP_CreatureSpawner_C' section :(

ardenee commented 2 years ago

So I messed about with the source code and updated the delete/undelete cheats for enemies.

exe and source included - http://www.arden.ie/SatisfactorySaveEditor-0.10.1b.zip

changes I made were only to DeleteEnemiesCheat.cs and UndoDeleteEnemiesCheat.cs

When you remove the enemies, it creates a file called enemies.txt in the exe folder, with the old values, when doing an undelete it reads from this file and populates the fields in the save file. it's a quick and dirty method.

Best method I've found is create a new save file, fresh game, delete enemies for a clean enemies.txt file, then load up your current game and undelete enemies, it will read from the enemies.txt, save and then start playing your current save game file again with enemies.

Like I said, it's quick and dirty method, hopefully a cleaner method will be implmented shortly....

DeleteEnemiesCheat.cs `public bool Apply(SaveObjectModel rootItem, SatisfactorySave saveGame) { var animalSpawners = rootItem.FindChild("BP_CreatureSpawner.BP_CreatureSpawner_C", false); if (animalSpawners == null) { MessageBox.Show("This save does not contain animals or it is corrupt.", "Cannot find animals", MessageBoxButton.OK, MessageBoxImage.Error); return false; }

        float offset = -50000;
        var hostPlayerModel = rootItem.FindChild("Char_Player.Char_Player_C", false);
        if (hostPlayerModel == null || hostPlayerModel.Items.Count < 1)
        {
            MessageBox.Show("This save does not contain a host player or it is corrupt.", "Cannot find host player", MessageBoxButton.OK, MessageBoxImage.Error);
            return false;
        }
        Vector3 playerPosition = ((SaveEntityModel)hostPlayerModel.Items[0]).Position;

        StreamWriter sw = new StreamWriter("enemies.txt");

        foreach (SaveObjectModel animalSpawner in animalSpawners.DescendantSelfViewModel)
        {
            // Some crab hatchers are marked as CreatureSpawner instead of EnemySpawner and there is no other trace of the difference between enemy and friendly in the savefile
            //if (animalSpawner.Title.ToLower().Contains("enemy"))
            //{
            sw.WriteLine(((SaveEntityModel)animalSpawner).Title + "," + ((SaveEntityModel)animalSpawner).Position.Z.ToString()); 
            ((SaveEntityModel)animalSpawner).Position.Z += offset; // Move the spawn under the map                

            animalSpawner.FindField("mSpawnData", (ArrayPropertyViewModel arrayProperty) =>
            {
                foreach (StructPropertyViewModel elem in arrayProperty.Elements)
                {   // back old data
                    sw.WriteLine(((SaveEntityModel)animalSpawner).Title + "," + ((Vector)((StructProperty)((DynamicStructDataViewModel)elem.StructData).Fields[0].Model).Data).Data.Z.ToString());
                    sw.WriteLine(((SaveEntityModel)animalSpawner).Title + "," + ((BoolPropertyViewModel)((DynamicStructDataViewModel)elem.StructData).Fields[2]).Value.ToString());
                    sw.WriteLine(((SaveEntityModel)animalSpawner).Title + "," + ((IntPropertyViewModel)((DynamicStructDataViewModel)elem.StructData).Fields[3]).Value.ToString());

                    ((Vector)((StructProperty)((DynamicStructDataViewModel)elem.StructData).Fields[0].Model).Data).Data.Z += offset; // Move the spawn point under the map
                    // Set WasKilled to true so they don't respawn after deleting them
                    ((BoolPropertyViewModel)((DynamicStructDataViewModel)elem.StructData).Fields[2]).Value = true;
                    // Set KilledOnDayNumber to a huge number (some far away animals respawn if the number is too small)
                    ((IntPropertyViewModel)((DynamicStructDataViewModel)elem.StructData).Fields[3]).Value = (int)(Distance(playerPosition, ((Vector)((StructProperty)((DynamicStructDataViewModel)elem.StructData).Fields[0].Model).Data).Data) /10000);
                }
            });
        }

        // Delete the already spawned enemies
        var enemies = rootItem.FindChild("Creature", false).FindChild("Enemy", false);
        rootItem.Remove(enemies);

        if (MessageBox.Show($"Deleted all spawned enemies, and all unspawned creatures (enemy & friendly). Would you like 3 tamed Lizzard Doggos as a compensation?", "Success", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
        {
            for (int i = 0; i < 3; i++)
                AddDoggo(rootItem, saveGame);
        }

        sw.Close();
        return true;
    }`

UndoDeleteEnemiesCheat.cs `public bool Apply(SaveObjectModel rootItem, SatisfactorySave save) { var animalSpawners = rootItem.FindChild("BP_CreatureSpawner.BP_CreatureSpawner_C", false); if (animalSpawners == null) { MessageBox.Show("This save does not contain animals or it is corrupt.", "Cannot find animals", MessageBoxButton.OK, MessageBoxImage.Error); return false; }

        var hostPlayerModel = rootItem.FindChild("Char_Player.Char_Player_C", false);
        if (hostPlayerModel == null || hostPlayerModel.Items.Count < 1)
        {
            MessageBox.Show("This save does not contain a host player or it is corrupt.", "Cannot find host player", MessageBoxButton.OK, MessageBoxImage.Error);
            return false;
        }
        Vector3 playerPosition = ((SaveEntityModel)hostPlayerModel.Items[0]).Position;

        StreamReader sr = new StreamReader("enemies.txt"); 

        foreach (SaveObjectModel animalSpawner in animalSpawners.DescendantSelfViewModel)
        {
            String line        = "";
            float fvalue       = 0;
            bool bvalue        = false;
            int ivalue         = -1;
            line               = sr.ReadLine();                         

            while (line != null) 
            {
                if(line.StartsWith(((SaveEntityModel)animalSpawner).Title)) // search for the corret line - first!
                {
                    fvalue = float.Parse(line.Substring(line.IndexOf(',') + 1), CultureInfo.InvariantCulture.NumberFormat);
                    ((SaveEntityModel)animalSpawner).Position.Z = fvalue;
                    break;
                }
                else // next line......correct line not found yet
                {
                    line = sr.ReadLine();
                }
            }

            if(line == null) // entry not found, skip over......
            {
                fvalue = ((SaveEntityModel)animalSpawner).Position.Z;
                ((SaveEntityModel)animalSpawner).Position.Z = fvalue; // reset to orignal value
                sr.BaseStream.Position = 0;
                sr.DiscardBufferedData(); // always go back to start of file as order may be different from save file
                continue;
            }

            ((SaveEntityModel)animalSpawner).Position.Z = fvalue; // reset to orignal value

            animalSpawner.FindField("mSpawnData", (ArrayPropertyViewModel arrayProperty) =>
            {
                foreach (StructPropertyViewModel elem in arrayProperty.Elements)
                {
                    line = sr.ReadLine();

                    while (line != null)
                    {
                        if (line.StartsWith(((SaveEntityModel)animalSpawner).Title)) // search for the corret line - second!!
                        {
                            fvalue = float.Parse(line.Substring(line.IndexOf(',') + 1), CultureInfo.InvariantCulture.NumberFormat);
                            ((Vector)((StructProperty)((DynamicStructDataViewModel)elem.StructData).Fields[0].Model).Data).Data.Z = fvalue;

                            line   = sr.ReadLine();
                            bvalue = bool.Parse(line.Substring(line.IndexOf(',') + 1));
                            ((BoolPropertyViewModel)((DynamicStructDataViewModel)elem.StructData).Fields[2]).Value = bvalue;

                            line   = sr.ReadLine();
                            ivalue = int.Parse(line.Substring(line.IndexOf(',') + 1));
                            ((IntPropertyViewModel)((DynamicStructDataViewModel)elem.StructData).Fields[3]).Value = ivalue;
                        }

                        break; //should be grouped, if not found, enemies.txt file is bad format, creat a new one
                    }
                }
            });

            sr.BaseStream.Position = 0;
            sr.DiscardBufferedData(); // always go back to start of file as order may be different from save file
        }

        sr.Close();
        return true;
    }`