Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
path = ArkSavegameToolkit
url = https://github.com/cadon/ArkSavegameToolkit.git
branch = master
[submodule "AsaSavegameToolkit"]
path = AsaSavegameToolkit
url = git@github.com:Olfi01/AsaSavegameToolkit.git
branch = asb
10 changes: 8 additions & 2 deletions ARKBreedingStats.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.32002.261
# Visual Studio Version 18
VisualStudioVersion = 18.6.11806.211 stable
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ARKBreedingStats", "ARKBreedingStats\ARKBreedingStats.csproj", "{991563CE-6B2C-40AE-BC80-A14F090A4D26}"
ProjectSection(ProjectDependencies) = postProject
Expand All @@ -25,6 +25,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_meta", "_meta", "{5DAADC66
translations.txt = translations.txt
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsaSavegameToolkit", "AsaSavegameToolkit\src\AsaSavegameToolkit.csproj", "{DC4E684E-1A84-2D33-95CA-154FECA26A10}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -51,6 +53,10 @@ Global
{E7C8F9A2-D4B3-4C1E-9F2A-1A8B5C3D4E5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7C8F9A2-D4B3-4C1E-9F2A-1A8B5C3D4E5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7C8F9A2-D4B3-4C1E-9F2A-1A8B5C3D4E5F}.Release|Any CPU.Build.0 = Release|Any CPU
{DC4E684E-1A84-2D33-95CA-154FECA26A10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC4E684E-1A84-2D33-95CA-154FECA26A10}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC4E684E-1A84-2D33-95CA-154FECA26A10}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC4E684E-1A84-2D33-95CA-154FECA26A10}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
8 changes: 8 additions & 0 deletions ARKBreedingStats/ARKBreedingStats.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@
</ItemGroup>


<ItemGroup Condition="'$(Configuration)'!='Debug'">
<PackageReference Include="AsaSavegameToolkit" Version="0.5.0" />
</ItemGroup>

<ItemGroup Condition="'$(Configuration)'=='Debug'">
<ProjectReference Include="..\AsaSavegameToolkit\src\AsaSavegameToolkit.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Windows.Forms.DataVisualization" Version="1.0.0-prerelease.20110.1" />
<PackageReference Include="System.Speech" Version="10.0.3" />
Expand Down
3 changes: 1 addition & 2 deletions ARKBreedingStats/Form1.importSave.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,7 @@ private async Task<string> RunSavegameImport(string fileLocation, string conveni
}
catch (Exception ex)
{
var noAsaSupportInfo = ex.Message.StartsWith("Found unknown Version 20819") ? "Importing save games from ARK: Survival Ascended (ASA) is not yet supported, currently only ARK: Survival Evolved (ASE) is supported for save file import.\n\n" : null;
MessageBoxes.ExceptionMessageBox(ex, $"{noAsaSupportInfo}An error occurred while importing the file {fileLocation}.", "Save file import error");
MessageBoxes.ExceptionMessageBox(ex, $"An error occurred while importing the file {fileLocation}.", "Save file import error");
return string.Empty;
}
finally
Expand Down
243 changes: 224 additions & 19 deletions ARKBreedingStats/ImportSavegame.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using ARKBreedingStats.Library;
using ARKBreedingStats.species;
using ARKBreedingStats.values;
using AsaSavegameToolkit.Porcelain;
using SavegameToolkit;
using SavegameToolkit.Arrays;
using SavegameToolkit.Structs;
Expand All @@ -12,6 +13,16 @@
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Creature = ARKBreedingStats.Library.Creature;
using SavegameCreature = AsaSavegameToolkit.Porcelain.Creature;
using GameObjectExtensions = SavegameToolkitAdditions.GameObjectExtensions;
using AsaSavegameToolkit.Plumbing.Utilities;
#if DEBUG
using AsaSavegameToolkit.Plumbing.Records;
#else
using AsaSavegameToolkit.Plumbing.Properties;
#endif
using Microsoft.Extensions.Logging.Abstractions;

namespace ARKBreedingStats
{
Expand All @@ -26,6 +37,77 @@ private ImportSavegame(float gameTime)
}

public static async Task ImportCollectionFromSavegame(CreatureCollection creatureCollection, string filename, string serverName)
{
byte[] first16Bytes = new byte[16];
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
fs.ReadExactly(first16Bytes, 0, 16);
}
string checkString = System.Text.ASCIIEncoding.ASCII.GetString(first16Bytes);
bool isAsaSavegame = checkString.Contains("SQLite format");
var creatures = await (isAsaSavegame ? ImportCollectionFromAsaSavegame(creatureCollection, filename, serverName) : ImportCollectionFromAseSavegame(creatureCollection, filename, serverName));

ArkName.ClearCache();

// if there are creatures with unknown species, check if the according mod-file is available
var unknownSpeciesCreatures = creatures.Where(c => c.Species == null).ToArray();

if (!unknownSpeciesCreatures.Any()
|| Properties.Settings.Default.IgnoreUnknownBlueprintsOnSaveImport
|| MessageBox.Show("The species of " + unknownSpeciesCreatures.Length + " creature" + (unknownSpeciesCreatures.Length != 1 ? "s" : "") + " is not recognized, probably because they are from a mod that is not loaded.\n"
+ "The unrecognized species-classes are as follows, all the according creatures cannot be imported:\n\n" + string.Join("\n", unknownSpeciesCreatures.Select(c => c.name).Distinct().ToArray())
+ "\n\nTo import the unrecognized creatures, you first need mod values-files, see Settings - Mod value manager… if the mod value is available\n\n"
+ "Do you want to import the recognized creatures? If you click no, nothing is imported.",
"Unrecognized species while importing savegame", MessageBoxButtons.YesNo, MessageBoxIcon.Question
) == DialogResult.Yes
)
{
ImportCollection(creatureCollection, creatures.Where(c => c.Species != null).ToList(), serverName);
}
}

private static async Task<Creature[]> ImportCollectionFromAsaSavegame(CreatureCollection creatureCollection, string filename, string serverName)
{
var ignoreClasses = Values.V.IgnoreSpeciesClassesOnImport;
var importUnclaimedBabies = Properties.Settings.Default.SaveFileImportUnclaimedBabies;
var saveImportCryo = Properties.Settings.Default.SaveImportCryo;

var save = AsaSaveGame.ReadFrom(filename, logger: NullLogger.Instance);
#if DEBUG
var gameTime = save.GameTime;
#else
var gameTime = 0d; // wait for https://github.com/flintthatchwood/AsaSavegameToolkit/pull/3 to be merged and published
#endif

var tamedCreatureObjects = save.TamedCreatures.Values.Where(o => (importUnclaimedBabies || !o.Record.IsUnclaimedBaby()) && !ignoreClasses.Contains(o.ClassName.Split('.').Last()));
if (saveImportCryo)
{
tamedCreatureObjects = tamedCreatureObjects.Concat(save.CryopoddedCreatures.Values.Where(o => !ignoreClasses.Contains(o.ClassName.Split('.').Last())));
}

if (!string.IsNullOrWhiteSpace(Properties.Settings.Default.ImportTribeNameFilter))
{
string[] filters = Properties.Settings.Default.ImportTribeNameFilter.Split(',')
.Select(s => s.Trim())
.Where(s => !string.IsNullOrEmpty(s))
.ToArray();

if (filters.Any())
{
tamedCreatureObjects = tamedCreatureObjects.Where(o =>
{
string tribeName = o.TribeName ?? string.Empty;
return filters.Any(filter => tribeName.Contains(filter));
});
}
}

ImportSavegame importSavegame = new ImportSavegame(Convert.ToSingle(gameTime));
int? wildLevelStep = creatureCollection.getWildLevelStep();
return tamedCreatureObjects.Select(o => importSavegame.ConvertCreature(o, wildLevelStep)).Where(c => c != null).ToArray();
}

private static async Task<Creature[]> ImportCollectionFromAseSavegame(CreatureCollection creatureCollection, string filename, string serverName)
{
(GameObjectContainer gameObjectContainer, float gameTime) = await Task.Run(() => ReadSavegameFile(filename));
var ignoreClasses = Values.V.IgnoreSpeciesClassesOnImport;
Expand Down Expand Up @@ -56,25 +138,7 @@ public static async Task ImportCollectionFromSavegame(CreatureCollection creatur

ImportSavegame importSavegame = new ImportSavegame(gameTime);
int? wildLevelStep = creatureCollection.getWildLevelStep();
var creatures = tamedCreatureObjects.Select(o => importSavegame.ConvertGameObject(o, wildLevelStep)).Where(c => c != null).ToArray();

ArkName.ClearCache();

// if there are creatures with unknown species, check if the according mod-file is available
var unknownSpeciesCreatures = creatures.Where(c => c.Species == null).ToArray();

if (!unknownSpeciesCreatures.Any()
|| Properties.Settings.Default.IgnoreUnknownBlueprintsOnSaveImport
|| MessageBox.Show("The species of " + unknownSpeciesCreatures.Length + " creature" + (unknownSpeciesCreatures.Length != 1 ? "s" : "") + " is not recognized, probably because they are from a mod that is not loaded.\n"
+ "The unrecognized species-classes are as follows, all the according creatures cannot be imported:\n\n" + string.Join("\n", unknownSpeciesCreatures.Select(c => c.name).Distinct().ToArray())
+ "\n\nTo import the unrecognized creatures, you first need mod values-files, see Settings - Mod value manager… if the mod value is available\n\n"
+ "Do you want to import the recognized creatures? If you click no, nothing is imported.",
"Unrecognized species while importing savegame", MessageBoxButtons.YesNo, MessageBoxIcon.Question
) == DialogResult.Yes
)
{
ImportCollection(creatureCollection, creatures.Where(c => c.Species != null).ToList(), serverName);
}
return tamedCreatureObjects.Select(o => importSavegame.ConvertGameObject(o, wildLevelStep)).Where(c => c != null).ToArray();
}

private static (GameObjectContainer, float) ReadSavegameFile(string fileName)
Expand Down Expand Up @@ -254,5 +318,146 @@ private Creature ConvertGameObject(GameObject creatureObject, int? levelStep)

return creature;
}

private Creature ConvertCreature(SavegameCreature creatureObject, int? levelStep)
{
if (!Values.V.TryGetSpeciesByBlueprint(creatureObject.ClassName, out Species species))
{
// species is unknown, creature cannot be imported.
// use name-field to temporarily save the unknown classString to display in a messageBox
return new Creature { name = creatureObject.ClassName };
}

string imprinterName = creatureObject.ImprinterName;
string owner = string.IsNullOrWhiteSpace(imprinterName) ? creatureObject.TamerString : imprinterName;

int[] wildLevels = Enumerable.Repeat(-1, Stats.StatsCount).ToArray(); // -1 is unknown
int[] tamedLevels = new int[Stats.StatsCount];
int[] mutatedLevels = new int[Stats.StatsCount];

for (int i = 0; i < Stats.StatsCount; i++)
{
wildLevels[i] = creatureObject.WildLevels[i] ?? 0;
}
wildLevels[Stats.Torpidity] = (creatureObject.BaseLevel ?? 1) - 1; // torpor

for (int i = 0; i < Stats.StatsCount; i++)
{
tamedLevels[i] = creatureObject.TamedLevelsApplied[i] ?? 0;
mutatedLevels[i] = creatureObject.MutationLevelsApplied[i] ?? 0;
}

if (!creatureObject.Record.Properties.TryGet("TamedIneffectivenessModifier", out float ti))
{
ti = creatureObject.Record.Properties.Get<float>("TameIneffectivenessModifier");
}
double te = 1f / (1 + ti);

Guid creatureGuid = creatureObject.Id;
long arkId = Utils.ConvertCreatureGuidToArkId(creatureGuid);
Creature creature = new Creature(species,
creatureObject.TamedName, owner, creatureObject.TribeName,
creatureObject.IsFemale ? Sex.Female : Sex.Male,
wildLevels, tamedLevels, mutatedLevels, te,
!string.IsNullOrWhiteSpace(creatureObject.ImprinterName),
creatureObject.ImprintQuality,
levelStep
)
{
imprinterName = creatureObject.ImprinterName,
guid = creatureGuid,
ArkId = arkId,
ArkIdImported = true,
ArkIdInGame = Utils.ConvertImportedArkIdToIngameVisualization(arkId),
domesticatedAt = DateTime.Now,
addedToLibrary = DateTime.Now,
mutationsMaternal = creatureObject.MutationsFemale,
mutationsPaternal = creatureObject.MutationsMale,
flags = (creatureObject.Record.Properties.Get<bool>("bNeutered") ? CreatureFlags.Neutered : CreatureFlags.None)
| (creatureObject.Record.Properties.Get<bool>("MutagenApplied") ? CreatureFlags.MutagenApplied : CreatureFlags.None)
};

// If it's a baby and still growing, work out growingUntil
float babyAge = creatureObject.BabyAge ?? 1;
if (babyAge < 1)
{
double maturationDuration = species.breeding?.maturationTimeAdjusted ?? 0;
float bornSecondsAgo = (float)maturationDuration * babyAge;
if (bornSecondsAgo < maturationDuration - 120) // there seems to be a slight offset of one of these saved values, so don't display a creature as being in cooldown if it is about to leave it in the next 2 minutes
creature.growingUntil = DateTime.Now.Add(TimeSpan.FromSeconds(maturationDuration - bornSecondsAgo));
}
else
{
double nextMatingPossible = creatureObject.Record.Properties.Get<double>("NextAllowedMatingTime");
if (_gameTime < nextMatingPossible)
{
creature.cooldownUntil = DateTime.Now.Add(TimeSpan.FromSeconds(nextMatingPossible - _gameTime));
}
}

// Ancestor linking is done later after entire collection is formed - here we just set the guids
#if DEBUG
IEnumerable<DinoAncestorsEntryRecord> femaleAncestors = creatureObject.Record.Properties.Get<object[]>("DinoAncestors")?.OfType<DinoAncestorsEntryRecord>();
DinoAncestorsEntryRecord femaleAncestor = femaleAncestors?.LastOrDefault();
if (femaleAncestor != null)
{
creature.motherGuid = Utils.ConvertArkIdToGuid(Utils.ConvertArkIdsToLongArkId(
(int)femaleAncestor.FemaleId1,
(int)femaleAncestor.FemaleId2));
creature.motherName = femaleAncestor.FemaleName;
creature.isBred = true;
}
IEnumerable<DinoAncestorsEntryRecord> maleAncestors = creatureObject.Record.Properties.Get<object[]>("DinoAncestors")?.OfType<DinoAncestorsEntryRecord>();
DinoAncestorsEntryRecord maleAncestor = maleAncestors?.LastOrDefault();
if (maleAncestor != null)
{
creature.fatherGuid = Utils.ConvertArkIdToGuid(Utils.ConvertArkIdsToLongArkId(
(int)maleAncestor.MaleId1,
(int)maleAncestor.MaleId2));
creature.fatherName = maleAncestor.MaleName;
creature.isBred = true;
}
#else
IEnumerable<List<Property>> femaleAncestors = creatureObject.Record.Properties.Get<object[]>("DinoAncestors")?.OfType<List<Property>>(); // for now, this has to use the raw properties until https://github.com/flintthatchwood/AsaSavegameToolkit/pull/4 is merged and published.
List<Property> femaleAncestor = femaleAncestors?.LastOrDefault();
if (femaleAncestor != null)
{
creature.motherGuid = Utils.ConvertArkIdToGuid(Utils.ConvertArkIdsToLongArkId(
(int)femaleAncestor.Get<uint>("FemaleDinoID1"),
(int)femaleAncestor.Get<uint>("FemaleDinoID2")));
creature.motherName = femaleAncestor.Get<string>("FemaleName");
creature.isBred = true;
}
IEnumerable<List<Property>> maleAncestors = creatureObject.Record.Properties.Get<object[]>("DinoAncestorsMale")?.OfType<List<Property>>();
List<Property> maleAncestor = maleAncestors?.LastOrDefault();
if (maleAncestor != null)
{
creature.fatherGuid = Utils.ConvertArkIdToGuid(GameObjectExtensions.CreateDinoId(
(int)maleAncestor.Get<uint>("MaleDinoID1"),
(int)maleAncestor.Get<uint>("MaleDinoID2")));
creature.fatherName = maleAncestor.Get<string>("MaleName");
creature.isBred = true;
}
#endif

creature.colors = new byte[Ark.ColorRegionCount];
for (int i = 0; i < 6; i++)
{
creature.colors[i] = creatureObject.ColorRegions[i] ?? 0;
}

bool isDead = creatureObject.Record.Properties.Get<bool>("bIsDead");
if (isDead)
{
creature.Status = CreatureStatus.Dead; // dead is always dead
}

if (creatureObject.IsInCryo)
creature.Status = CreatureStatus.Cryopod;

creature.RecalculateCreatureValues(levelStep);

return creature;
}
}
}
6 changes: 6 additions & 0 deletions ARKBreedingStats/values/Values.cs
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,12 @@ public Species SpeciesByBlueprint(string blueprintPath, bool removeTrailingC) =>
? blueprintPath.Substring(0, blueprintPath.Length - 2)
: blueprintPath);

public bool TryGetSpeciesByBlueprint(string blueprintPath, out Species species, bool removeTrailingC = true)
{
species = SpeciesByBlueprint(blueprintPath, removeTrailingC);
return species != null;
}

/// <summary>
/// Sets the ModsManifest. If the value is null, a new default object will be created.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions AsaSavegameToolkit
Submodule AsaSavegameToolkit added at 2006dc