/// <summary> /// Extracts possible level combinations for the given values. /// </summary> /// <param name="species"></param> /// <param name="level">Total level of the creature.</param> /// <param name="statIOs">Controls that display the stats</param> /// <param name="lowerTEBound">Lowest possible taming effectiveness</param> /// <param name="upperTEBound">Highest possible taming effectiveness</param> /// <param name="tamed"></param> /// <param name="bred"></param> /// <param name="imprintingBonusRounded"></param> /// <param name="adjustImprinting"></param> /// <param name="allowMoreThanHundredImprinting"></param> /// <param name="imprintingBonusMultiplier"></param> /// <param name="cuddleIntervalMultiplier"></param> /// <param name="considerWildLevelSteps"></param> /// <param name="wildLevelSteps"></param> /// <param name="highPrecisionInputs">If true, the input is expected to be a float value from an export file. /// If false, it's assumed to be a displayed value from the game with one decimal digit.</param> /// <param name="imprintingChanged"></param> public void ExtractLevels(Species species, int level, List <StatIO> statIOs, double lowerTEBound, double upperTEBound, bool tamed, bool bred, double imprintingBonusRounded, bool adjustImprinting, bool allowMoreThanHundredImprinting, double imprintingBonusMultiplier, double cuddleIntervalMultiplier, bool considerWildLevelSteps, int wildLevelSteps, bool highPrecisionInputs, out bool imprintingChanged) { List <CreatureStat> stats = species.stats; validResults = true; imprintingChanged = false; considerWildLevelSteps = considerWildLevelSteps && !bred && species.name.Substring(0, 3) != "Tek" && species.name != "Jerboa" ; this.bred = bred; postTamed = bred || tamed; List <MinMaxDouble> imprintingBonusList = new List <MinMaxDouble> { new MinMaxDouble(0) }; if (bred) { if (!adjustImprinting) { imprintingBonusList[0] = new MinMaxDouble(imprintingBonusRounded); } else { imprintingBonusList = CalculateImprintingBonus(species, imprintingBonusRounded, imprintingBonusMultiplier, cuddleIntervalMultiplier, statIOs[(int)StatNames.Torpidity].Input, statIOs[(int)StatNames.Food].Input); } } for (int IBi = 0; IBi < imprintingBonusList.Count; IBi++) { imprintingBonusRange = imprintingBonusList[IBi]; imprintingBonusRange.SetToIntersectionWith(0, (allowMoreThanHundredImprinting ? 5 : 1)); // it's assumed that a valid IB will not be larger than 500% var imprintingMultiplierRanges = new MinMaxDouble[Values.STATS_COUNT]; for (int s = 0; s < Values.STATS_COUNT; s++) { double statImprintingMultiplier = species.StatImprintMultipliers[s]; imprintingMultiplierRanges[s] = statImprintingMultiplier != 0 ? new MinMaxDouble(1 + imprintingBonusRange.Min * imprintingBonusMultiplier * statImprintingMultiplier, 1 + imprintingBonusRange.Max * imprintingBonusMultiplier * statImprintingMultiplier) : new MinMaxDouble(1); } var levelWildSumRange = new MinMaxInt((int)Math.Round((statIOs[(int)StatNames.Torpidity].Input / imprintingMultiplierRanges[(int)StatNames.Torpidity].Max - (postTamed ? stats[(int)StatNames.Torpidity].AddWhenTamed : 0) - stats[(int)StatNames.Torpidity].BaseValue) / (stats[(int)StatNames.Torpidity].BaseValue * stats[(int)StatNames.Torpidity].IncPerWildLevel)), (int)Math.Round((statIOs[(int)StatNames.Torpidity].Input / imprintingMultiplierRanges[(int)StatNames.Torpidity].Min - (postTamed ? stats[(int)StatNames.Torpidity].AddWhenTamed : 0) - stats[(int)StatNames.Torpidity].BaseValue) / (stats[(int)StatNames.Torpidity].BaseValue * stats[(int)StatNames.Torpidity].IncPerWildLevel))); var levelDomSumRange = new MinMaxInt(Math.Max(0, level - 1 - levelWildSumRange.Max), Math.Max(0, level - 1 - levelWildSumRange.Min)); levelWildSum = levelWildSumRange.Min; levelDomSum = levelDomSumRange.Min; // TODO implement range-mechanic levelsUndeterminedWild = levelWildSum; levelsUndeterminedDom = levelDomSum; if (bred) { // bred creatures always have 100% TE lowerTEBound = 1; upperTEBound = 1; } else { // sometimes it fails due to double-precision errors, e.g. // Pteranodon (Lvl 34, TE: 80%): HP: 415.9 (6, 0); St: 195 (6, 0); Ox: 240 (6, 0); Fo: 2150.4 (6, 0); We: 134.4 (6, 0); Dm: 141.6% (3, 0); Sp: 135% (0, 0); To: 358.1 (33); // will fail the extraction with a lowerTEBound of 0.8, it only extracts with a lowerTEBound of 0.79, then displays 0.8 as result for the TE. Adding these margins make it work as expected. lowerTEBound -= 0.0006; if (lowerTEBound < 0) { lowerTEBound = 0; } upperTEBound += 0.0006; } // check all possible level-combinations for (int s = 0; s < Values.STATS_COUNT; s++) { if (!species.UsesStat(s)) { results[s].Add(new StatResult(0, 0)); continue; } if (statIOs[s].Input <= 0) // if stat is unknown (e.g. oxygen sometimes is not shown) { results[s].Add(new StatResult(-1, 0)); continue; } statIOs[s].postTame = postTamed; // determine the precision of the input value float toleranceForThisStat = StatValueCalculation.DisplayedAberration(statIOs[s].Input, Utils.Precision(s), highPrecisionInputs); //Console.WriteLine($"Precision stat {s}: {toleranceForThisStat}"); MinMaxDouble inputValue = new MinMaxDouble(statIOs[s].Input - toleranceForThisStat, statIOs[s].Input + toleranceForThisStat); double statBaseValue = stats[s].BaseValue; if (postTamed && s == (int)StatNames.Health) { statBaseValue *= (double)species.TamedBaseHealthMultiplier; // + 0.00000000001; // todo double-precision handling } bool withTEff = (postTamed && stats[s].MultAffinity > 0); if (withTEff) { statsWithTE.Add(s); } int minLW = 0; int maxLW; if (stats[s].IncPerWildLevel > 0) { double multAffinityFactor = stats[s].MultAffinity; if (postTamed) { // the multiplicative bonus is only multiplied with the TE if it is positive (i.e. negative boni won't get less bad if the TE is low) if (multAffinityFactor > 0) { multAffinityFactor *= lowerTEBound; } multAffinityFactor += 1; } else { multAffinityFactor = 1; } maxLW = (int)Math.Round(((inputValue.Max / multAffinityFactor - (postTamed ? stats[s].AddWhenTamed : 0)) / statBaseValue - 1) / stats[s].IncPerWildLevel); // floor is too unprecise } else { minLW = -1; maxLW = -1; } if (maxLW > levelWildSum) { maxLW = levelWildSum; } double maxLD = 0; if (!statIOs[s].DomLevelLockedZero && postTamed && species.DisplaysStat(s) && stats[s].IncPerTamedLevel > 0) { int ww = 0; // base wild level for the tamed creature needed to be alive if (statBaseValue + stats[s].AddWhenTamed < 0) { // e.g. Griffin // get lowest wild level at which the creature is alive while (StatValueCalculation.CalculateValue(species, s, ww, 0, true, lowerTEBound, 0, false) <= 0) { ww++; } } maxLD = Math.Round((inputValue.Max / ((statBaseValue * (1 + stats[s].IncPerWildLevel * ww) + stats[s].AddWhenTamed) * (1 + lowerTEBound * stats[s].MultAffinity)) - 1) / stats[s].IncPerTamedLevel); //floor is sometimes too low } if (maxLD > levelsUndeterminedDom) { maxLD = levelsUndeterminedDom; } if (maxLD < 0) { maxLD = 0; } MinMaxDouble statImprintingMultiplierRange = new MinMaxDouble(1); // only use imprintingMultiplier for stats that use them. Stamina and Oxygen don't use ist. Sometimes speed neither. if (bred && species.StatImprintMultipliers[s] != 0) { statImprintingMultiplierRange = imprintingMultiplierRanges[s].Clone(); } // if dom levels have no effect, just calculate the wild level // for flyers (without mods) this means for speed no wild levels at all (i.e. not unknown, but 0) // for the Diplodocus this means 0 wild levels in melee if (stats[s].IncPerTamedLevel == 0) { if (stats[s].IncPerWildLevel == 0) { // check if the input value is valid MinMaxDouble possibleStatValues = new MinMaxDouble(StatValueCalculation.CalculateValue(species, s, 0, 0, postTamed, lowerTEBound, imprintingBonusRange.Min, false), StatValueCalculation.CalculateValue(species, s, 0, 0, postTamed, upperTEBound, imprintingBonusRange.Max, false)); if (inputValue.Overlaps(possibleStatValues)) { results[s].Add(new StatResult(0, 0, inputValue.Mean)); } } else { MinMaxDouble lwRange = new MinMaxDouble(((inputValue.Min / (postTamed ? 1 + stats[s].MultAffinity : 1) - (postTamed ? stats[s].AddWhenTamed : 0)) / (statBaseValue * statImprintingMultiplierRange.Max) - 1) / stats[s].IncPerWildLevel, ((inputValue.Max / (postTamed ? 1 + stats[s].MultAffinity : 1) - (postTamed ? stats[s].AddWhenTamed : 0)) / (statBaseValue * statImprintingMultiplierRange.Min) - 1) / stats[s].IncPerWildLevel); int lw = (int)Math.Round(lwRange.Mean); if (lwRange.Includes(lw) && lw >= 0 && lw <= maxLW) { results[s].Add(new StatResult(lw, 0, inputValue.Mean)); } } // even if no result was found, there is no other valid continue; } for (int lw = minLW; lw < maxLW + 1; lw++) { // imprinting bonus is applied to all stats except stamina (s==1) and oxygen (s==2) and speed (s==6) MinMaxDouble valueWODomRange = new MinMaxDouble(statBaseValue * (1 + stats[s].IncPerWildLevel * lw) * statImprintingMultiplierRange.Min + (postTamed ? stats[s].AddWhenTamed : 0), statBaseValue * (1 + stats[s].IncPerWildLevel * lw) * statImprintingMultiplierRange.Max + (postTamed ? stats[s].AddWhenTamed : 0)); // value without domesticated levels if (!withTEff) { // calculate the only possible Ld, if it's an integer, take it. if (stats[s].IncPerTamedLevel > 0) { MinMaxDouble ldRange = new MinMaxDouble((inputValue.Min / (valueWODomRange.Max * (postTamed ? 1 + stats[s].MultAffinity : 1)) - 1) / stats[s].IncPerTamedLevel, (inputValue.Max / (valueWODomRange.Min * (postTamed ? 1 + stats[s].MultAffinity : 1)) - 1) / stats[s].IncPerTamedLevel); int ld = (int)Math.Round(ldRange.Mean); if (ldRange.Includes(ld) && ld >= 0 && ld <= maxLD) { results[s].Add(new StatResult(lw, ld, inputValue.Mean)); } } else { results[s].Add(new StatResult(lw, 0, inputValue.Mean)); } } else { for (int ld = 0; ld <= maxLD; ld++) { // taming bonus is dependant on taming-effectiveness // get tamingEffectiveness-possibility // calculate rounding-error thresholds. Here it's assumed that the displayed ingame value is maximal 0.5 off of the true ingame value MinMaxDouble tamingEffectiveness = new MinMaxDouble((inputValue.Min / (1 + stats[s].IncPerTamedLevel * ld) - valueWODomRange.Max) / (valueWODomRange.Max * stats[s].MultAffinity), (inputValue.Max / (1 + stats[s].IncPerTamedLevel * ld) - valueWODomRange.Min) / (valueWODomRange.Min * stats[s].MultAffinity)); if (tamingEffectiveness.Min > upperTEBound) { continue; } if (tamingEffectiveness.Max < lowerTEBound) { break; // if tamingEff < lowerBound: break, in this d-loop it's getting only smaller } // here it's ensured the TE overlaps the bounds, so we can clamp it to the bounds if (tamingEffectiveness.Min < lowerTEBound) { tamingEffectiveness.Min = lowerTEBound; } if (tamingEffectiveness.Max > upperTEBound) { tamingEffectiveness.Max = upperTEBound; } if (!bred) { // check if the totalLevel and the TE is possible by using the TE-levelbonus (credits for this check which sorts out more impossible results: https://github.com/VolatilePulse , thanks!) int levelPostTame = levelWildSum + 1; MinMaxInt levelPreTameRange = new MinMaxInt(Creature.CalculatePreTameWildLevel(levelPostTame, tamingEffectiveness.Max), Creature.CalculatePreTameWildLevel(levelPostTame, tamingEffectiveness.Min)); bool impossibleTE = true; for (int wildLevel = levelPreTameRange.Min; wildLevel <= levelPreTameRange.Max; wildLevel++) { MinMaxInt levelPostTameRange = new MinMaxInt((int)Math.Floor(wildLevel * (1 + tamingEffectiveness.Min / 2)), (int)Math.Floor(wildLevel * (1 + tamingEffectiveness.Max / 2))); if (levelPostTameRange.Includes(levelPostTame)) { impossibleTE = false; break; } } if (impossibleTE) { continue; } // test if TE with torpor-level of tamed-creatures results in a valid wild-level according to the possible levelSteps if (considerWildLevelSteps) { bool validWildLevel = false; for (int wildLevel = levelPreTameRange.Min; wildLevel <= levelPreTameRange.Max; wildLevel++) { if (wildLevel % wildLevelSteps == 0) { validWildLevel = true; break; } } if (!validWildLevel) { continue; } } // if another stat already is dependant on TE, check if this TE overlaps any of their TE-ranges. If not, TE is not possible (a creature can only have the same TE for all TE-dependant stats) if (statsWithTE.Count > 1) { bool TEExistant = false; for (int er = 0; er < results[statsWithTE[0]].Count; er++) { if (tamingEffectiveness.Overlaps(results[statsWithTE[0]][er].TE)) { TEExistant = true; break; } } if (!TEExistant) { continue; } } } results[s].Add(new StatResult(lw, ld, inputValue.Mean, tamingEffectiveness)); } } } } if (bred) { // if each stat has at least one result, assume the extraction was valid with the chosen IB if (EveryStatHasAtLeastOneResult) { // all stats have a result, don't test the other possible IBs imprintingChanged = (Math.Abs(imprintingBonusRounded - ImprintingBonus) > 0.01); break; } else if (IBi < imprintingBonusList.Count - 1) { // not all stats got a result, clear results for the next round Clear(); validResults = true; } } } }
public void Calculate(Species species, int[] wildLevels1, int[] wildLevels2) { var levelProbabilities = new Dictionary <int, double>(); double maxProbability = 0; if (wildLevels1 == null || wildLevels2 == null || wildLevels1.Length != Values.STATS_COUNT || wildLevels2.Length != Values.STATS_COUNT) { Clear(true); return; } List <int> usedStatIndicesTest = new List <int>(Values.STATS_COUNT); for (int s = 0; s < Values.STATS_COUNT; s++) { if (species.UsesStat(s) && s != (int)StatNames.Torpidity) { usedStatIndicesTest.Add(s); } } int usedStatsCount = usedStatIndicesTest.Count; List <int> usedStatIndices = new List <int>(usedStatsCount); // first check for equal levels, these can be skipped in the all-combinations loop int minimumLevel = 1; // includes the base level and all levels that are equal in both parents for (int s = 0; s < usedStatsCount; s++) { if (wildLevels1[usedStatIndicesTest[s]] == wildLevels2[usedStatIndicesTest[s]]) { minimumLevel += wildLevels1[usedStatIndicesTest[s]]; } else { usedStatIndices.Add(usedStatIndicesTest[s]); } } usedStatsCount = usedStatIndices.Count; int totalLevelCombinations = 1 << usedStatsCount; // loop through all combinations the offspring can inherit stat-levels // each used stat multiplies the combinations by two for (int p = 0; p < totalLevelCombinations; p++) { int totalLevel = minimumLevel; double probability = 1; for (int s = 0; s < usedStatsCount; s++) { // determine if stat-level of creature one or two should be used if ((p & (1 << s)) != 0) { // use the stat level of creature 1 totalLevel += wildLevels1[usedStatIndices[s]]; probability *= wildLevels1[usedStatIndices[s]] > wildLevels2[usedStatIndices[s]] ? BreedingPlan.ProbabilityHigherLevel : BreedingPlan.ProbabilityLowerLevel; } else { // use the stat level of creature 2 totalLevel += wildLevels2[usedStatIndices[s]]; probability *= wildLevels1[usedStatIndices[s]] < wildLevels2[usedStatIndices[s]] ? BreedingPlan.ProbabilityHigherLevel : BreedingPlan.ProbabilityLowerLevel; } } if (!levelProbabilities.ContainsKey(totalLevel)) { levelProbabilities[totalLevel] = 0; } levelProbabilities[totalLevel] += probability; if (levelProbabilities[totalLevel] > maxProbability) { maxProbability = levelProbabilities[totalLevel]; } } DrawBars(levelProbabilities, maxProbability); }
private List <MinMaxDouble> CalculateImprintingBonus(Species species, double imprintingBonusRounded, double imprintingBonusMultiplier, double cuddleIntervalMultiplier, double torpor, double food) { List <MinMaxDouble> imprintingBonusList = new List <MinMaxDouble>(); if (species.stats[(int)StatNames.Torpidity].BaseValue == 0 || species.stats[(int)StatNames.Torpidity].IncPerWildLevel == 0) { return(imprintingBonusList); // invalid species-data } // classic way to calculate the ImprintingBonus, this is the most exact value, but will not work if the imprinting-gain was different (e.g. events, mods (S+Nanny)) double imprintingBonusFromGainPerCuddle = 0; if (species.breeding != null) { double imprintingGainPerCuddle = Utils.ImprintingGainPerCuddle(species.breeding.maturationTimeAdjusted, cuddleIntervalMultiplier); imprintingBonusFromGainPerCuddle = Math.Round(imprintingBonusRounded / imprintingGainPerCuddle) * imprintingGainPerCuddle; } double[] statImprintMultipliers = species.StatImprintMultipliers; MinMaxInt wildLevelsFromImprintedTorpor = new MinMaxInt( (int)Math.Round(((((torpor / (1 + species.stats[(int)StatNames.Torpidity].MultAffinity)) - species.stats[(int)StatNames.Torpidity].AddWhenTamed) / ((1 + (imprintingBonusRounded + 0.005) * statImprintMultipliers[(int)StatNames.Torpidity] * imprintingBonusMultiplier) * species.stats[(int)StatNames.Torpidity].BaseValue)) - 1) / species.stats[(int)StatNames.Torpidity].IncPerWildLevel), (int)Math.Round(((((torpor / (1 + species.stats[(int)StatNames.Torpidity].MultAffinity)) - species.stats[(int)StatNames.Torpidity].AddWhenTamed) / ((1 + (imprintingBonusRounded - 0.005) * statImprintMultipliers[(int)StatNames.Torpidity] * imprintingBonusMultiplier) * species.stats[(int)StatNames.Torpidity].BaseValue)) - 1) / species.stats[(int)StatNames.Torpidity].IncPerWildLevel)); // assuming food has no dom-levels, extract the exact imprinting from this stat. If the range is in the range of the torpor-dependant IB, take this more precise value for the imprinting. (food has higher values and yields more precise results) MinMaxInt wildLevelsFromImprintedFood = new MinMaxInt( (int)Math.Round(((((food / (1 + species.stats[(int)StatNames.Food].MultAffinity)) - species.stats[(int)StatNames.Food].AddWhenTamed) / ((1 + (imprintingBonusRounded + 0.005) * statImprintMultipliers[(int)StatNames.Food] * imprintingBonusMultiplier) * species.stats[(int)StatNames.Food].BaseValue)) - 1) / species.stats[(int)StatNames.Food].IncPerWildLevel), (int)Math.Round(((((food / (1 + species.stats[(int)StatNames.Food].MultAffinity)) - species.stats[(int)StatNames.Food].AddWhenTamed) / ((1 + (imprintingBonusRounded - 0.005) * statImprintMultipliers[(int)StatNames.Food] * imprintingBonusMultiplier) * species.stats[(int)StatNames.Food].BaseValue)) - 1) / species.stats[(int)StatNames.Food].IncPerWildLevel)); List <int> otherStatsSupportIB = new List <int>(); // the number of other stats that support this IB-range // for high-level creatures the bonus from imprinting is so high, that a displayed and rounded value of the imprinting bonus can be possible with multiple torpor-levels, i.e. 1 %point IB results in a larger change than a level in torpor. for (int torporLevel = wildLevelsFromImprintedTorpor.Min; torporLevel <= wildLevelsFromImprintedTorpor.Max; torporLevel++) { int support = 0; double imprintingProductTorpor = statImprintMultipliers[(int)StatNames.Torpidity] * imprintingBonusMultiplier; double imprintingProductFood = statImprintMultipliers[(int)StatNames.Food] * imprintingBonusMultiplier; if (imprintingProductTorpor == 0 || imprintingProductFood == 0) { break; } MinMaxDouble imprintingBonusRange = new MinMaxDouble( (((torpor - 0.05) / (1 + species.stats[(int)StatNames.Torpidity].MultAffinity) - species.stats[(int)StatNames.Torpidity].AddWhenTamed) / StatValueCalculation.CalculateValue(species, (int)StatNames.Torpidity, torporLevel, 0, false, 0, 0) - 1) / imprintingProductTorpor, (((torpor + 0.05) / (1 + species.stats[(int)StatNames.Torpidity].MultAffinity) - species.stats[(int)StatNames.Torpidity].AddWhenTamed) / StatValueCalculation.CalculateValue(species, (int)StatNames.Torpidity, torporLevel, 0, false, 0, 0) - 1) / imprintingProductTorpor); // check for each possible food-level the IB-range and if it can narrow down the range derived from the torpor (deriving from food is more precise, due to the higher values) for (int foodLevel = wildLevelsFromImprintedFood.Min; foodLevel <= wildLevelsFromImprintedFood.Max; foodLevel++) { MinMaxDouble imprintingBonusFromFood = new MinMaxDouble( (((food - 0.05) / (1 + species.stats[(int)StatNames.Food].MultAffinity) - species.stats[(int)StatNames.Food].AddWhenTamed) / StatValueCalculation.CalculateValue(species, (int)StatNames.Food, foodLevel, 0, false, 0, 0) - 1) / imprintingProductFood, (((food + 0.05) / (1 + species.stats[(int)StatNames.Food].MultAffinity) - species.stats[(int)StatNames.Food].AddWhenTamed) / StatValueCalculation.CalculateValue(species, (int)StatNames.Food, foodLevel, 0, false, 0, 0) - 1) / imprintingProductFood); // NOTE: it's assumed if the IB-food is in the range of IB-torpor, the values are correct. This doesn't have to be true, but is very probable. If extraction-issues appear, this assumption could be the reason. //if (imprintingBonusFromTorpor.Includes(imprintingBonusFromFood) if (imprintingBonusRange.Overlaps(imprintingBonusFromFood)) { MinMaxDouble intersectionIB = new MinMaxDouble(imprintingBonusRange); intersectionIB.SetToIntersectionWith(imprintingBonusFromFood); if (StatValueCalculation.CalculateValue(species, (int)StatNames.Torpidity, torporLevel, 0, true, 1, intersectionIB.Min) <= torpor && StatValueCalculation.CalculateValue(species, (int)StatNames.Torpidity, torporLevel, 0, true, 1, intersectionIB.Max) >= torpor) { //imprintingBonusFromTorpor = imprintingBonusFromFood; imprintingBonusRange.SetToIntersectionWith(imprintingBonusFromFood); support++; } } } // if classic method results in value in the possible range, take this, probably most exact value if (imprintingBonusRange.Includes(imprintingBonusFromGainPerCuddle) && StatValueCalculation.CalculateValue(species, (int)StatNames.Torpidity, torporLevel, 0, true, 1, imprintingBonusFromGainPerCuddle) == torpor) { imprintingBonusRange.MinMax = imprintingBonusFromGainPerCuddle; support++; } // TODO check if this range has already been added to avoid double loops in the extraction. if existant, update support imprintingBonusList.Add(imprintingBonusRange); otherStatsSupportIB.Add(support); } // sort IB according to the support they got by other stats, then return the distinct means of the possible ranges. return(imprintingBonusList.OrderByDescending(i => otherStatsSupportIB[imprintingBonusList.IndexOf(i)]).ToList()); }
/// <summary> /// Import exported file. Used by a fileWatcher. /// </summary> /// <param name="filePath"></param> private void ImportExportedAddIfPossible(string filePath) { var loadResult = ExtractExportedFileInExtractor(filePath); if (!loadResult.HasValue) { return; } bool alreadyExists = loadResult.Value; bool addedToLibrary = false; bool uniqueExtraction = _extractor.UniqueResults || (alreadyExists && _extractor.ValidResults); bool copyNameToClipboard = Properties.Settings.Default.copyNameToClipboardOnImportWhenAutoNameApplied && (Properties.Settings.Default.applyNamePatternOnAutoImportAlways || Properties.Settings.Default.applyNamePatternOnImportIfEmptyName || (!alreadyExists && Properties.Settings.Default.applyNamePatternOnAutoImportForNewCreatures) ); Species species = speciesSelector1.SelectedSpecies; Creature creature = null; if (uniqueExtraction && Properties.Settings.Default.OnAutoImportAddToLibrary) { creature = AddCreatureToCollection(true, goToLibraryTab: Properties.Settings.Default.AutoImportGotoLibraryAfterSuccess); SetMessageLabelText($"Successful {(alreadyExists ? "updated" : "added")} {creature.name} ({species.name}) of the exported file\n" + filePath, MessageBoxIcon.Information, filePath); addedToLibrary = true; } bool topLevels = false; bool newTopLevels = false; // give feedback in overlay string infoText; Color textColor; const int colorSaturation = 200; if (uniqueExtraction) { var sb = new StringBuilder(); sb.AppendLine($"{species.name} \"{creatureInfoInputExtractor.CreatureName}\" {(alreadyExists ? "updated in " : "added to")} the library."); if (addedToLibrary && copyNameToClipboard) { sb.AppendLine("Name copied to clipboard."); } for (int s = 0; s < values.Values.STATS_COUNT; s++) { int statIndex = values.Values.statsDisplayOrder[s]; if (!species.UsesStat(statIndex)) { continue; } sb.Append($"{Utils.StatName(statIndex, true, species.statNames)}: { _statIOs[statIndex].LevelWild} ({_statIOs[statIndex].BreedingValue})"); if (_statIOs[statIndex].TopLevel.HasFlag(LevelStatus.NewTopLevel)) { sb.Append($" {Loc.S("newTopLevel")}"); newTopLevels = true; } else if (_statIOs[statIndex].TopLevel.HasFlag(LevelStatus.TopLevel)) { sb.Append($" {Loc.S("topLevel")}"); topLevels = true; } sb.AppendLine(); } infoText = sb.ToString(); textColor = Color.FromArgb(colorSaturation, 255, colorSaturation); } else { infoText = $"Creature \"{creatureInfoInputExtractor.CreatureName}\" couldn't be extracted uniquely, manual level selection is necessary."; textColor = Color.FromArgb(255, colorSaturation, colorSaturation); } if (_overlay != null) { _overlay.SetInfoText(infoText, textColor); if (Properties.Settings.Default.DisplayInheritanceInOverlay && creature != null) { _overlay.SetInheritanceCreatures(creature, creature.Mother, creature.Father); } } if (addedToLibrary) { if (Properties.Settings.Default.DeleteAutoImportedFile) { FileService.TryDeleteFile(filePath); } else if (Properties.Settings.Default.MoveAutoImportedFileToSubFolder || Properties.Settings.Default.AutoImportedExportFileRename) { string newPath = Properties.Settings.Default.MoveAutoImportedFileToSubFolder ? (string.IsNullOrEmpty(Properties.Settings.Default.ImportExportedArchiveFolder) ? Path.Combine(Path.GetDirectoryName(filePath), "imported") : Properties.Settings.Default.ImportExportedArchiveFolder) : Path.GetDirectoryName(filePath); if (Properties.Settings.Default.MoveAutoImportedFileToSubFolder && !FileService.TryCreateDirectory(newPath, out string errorMessage)) { MessageBoxes.ShowMessageBox($"Subfolder\n{newPath}\ncould not be created.\n{errorMessage}"); return; } string namePattern = Properties.Settings.Default.AutoImportedExportFileRenamePattern; string newFileName = Properties.Settings.Default.AutoImportedExportFileRename && !string.IsNullOrWhiteSpace(namePattern) ? NamePattern.GenerateCreatureName(creature, _creatureCollection.creatures.Where(c => c.Species == speciesSelector1.SelectedSpecies).ToArray(), null, null, _customReplacingNamingPattern, false, -1, false, namePattern) : Path.GetFileName(filePath); // remove invalid characters var invalidCharacters = Path.GetInvalidFileNameChars(); foreach (var invalidChar in invalidCharacters) { newFileName = newFileName.Replace(invalidChar, '_'); } string newFileNameWithoutExtension = Path.GetFileNameWithoutExtension(newFileName); string newFileNameExtension = Path.GetExtension(newFileName); string newFilePath = Path.Combine(newPath, newFileName); int fileSuffix = 1; while (File.Exists(newFilePath)) { newFilePath = Path.Combine(newPath, $"{newFileNameWithoutExtension}_{++fileSuffix}{newFileNameExtension}"); } if (FileService.TryMoveFile(filePath, newFilePath)) { _librarySelectionInfoClickPath = newFilePath; } } } else if (!uniqueExtraction && copyNameToClipboard) { // extraction failed, user might expect the name of the new creature in the clipboard Clipboard.SetText("Automatic extraction was not possible"); } if (Properties.Settings.Default.PlaySoundOnAutoImport) { if (uniqueExtraction) { if (alreadyExists) { SoundFeedback.BeepSignal(SoundFeedback.FeedbackSounds.Indifferent); } if (newTopLevels) { SoundFeedback.BeepSignal(SoundFeedback.FeedbackSounds.Great); } else if (topLevels) { SoundFeedback.BeepSignal(SoundFeedback.FeedbackSounds.Good); } else { SoundFeedback.BeepSignal(SoundFeedback.FeedbackSounds.Success); } } else { SoundFeedback.BeepSignal(SoundFeedback.FeedbackSounds.Failure); } } }
/// <summary> /// Loads the given creature collection file. /// </summary> /// <param name="filePath">File that contains the collection</param> /// <param name="keepCurrentCreatures">add the creatures of the loaded file to the current ones</param> /// <param name="keepCurrentSelections">don't change the species selection or tab</param> /// <returns></returns> private bool LoadCollectionFile(string filePath, bool keepCurrentCreatures = false, bool keepCurrentSelections = false) { Species selectedSpecies = speciesSelector1.SelectedSpecies; Species selectedlibrarySpecies = listBoxSpeciesLib.SelectedItem as Species; if (!File.Exists(filePath)) { MessageBox.Show($"Save file with name \"{filePath}\" does not exist!", "File not found", MessageBoxButtons.OK, MessageBoxIcon.Error); return(false); } List <Creature> oldCreatures = null; if (keepCurrentCreatures) { oldCreatures = creatureCollection.creatures; } // for the case the collectionfile has no multipliers, keep the current ones ServerMultipliers oldMultipliers = creatureCollection.serverMultipliers; // Wait until the file is readable const int numberOfRetries = 5; const int delayOnRetry = 1000; FileStream fileStream = null; for (int i = 1; i <= numberOfRetries; ++i) { try { if (Path.GetExtension(filePath).ToLower() == ".xml") { using (fileStream = File.OpenRead(filePath)) { // use xml-serializer for old library-format XmlSerializer reader = new XmlSerializer(typeof(oldLibraryFormat.CreatureCollectionOld)); var creatureCollectionOld = (oldLibraryFormat.CreatureCollectionOld)reader.Deserialize(fileStream); List <Mod> mods = null; // first check if additional values are used, and if the according values-file is already available. // if not, abort conversion and first make sure the file is available, e.g. downloaded if (!string.IsNullOrEmpty(creatureCollectionOld.additionalValues)) { // usually the old filename is equal to the mod-tag bool modFound = false; string modTag = Path.GetFileNameWithoutExtension(creatureCollectionOld.additionalValues).Replace(" ", "").ToLower().Replace("gaiamod", "gaia"); foreach (KeyValuePair <string, ModInfo> tmi in Values.V.modsManifest.modsByTag) { if (tmi.Key.ToLower() == modTag) { modFound = true; MessageBox.Show("The library contains creatures of modded species. For a correct file-conversion the correct mod-values file is needed.\n\n" + "If the mod-value file is not loaded, the conversion may assign wrong species to your creatures.\n" + "If the mod-value file is not available locally, it will be tried to download it.", "Mod values needed", MessageBoxButtons.OK, MessageBoxIcon.Information); if (Values.V.loadedModsHash != CreatureCollection.CalculateModListHash(new List <Mod>())) { LoadStatAndKibbleValues(false); // reset values to default } LoadModValueFiles(new List <string> { tmi.Value.mod.FileName }, true, true, out mods); break; } } if (!modFound && MessageBox.Show("The additional-values file in the library you're loading is unknown. You should first get a values-file in the new format for that mod.\n" + "If you're loading the library the conversion of some modded species to the new format may fail and the according creatures have to be imported again later.\n\n" + $"File:\n{filePath}\n" + $"unknown mod-file: {modTag}\n\n" + "Do you want to load the library and risk losing creatures?", "Unknown mod-file", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes) { return(false); } } creatureCollection = oldLibraryFormat.FormatConverter.ConvertXml2Asb(creatureCollectionOld, filePath); creatureCollection.ModList = mods; if (creatureCollection == null) { throw new Exception("Conversion failed"); } string fileNameWOExt = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath)); // check if new fileName is not yet existing filePath = fileNameWOExt + COLLECTION_FILE_EXTENSION; if (File.Exists(filePath)) { int fi = 2; while (File.Exists(fileNameWOExt + "_" + fi + COLLECTION_FILE_EXTENSION)) { fi++; } filePath = fileNameWOExt + "_" + fi + COLLECTION_FILE_EXTENSION; } // save converted library SaveCollectionToFileName(filePath); } } else { CreatureCollection tmpCC; using (StreamReader file = File.OpenText(filePath)) { JsonSerializer serializer = new JsonSerializer(); tmpCC = (CreatureCollection)serializer.Deserialize(file, typeof(CreatureCollection)); } if (!Version.TryParse(tmpCC.FormatVersion, out Version ccVersion) || !Version.TryParse(CreatureCollection.CURRENT_FORMAT_VERSION, out Version currentVersion) || ccVersion > currentVersion) { throw new FormatException("Unhandled format version"); } creatureCollection = tmpCC; } break; } catch (IOException) { // if file is not readable Thread.Sleep(delayOnRetry); } catch (FormatException) { // This FormatVersion is not understood, abort MessageBox.Show($"This library format is unsupported in this version of ARK Smart Breeding." + "\n\nTry updating to a newer version.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return(false); } catch (InvalidOperationException e) { MessageBox.Show($"The library-file\n{filePath}\ncouldn\'t be opened, we thought you should know.\nErrormessage:\n\n{e.Message}" + (e.InnerException == null ? string.Empty : $"\n\nInnerException:\n\n{e.InnerException.Message}"), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return(false); } finally { fileStream?.Close(); } } if (creatureCollection.ModValueReloadNeeded) { // load original multipliers if they were changed if (!LoadStatAndKibbleValues(false).statValuesLoaded) { creatureCollection = new CreatureCollection(); return(false); } } if (creatureCollection.ModValueReloadNeeded && !LoadModValuesOfCollection(creatureCollection, false, false)) { creatureCollection = new CreatureCollection(); return(false); } if (creatureCollection.serverMultipliers == null) { creatureCollection.serverMultipliers = oldMultipliers ?? Values.V.serverMultipliersPresets.GetPreset(ServerMultipliersPresets.OFFICIAL); } if (speciesSelector1.LastSpecies != null && speciesSelector1.LastSpecies.Length > 0) { tamingControl1.SetSpecies(Values.V.SpeciesByBlueprint(speciesSelector1.LastSpecies[0])); } creatureCollection.FormatVersion = CreatureCollection.CURRENT_FORMAT_VERSION; ApplySettingsToValues(); bool creatureWasAdded = false; if (keepCurrentCreatures) { creatureWasAdded = creatureCollection.MergeCreatureList(oldCreatures); } else { currentFileName = filePath; fileSync.ChangeFile(currentFileName); creatureBoxListView.Clear(); } creatureCollection.DeletedCreatureGuids = null; // no longer needed InitializeCollection(); filterListAllowed = false; SetLibraryFilter("Dead", creatureCollection.showFlags.HasFlag(CreatureFlags.Dead)); SetLibraryFilter("Unavailable", creatureCollection.showFlags.HasFlag(CreatureFlags.Unavailable)); SetLibraryFilter("Neutered", creatureCollection.showFlags.HasFlag(CreatureFlags.Neutered)); SetLibraryFilter("Obelisk", creatureCollection.showFlags.HasFlag(CreatureFlags.Obelisk)); SetLibraryFilter("Cryopod", creatureCollection.showFlags.HasFlag(CreatureFlags.Cryopod)); SetLibraryFilter("Mutated", creatureCollection.showFlags.HasFlag(CreatureFlags.Mutated)); checkBoxUseFiltersInTopStatCalculation.Checked = creatureCollection.useFiltersInTopStatCalculation; SetCollectionChanged(creatureWasAdded); // setCollectionChanged only if there really were creatures added from the old library to the just opened one ///// creatures loaded. // calculate creature values RecalculateAllCreaturesValues(); if (!keepCurrentSelections && creatureCollection.creatures.Count > 0) { tabControlMain.SelectedTab = tabPageLibrary; } creatureBoxListView.maxDomLevel = creatureCollection.maxDomLevel; UpdateCreatureListings(); // set global species that was set before loading if (selectedSpecies != null && creatureCollection.creatures.Any(c => c.Species != null && c.Species.Equals(selectedSpecies)) ) { speciesSelector1.SetSpecies(selectedSpecies); } else if (creatureCollection.creatures.Any()) { speciesSelector1.SetSpecies(creatureCollection.creatures[0].Species); } // set library species to what it was before loading if (selectedlibrarySpecies == null || !creatureCollection.creatures.Any(c => c.Species != null && c.Species.Equals(selectedlibrarySpecies)) ) { selectedlibrarySpecies = speciesSelector1.SelectedSpecies; } if (selectedlibrarySpecies != null) { listBoxSpeciesLib.SelectedIndex = listBoxSpeciesLib.Items.IndexOf(selectedlibrarySpecies); } filterListAllowed = true; FilterLib(); // apply last sorting listViewLibrary.Sort(); UpdateTempCreatureDropDown(); Properties.Settings.Default.LastSaveFile = filePath; lastAutoSaveBackup = DateTime.Now.AddMinutes(-10); return(true); }
public bool loadAdditionalValues(string filename, bool showResults) { if (!TryLoadValuesFile(filename, setModFileName: true, out Values modifiedValues)) { return(false); } _V.modValuesFile = Path.GetFileName(filename); int speciesUpdated = 0; int speciesAdded = 0; // update data if existing // version try { _V.modVersion = new Version(modifiedValues.ver); } catch { _V.modVersion = new Version(0, 0); } // species if (modifiedValues.species != null) { foreach (Species sp in modifiedValues.species) { if (string.IsNullOrWhiteSpace(sp.blueprintPath)) { continue; } Species originalSpecies = speciesByBlueprint(sp.blueprintPath); if (originalSpecies == null) { _V.species.Add(sp); sp.Initialize(); sp.mod = modifiedValues.mod; speciesAdded++; } else { // species already exists, update all values which are not null bool updated = false; if (sp.TamedBaseHealthMultiplier != null) { originalSpecies.TamedBaseHealthMultiplier = sp.TamedBaseHealthMultiplier; updated = true; } if (sp.NoImprintingForSpeed != null) { originalSpecies.NoImprintingForSpeed = sp.NoImprintingForSpeed; updated = true; } if (sp.fullStatsRaw != null && sp.fullStatsRaw.Length > 0) { for (int s = 0; s < statsCount && s < sp.fullStatsRaw.Length; s++) { if (sp.fullStatsRaw[s] == null) { continue; } for (int si = 0; si < 5 && si < sp.fullStatsRaw[s].Length; si++) { if (sp.fullStatsRaw[s][si] == null) { continue; } originalSpecies.fullStatsRaw[s][si] = sp.fullStatsRaw[s][si]; updated = true; } } } if (!string.IsNullOrEmpty(sp.blueprintPath)) { originalSpecies.blueprintPath = sp.blueprintPath; updated = true; } if (updated) { speciesUpdated++; } } } // sort new species OrderSpecies(); } // fooddata TODO // default-multiplier TODO _V.loadAliases(); _V.updateSpeciesBlueprints(); if (showResults) { MessageBox.Show($"Species with changed stats: {speciesUpdated}\nSpecies added: {speciesAdded}", "Additional Values succesfully added", MessageBoxButtons.OK, MessageBoxIcon.Information); } return(true); }
/// <summary> /// Loads the given creature collection file. /// </summary> /// <param name="filePath">File that contains the collection</param> /// <param name="keepCurrentCreatures">add the creatures of the loaded file to the current ones</param> /// <param name="keepCurrentSelections">don't change the species selection or tab, use if a synchronized library is loaded</param> /// <returns></returns> private bool LoadCollectionFile(string filePath, bool keepCurrentCreatures = false, bool keepCurrentSelections = false) { Species selectedSpecies = speciesSelector1.SelectedSpecies; Species selectedLibrarySpecies = listBoxSpeciesLib.SelectedItem as Species; if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) { MessageBox.Show($"Save file with name \"{filePath}\" does not exist!", $"File not found - {Utils.ApplicationNameVersion}", MessageBoxButtons.OK, MessageBoxIcon.Error); return(false); } CreatureCollection previouslyLoadedCreatureCollection = _creatureCollection; // Wait until the file is readable const int numberOfRetries = 5; const int delayOnRetry = 1000; FileStream fileStream = null; for (int i = 1; i <= numberOfRetries; ++i) { // sometimes a synchronized file has only 0 bytes, i.e. it's not yet synchronized fully. In this case wait a bit and try again if (Properties.Settings.Default.syncCollection && new FileInfo(filePath).Length == 0) { Thread.Sleep(delayOnRetry); continue; } try { if (Path.GetExtension(filePath).ToLower() == ".xml") { // old format for backwards compatibility using (fileStream = File.OpenRead(filePath)) { // use xml-serializer for old library-format XmlSerializer reader = new XmlSerializer(typeof(oldLibraryFormat.CreatureCollectionOld)); var creatureCollectionOld = (oldLibraryFormat.CreatureCollectionOld)reader.Deserialize(fileStream); List <Mod> mods = null; // first check if additional values are used, and if the according values-file is already available. // if not, abort conversion and first make sure the file is available, e.g. downloaded if (!string.IsNullOrEmpty(creatureCollectionOld.additionalValues)) { // usually the old filename is equal to the mod-tag bool modFound = false; string modTag = Path.GetFileNameWithoutExtension(creatureCollectionOld.additionalValues).Replace(" ", "").ToLower().Replace("gaiamod", "gaia"); foreach (KeyValuePair <string, ModInfo> tmi in Values.V.modsManifest.modsByTag) { if (tmi.Key.ToLower() == modTag) { modFound = true; MessageBox.Show("The library contains creatures of modded species. For a correct file-conversion the correct mod-values file is needed.\n\n" + "If the mod-value file is not loaded, the conversion may assign wrong species to your creatures.\n" + "If the mod-value file is not available locally, it will be tried to download it.", $"Mod values needed - {Utils.ApplicationNameVersion}", MessageBoxButtons.OK, MessageBoxIcon.Information); if (Values.V.loadedModsHash != Values.NoModsHash) { LoadStatAndKibbleValues(false); // reset values to default } LoadModValueFiles(new List <string> { tmi.Value.mod.FileName }, true, true, out mods); break; } } if (!modFound && MessageBox.Show("The additional-values file in the library you're loading is unknown. You should first get a values-file in the new format for that mod.\n" + "If you're loading the library the conversion of some modded species to the new format may fail and the according creatures have to be imported again later.\n\n" + $"File:\n{filePath}\n" + $"unknown mod-file: {modTag}\n\n" + "Do you want to load the library and risk losing creatures?", $"Unknown mod-file - {Utils.ApplicationNameVersion}", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes) { return(false); } } _creatureCollection = oldLibraryFormat.FormatConverter.ConvertXml2Asb(creatureCollectionOld, filePath); _creatureCollection.ModList = mods; if (_creatureCollection == null) { throw new Exception("Conversion failed"); } string fileNameWOExt = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath)); // check if new fileName is not yet existing filePath = fileNameWOExt + CollectionFileExtension; if (File.Exists(filePath)) { int fi = 2; while (File.Exists(fileNameWOExt + "_" + fi + CollectionFileExtension)) { fi++; } filePath = fileNameWOExt + "_" + fi + CollectionFileExtension; } // save converted library SaveCollectionToFileName(filePath); } } else { // new json-format if (FileService.LoadJsonFile(filePath, out CreatureCollection readCollection, out string errorMessage)) { if (!Version.TryParse(readCollection.FormatVersion, out Version ccVersion) || !Version.TryParse(CreatureCollection.CURRENT_FORMAT_VERSION, out Version currentVersion) || ccVersion > currentVersion) { throw new FormatException("Unhandled format version"); } _creatureCollection = readCollection; } else { MessageBox.Show($"Error while trying to read the library-file\n{filePath}\n\n{errorMessage}", $"{Loc.S("error")} - {Utils.ApplicationNameVersion}", MessageBoxButtons.OK, MessageBoxIcon.Error); return(false); } } break; }
private Creature convertGameObject(GameObject creatureObject, int?levelStep) { GameObject statusObject = creatureObject.CharacterStatusComponent(); string imprinterName = creatureObject.GetPropertyValue <string>("ImprinterName"); string owner = string.IsNullOrWhiteSpace(imprinterName) ? creatureObject.GetPropertyValue <string>("TamerString") : imprinterName; int[] wildLevels = Enumerable.Repeat(-1, statsCount).ToArray(); // -1 is unknown int[] tamedLevels = new int[statsCount]; for (int i = 0; i < statsCount; i++) { wildLevels[i] = statusObject.GetPropertyValue <ArkByteValue>("NumberOfLevelUpPointsApplied", i)?.ByteValue ?? 0; } wildLevels[(int)StatNames.Torpidity] = statusObject.GetPropertyValue <int>("BaseCharacterLevel", defaultValue: 1) - 1; // torpor for (int i = 0; i < statsCount; i++) { tamedLevels[i] = statusObject.GetPropertyValue <ArkByteValue>("NumberOfLevelUpPointsAppliedTamed", i)?.ByteValue ?? 0; } Species species = Values.V.speciesByBlueprint(creatureObject.ClassString); // for debugging if (species == null) { species = null; } float ti = statusObject.GetPropertyValue <float>("TamedIneffectivenessModifier", defaultValue: float.NaN); double te = 1f / (1 + (!float.IsNaN(ti) ? ti : creatureObject.GetPropertyValue <float>("TameIneffectivenessModifier"))); Creature creature = new Creature(species, creatureObject.GetPropertyValue <string>("TamedName"), owner, creatureObject.GetPropertyValue <string>("TribeName"), creatureObject.IsFemale() ? Sex.Female : Sex.Male, wildLevels, tamedLevels, te, !string.IsNullOrWhiteSpace(creatureObject.GetPropertyValue <string>("ImprinterName")), statusObject.GetPropertyValue <float>("DinoImprintingQuality"), levelStep ) { imprinterName = creatureObject.GetPropertyValue <string>("ImprinterName"), guid = Utils.ConvertArkIdToGuid(creatureObject.GetDinoId()), ArkId = creatureObject.GetDinoId(), ArkIdImported = true, domesticatedAt = DateTime.Now, // TODO: possible to convert ingame-time to realtime? addedToLibrary = DateTime.Now, mutationsMaternal = creatureObject.GetPropertyValue <int>("RandomMutationsFemale"), mutationsPaternal = creatureObject.GetPropertyValue <int>("RandomMutationsMale") }; // If it's a baby and still growing, work out growingUntil if (creatureObject.GetPropertyValue <bool>("bIsBaby") || !creatureObject.GetPropertyValue <bool>("bIsBaby") && !string.IsNullOrWhiteSpace(imprinterName)) { double maturationTime = species.breeding?.maturationTimeAdjusted ?? 0; float tamedTime = gameTime - (float)creatureObject.GetPropertyValue <double>("TamedAtTime"); if (tamedTime < maturationTime - 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 + TimeSpan.FromSeconds(maturationTime - tamedTime); } } // Ancestor linking is done later after entire collection is formed - here we just set the guids ArkArrayStruct femaleAncestors = creatureObject.GetPropertyValue <IArkArray, ArkArrayStruct>("DinoAncestors"); StructPropertyList femaleAncestor = (StructPropertyList)femaleAncestors?.LastOrDefault(); if (femaleAncestor != null) { creature.motherGuid = Utils.ConvertArkIdToGuid(GameObjectExtensions.CreateDinoId( femaleAncestor.GetPropertyValue <int>("FemaleDinoID1"), femaleAncestor.GetPropertyValue <int>("FemaleDinoID2"))); creature.motherName = femaleAncestor.GetPropertyValue <string>("FemaleName"); creature.isBred = true; } ArkArrayStruct maleAncestors = creatureObject.GetPropertyValue <IArkArray, ArkArrayStruct>("DinoAncestorsMale"); StructPropertyList maleAncestor = (StructPropertyList)maleAncestors?.LastOrDefault(); if (maleAncestor != null) { creature.fatherGuid = Utils.ConvertArkIdToGuid(GameObjectExtensions.CreateDinoId( maleAncestor.GetPropertyValue <int>("MaleDinoID1"), maleAncestor.GetPropertyValue <int>("MaleDinoID2"))); creature.fatherName = maleAncestor.GetPropertyValue <string>("MaleName"); creature.isBred = true; } creature.colors = new int[6]; for (int i = 0; i < 6; i++) { creature.colors[i] = colorModulo(creatureObject.GetPropertyValue <ArkByteValue>("ColorSetIndices", i)?.ByteValue ?? 0); } bool isDead = creatureObject.GetPropertyValue <bool>("bIsDead"); if (isDead) { creature.status = CreatureStatus.Dead; // dead is always dead } if (!isDead && creature.status == CreatureStatus.Dead) { creature.status = CreatureStatus.Unavailable; // if found alive when marked dead, mark as unavailable } creature.recalculateAncestorGenerations(); creature.recalculateCreatureValues(levelStep); return(creature); }
/// <summary> /// Import exported file. Used by a fileWatcher. /// </summary> /// <param name="filePath"></param> private void ImportExportedAddIfPossible(string filePath) { bool alreadyExists = ExtractExportedFileInExtractor(filePath); bool added = false; bool copyNameToClipboard = Properties.Settings.Default.copyNameToClipboardOnImportWhenAutoNameApplied && (Properties.Settings.Default.applyNamePatternOnImportIfEmptyName || (!alreadyExists && Properties.Settings.Default.applyNamePatternOnAutoImportForNewCreatures)); Species species = speciesSelector1.SelectedSpecies; if (_extractor.uniqueResults || (alreadyExists && _extractor.validResults)) { AddCreatureToCollection(true, goToLibraryTab: false); SetMessageLabelText($"Successful {(alreadyExists ? "updated" : "added")} {creatureInfoInputExtractor.CreatureName} ({species.name}) of the exported file\n" + filePath, MessageBoxIcon.Information); added = true; } bool topLevels = false; bool newTopLevels = false; // give feedback in overlay string infoText; Color textColor; const int colorSaturation = 200; if (added) { var sb = new StringBuilder(); sb.AppendLine($"{species.name} \"{creatureInfoInputExtractor.CreatureName}\" {(alreadyExists ? "updated in " : "added to")} the library."); if (copyNameToClipboard) { sb.AppendLine("Name copied to clipboard."); } for (int s = 0; s < values.Values.STATS_COUNT; s++) { int statIndex = values.Values.statsDisplayOrder[s]; if (!species.UsesStat(statIndex)) { continue; } sb.Append($"{Utils.StatName(statIndex, true, species.IsGlowSpecies)}: {_statIOs[statIndex].LevelWild} ({_statIOs[statIndex].BreedingValue})"); if (_statIOs[statIndex].TopLevel == StatIOStatus.NewTopLevel) { sb.Append($" {Loc.S("newTopLevel")}"); newTopLevels = true; } else if (_statIOs[statIndex].TopLevel == StatIOStatus.TopLevel) { sb.Append($" {Loc.S("topLevel")}"); topLevels = true; } sb.AppendLine(); } infoText = sb.ToString(); textColor = Color.FromArgb(colorSaturation, 255, colorSaturation); } else { infoText = $"Creature \"{creatureInfoInputExtractor.CreatureName}\" couldn't be extracted uniquely, manual level selection is necessary."; textColor = Color.FromArgb(255, colorSaturation, colorSaturation); } _overlay?.SetInfoText(infoText, textColor); if (added) { if (Properties.Settings.Default.MoveAutoImportedFileToSubFolder) { string importedPath = Path.Combine(Path.GetDirectoryName(filePath), "imported"); if (!FileService.TryCreateDirectory(importedPath, out string errorMessage)) { MessageBox.Show($"Subfolder\n{importedPath}\ncould not be created.\n{errorMessage}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } FileService.TryMoveFile(filePath, Path.Combine(importedPath, Path.GetFileName(filePath))); } else if (Properties.Settings.Default.DeleteAutoImportedFile) { FileService.TryDeleteFile(filePath); } } else if (copyNameToClipboard) { // extraction failed, user might expect the name of the new creature in the clipboard Clipboard.SetText("Automatic extraction was not possible"); } if (Properties.Settings.Default.PlaySoundOnAutoImport) { if (added) { if (newTopLevels) { Utils.BeepSignal(3); } else if (topLevels) { Utils.BeepSignal(2); } else { Utils.BeepSignal(1); } } else { Utils.BeepSignal(0); } } }
/// <summary> /// Returns a bitmap image that represents the given colors. If a species color file is available, that is used, else a pic-chart like representation. /// </summary> /// <param name="colorIds"></param> /// <param name="species"></param> /// <param name="enabledColorRegions"></param> /// <param name="size"></param> /// <param name="pieSize"></param> /// <param name="onlyColors">Only return a pie-chart like color representation.</param> /// <param name="onlyImage">Only return an image of the colored creature. If that's not possible, return null.</param> /// <param name="creatureSex">If given, it's tried for find a sex-specific image.</param> /// <returns></returns> public static Bitmap GetColoredCreature(int[] colorIds, Species species, bool[] enabledColorRegions, int size = 128, int pieSize = 64, bool onlyColors = false, bool onlyImage = false, Sex creatureSex = Sex.Unknown) { if (colorIds == null) { colorIds = new int[Species.ColorRegionCount]; } string speciesName = SpeciesImageName(species?.name); // check if there are sex specific images if (creatureSex != Sex.Unknown) { string speciesNameWithSex; switch (creatureSex) { case Sex.Female: speciesNameWithSex = speciesName + "F"; if (File.Exists(Path.Combine(ImgFolder, speciesNameWithSex + Extension))) { speciesName = speciesNameWithSex; } break; case Sex.Male: speciesNameWithSex = speciesName + "M"; if (File.Exists(Path.Combine(ImgFolder, speciesNameWithSex + Extension))) { speciesName = speciesNameWithSex; } break; } } string speciesBackgroundFilePath = Path.Combine(ImgFolder, speciesName + Extension); string speciesColorMaskFilePath = Path.Combine(ImgFolder, speciesName + "_m" + Extension); string cacheFilePath = ColoredCreatureCacheFilePath(speciesName, colorIds); bool cacheFileExists = File.Exists(cacheFilePath); if (!onlyColors && !cacheFileExists) { cacheFileExists = CreateAndSaveCacheSpeciesFile(colorIds, enabledColorRegions, speciesBackgroundFilePath, speciesColorMaskFilePath, cacheFilePath); } if (onlyImage && !cacheFileExists) { return(null); // creating the species file failed } if (cacheFileExists && size == TemplateSize) { try { return(new Bitmap(cacheFilePath)); } catch { // cached file corrupted, recreate if (CreateAndSaveCacheSpeciesFile(colorIds, enabledColorRegions, speciesBackgroundFilePath, speciesColorMaskFilePath, cacheFilePath)) { try { return(new Bitmap(cacheFilePath)); } catch { // file is still invalid after recreation, ignore file } } } return(null); } Bitmap bm = new Bitmap(size, size); using (Graphics graph = Graphics.FromImage(bm)) { graph.SmoothingMode = SmoothingMode.AntiAlias; if (cacheFileExists) { graph.CompositingMode = CompositingMode.SourceCopy; graph.CompositingQuality = CompositingQuality.HighQuality; graph.InterpolationMode = InterpolationMode.HighQualityBicubic; graph.SmoothingMode = SmoothingMode.HighQuality; graph.PixelOffsetMode = PixelOffsetMode.HighQuality; try { using (var cachedImgBmp = new Bitmap(cacheFilePath)) graph.DrawImage(cachedImgBmp, 0, 0, size, size); } catch { // cached file invalid, recreate if (CreateAndSaveCacheSpeciesFile(colorIds, enabledColorRegions, speciesBackgroundFilePath, speciesColorMaskFilePath, cacheFilePath)) { try { using (var cachedImgBmp = new Bitmap(cacheFilePath)) graph.DrawImage(cachedImgBmp, 0, 0, size, size); } catch { // file is still invalid after recreation, ignore file bm.Dispose(); return(null); } } } } else { // draw pieChart int pieAngle = enabledColorRegions?.Count(c => c) ?? Species.ColorRegionCount; pieAngle = 360 / (pieAngle > 0 ? pieAngle : 1); int pieNr = 0; for (int c = 0; c < Species.ColorRegionCount; c++) { if (enabledColorRegions?[c] ?? true) { if (colorIds[c] > 0) { using (var b = new SolidBrush(CreatureColors.CreatureColor(colorIds[c]))) { graph.FillPie(b, (size - pieSize) / 2, (size - pieSize) / 2, pieSize, pieSize, pieNr * pieAngle + 270, pieAngle); } } pieNr++; } } using (var pen = new Pen(Color.Gray)) graph.DrawEllipse(pen, (size - pieSize) / 2, (size - pieSize) / 2, pieSize, pieSize); } } return(bm); }
public void displayData(Species species) { if (species?.breeding == null) { return; } string breedingInfo = ""; string firstTime = "Gestation"; if (species.breeding.gestationTimeAdjusted <= 0) { firstTime = "Incubation"; } string[] rowNames = { firstTime, "Baby", "Maturation" }; for (int k = 0; k < 3; k++) { int t1, totalTime = 0; switch (k) { default: case 0: t1 = (int)(species.breeding.gestationTimeAdjusted == 0 ? species.breeding.incubationTimeAdjusted : species.breeding.gestationTimeAdjusted); totalTime = t1; break; case 1: t1 = (int)(.1f * species.breeding.maturationTimeAdjusted); totalTime = t1; break; case 2: t1 = (int)species.breeding.maturationTimeAdjusted; totalTime = (int)(species.breeding.gestationTimeAdjusted + species.breeding.incubationTimeAdjusted + species.breeding.maturationTimeAdjusted); break; } string[] subitems = { rowNames[k], new TimeSpan(0, 0, t1).ToString("d':'hh':'mm':'ss"), new TimeSpan(0, 0, totalTime).ToString("d':'hh':'mm':'ss"), DateTime.Now.AddSeconds(totalTime).ToShortTimeString() + ", " + DateTime.Now.AddSeconds(totalTime).ToShortDateString() }; //listView1.Items.Add(new ListViewItem(subitems)); breedingInfo += string.Join(", ", subitems) + "\n"; } breedingInfo += "\n"; buttonHatching.Text = firstTime; // further info if (species.breeding.eggTempMin > 0) { breedingInfo += raising.Raising.eggTemperature(species); } if (species.breeding.eggTempMin > 0 && species.breeding.matingCooldownMinAdjusted > 0) { breedingInfo += "\n\n"; } if (species.breeding.matingCooldownMinAdjusted > 0) { breedingInfo += "Time until next mating is possible:\n" + new TimeSpan(0, 0, (int)species.breeding.matingCooldownMinAdjusted).ToString("d':'hh':'mm") + " - " + new TimeSpan(0, 0, (int)species.breeding.matingCooldownMaxAdjusted).ToString("d':'hh':'mm"); } labelBreedingInfos.Text = breedingInfo; }
/// <summary> /// Checks if an according species image exists in the cache folder, if not it tries to creates one. Returns false if there's no image. /// </summary> internal static (bool imageExists, string imagePath, string speciesListName) SpeciesImageExists(Species species, int[] colorIds) { string speciesImageName = SpeciesImageName(species?.name); string speciesNameForList = SpeciesImageName(species?.name, false); string cacheFileName = ColoredCreatureCacheFilePath(speciesImageName, colorIds, true); if (File.Exists(cacheFileName)) { return(true, cacheFileName, speciesNameForList); } string speciesBackgroundFilePath = Path.Combine(ImgFolder, speciesImageName + Extension); string speciesColorMaskFilePath = Path.Combine(ImgFolder, speciesImageName + "_m" + Extension); if (CreateAndSaveCacheSpeciesFile(colorIds, species?.EnabledColorRegions, speciesBackgroundFilePath, speciesColorMaskFilePath, cacheFileName, 64)) { return(true, cacheFileName, speciesNameForList); } return(false, null, null); }
public void SetSpecies(Species species, bool forceRefresh = false) { if (species == null || (_selectedSpecies == species && !forceRefresh)) { return; } _selectedSpecies = species; if (species.taming == null) { NoTamingData(); return; } this.SuspendDrawing(); SuspendLayout(); string speciesName = species.name; linkLabelWikiPage.Text = "Wiki: " + speciesName; linkLabelWikiPage.Tag = speciesName; _tt.SetToolTip(linkLabelWikiPage, ArkWiki.WikiUrl(speciesName)); // bone damage adjusters _boneDamageAdjustersImmobilization = Taming.BoneDamageAdjustersImmobilization(_selectedSpecies, out Dictionary <string, double> boneDamageAdjusters); int ib = 0; foreach (KeyValuePair <string, double> bd in boneDamageAdjusters) { ib++; if (ib >= _rbBoneDamageAdjusters.Count) { RadioButton rbBD = new RadioButton(); flcBodyDamageMultipliers.Controls.Add(rbBD); flcBodyDamageMultipliers.SetFlowBreak(rbBD, true); rbBD.AutoSize = true; _rbBoneDamageAdjusters.Add(rbBD); _rbBoneDamageAdjusterValues.Add(1); rbBD.CheckedChanged += rbBoneDamage_CheckedChanged; } _rbBoneDamageAdjusterValues[ib] = bd.Value; _rbBoneDamageAdjusters[ib].Text = $"{Loc.S(bd.Key)} (× {bd.Value})"; _rbBoneDamageAdjusters[ib].Visible = true; } for (int j = ib + 1; j < _rbBoneDamageAdjusters.Count; j++) { _rbBoneDamageAdjusters[j].Visible = false; } _rbBoneDamageAdjusters[0].Checked = true; // bone damage adjusters adjusted _updateCalculation = false; TamingData td = species.taming; _kibbleRecipe = string.Empty; // list all recipes of kibbles that give a reasonable affinity (assuming that is larger or equal than 100) foreach (var k in Kibbles.K.kibble) { var kibbleName = $"{k.Key} Kibble"; var kibbleFood = Values.V.GetTamingFood(species, kibbleName); if (kibbleFood != null && kibbleFood.affinity >= 100) { _kibbleRecipe += $"\n\n{k.Key} Kibble:{k.Value.RecipeAsText()}"; } } _foodDepletion = td.foodConsumptionBase * td.foodConsumptionMult * _tamingFoodRateMultiplier; SetTamingFoodControls(species); _updateCalculation = true; UpdateFirstFeedingWaiting(); UpdateTamingData(); if (Properties.Settings.Default.TamingFoodOrderByTime) { SetOrderOfTamingFood(true, true); } ResumeLayout(); this.ResumeDrawing(); }
/// <summary> /// Sets the taming food controls. /// </summary> private void SetTamingFoodControls(Species species) { var td = species.taming; int i = 0; if (td.eats != null) { var length = td.eats.Length; for (; i < length; i++) { string f = td.eats[i]; TamingFoodControl tf; // if Augmented are not wanted, and food control already exist, update it and hide it. if (!checkBoxAugmented.Checked && f.Contains("Augmented")) { if (i < _foodControls.Count) { tf = _foodControls[i]; tf.FoodName = f; tf.Hide(); } continue; } if (i >= _foodControls.Count) { tf = new TamingFoodControl(f); tf.valueChanged += UpdateTamingData; tf.Clicked += OnlyOneFood; _foodControls.Add(tf); flpTamingFood.Controls.Add(tf); } else { tf = _foodControls[i]; tf.FoodName = f; flpTamingFood.Controls.SetChildIndex(tf, i); tf.Show(); } // special cases where a creature eats multiple food items of one kind at once var food = Values.V.GetTamingFood(species, f); if (food != null && food.quantity > 1) { tf.foodNameDisplay = food.quantity + "× " + tf.foodNameDisplay; } } } for (int fci = _foodControls.Count - 1; fci >= i; fci--) { _foodControls[fci].FoodName = null; _foodControls[fci].Hide(); } if (i > 0) { _foodControls[0].amount = Taming.FoodAmountNeeded(species, (int)nudLevel.Value, _tamingSpeedMultiplier, _foodControls[0].FoodName, td.nonViolent); } }
public static void TamingTimes(Species species, int level, double tamingSpeedMultiplier, double tamingFoodRateMultiplier, List <string> usedFood, List <int> foodAmount, out List <int> foodAmountUsed, out TimeSpan duration, out int neededNarcoberries, out int neededAscerbicMushrooms, out int neededNarcotics, out int neededBioToxines, out double te, out double hunger, out int bonusLevel, out bool enoughFood) { double totalTorpor = 0, torporDeplPS = 0, torporNeeded = 0; int totalSeconds = 0; bonusLevel = 0; te = 1; duration = TimeSpan.Zero; neededNarcoberries = 0; neededAscerbicMushrooms = 0; neededNarcotics = 0; neededBioToxines = 0; hunger = 0; enoughFood = false; foodAmountUsed = new List <int>(); foreach (int i in foodAmount) { foodAmountUsed.Add(0); } if (species != null && species.taming != null) { double affinityNeeded = species.taming.affinityNeeded0 + species.taming.affinityIncreasePL * level; // test if creature is tamend non-violently, then use wakeTame multiplicators if (!species.taming.nonViolent) { //total torpor for level totalTorpor = species.stats[(int)StatNames.Torpidity].BaseValue * (1 + species.stats[(int)StatNames.Torpidity].IncPerWildLevel * (level - 1)); // torpor depletion per second for level torporDeplPS = TorporDepletionPS(species.taming.torporDepletionPS0, level); } double foodByAffinity = 0; // needed for the effectiveness calculation // how much food / resources of the different kinds that this creature eats is needed for (int f = 0; f < usedFood.Count; f++) { if (foodAmount[f] > 0) { string food = usedFood[f]; bool specialFood = species.taming.specialFoodValues != null && species.taming.specialFoodValues.ContainsKey(food); if (specialFood || Values.V.defaultFoodData.ContainsKey(food)) { double foodAffinity; double foodValue; // check if (creature handles this food in a special way (e.g. scorpions not liking raw meat as much) if (specialFood) { foodAffinity = species.taming.specialFoodValues[food].affinity; foodValue = species.taming.specialFoodValues[food].foodValue; } else { foodAffinity = Values.V.defaultFoodData[food].affinity; foodValue = Values.V.defaultFoodData[food].foodValue; } foodAffinity *= specialFood ? species.taming.specialFoodValues[food].quantity : 1; if (species.taming.nonViolent) { // consider wake taming multiplicators (non - violent taming) foodAffinity *= species.taming.wakeAffinityMult; foodValue = foodValue * species.taming.wakeFoodDeplMult; } // *2 in accordance with the hardcoded 2x taming-bonus that was introduced in the game in patch 253.0 on 2016-12-23 // https://ark.gamepedia.com/253.0 foodAffinity *= tamingSpeedMultiplier * 2; if (foodAffinity > 0 && foodValue > 0) { // amount of food needed for the left affinity. int foodPiecesNeeded = (int)Math.Ceiling(affinityNeeded / foodAffinity); if (foodPiecesNeeded > foodAmount[f]) { foodPiecesNeeded = foodAmount[f]; } foodAmountUsed[f] = foodPiecesNeeded; // time to eat needed food // mantis eats every 3 minutes, regardless of level int seconds; if (species.name == "Mantis") { seconds = foodPiecesNeeded * 180; } else { seconds = (int)Math.Ceiling(foodPiecesNeeded * foodValue / (species.taming.foodConsumptionBase * species.taming.foodConsumptionMult * tamingFoodRateMultiplier)); } affinityNeeded -= foodPiecesNeeded * foodAffinity; // new approach with 1/(1 + IM*IA*N/AO + ID*D) from https://forums.unrealengine.com/development-discussion/modding/ark-survival-evolved/56959-tutorial-dinosaur-taming-parameters?85457-Tutorial-Dinosaur-Taming-Parameters= foodByAffinity += foodPiecesNeeded / foodAffinity; if (!species.taming.nonViolent) { //extra needed torpor to eat needed food torporNeeded += torporDeplPS * seconds; } totalSeconds += seconds; } if (affinityNeeded <= 0) { break; } } } } // add tamingIneffectivenessMultiplier? Needs settings? te = 1 / (1 + species.taming.tamingIneffectiveness * foodByAffinity); // ignores damage, which has no input if (te < 0) { te = 0; } torporNeeded -= totalTorpor; if (torporNeeded < 0) { torporNeeded = 0; } // amount of Narcoberries(give 7.5 torpor each over 3s) neededNarcoberries = (int)Math.Ceiling(torporNeeded / (7.5 + 3 * torporDeplPS)); // amount of Ascerbic Mushrooms (give 25 torpor each over 3s) neededAscerbicMushrooms = (int)Math.Ceiling(torporNeeded / (25 + 3 * torporDeplPS)); // amount of Narcotics(give 40 each over 8s) neededNarcotics = (int)Math.Ceiling(torporNeeded / (40 + 8 * torporDeplPS)); // amount of BioToxines (give 80 each over 16s) neededBioToxines = (int)Math.Ceiling(torporNeeded / (80 + 16 * torporDeplPS)); enoughFood = affinityNeeded <= 0; // needed Time to eat duration = new TimeSpan(0, 0, totalSeconds); bonusLevel = (int)Math.Floor(level * te / 2); for (int i = 0; i < usedFood.Count; i++) { if (species.taming.specialFoodValues != null && species.taming.specialFoodValues.ContainsKey(usedFood[i])) { hunger += foodAmountUsed[i] * species.taming.specialFoodValues[usedFood[i]].foodValue; } else if (Values.V.defaultFoodData.ContainsKey(usedFood[i])) { hunger += foodAmountUsed[i] * Values.V.defaultFoodData[usedFood[i]].foodValue; } } } }
public void determineBestBreeding(Creature chosenCreature = null, bool forceUpdate = false, Species setSpecies = null) { if (creatureCollection == null) { return; } Species selectedSpecies = (chosenCreature != null ? chosenCreature.Species : null); bool newSpecies = false; if (selectedSpecies == null) { selectedSpecies = setSpecies != null ? setSpecies : currentSpecies; } if (selectedSpecies != null && currentSpecies != selectedSpecies) { CurrentSpecies = selectedSpecies; newSpecies = true; EnabledColorRegions = currentSpecies?.colors.Select(n => n.name != "").ToArray() ?? new bool[6] { true, true, true, true, true, true }; breedingPlanNeedsUpdate = true; } if (forceUpdate || breedingPlanNeedsUpdate) { Creatures = creatureCollection.creatures .Where(c => c.speciesBlueprint == currentSpecies.blueprintPath && c.status == CreatureStatus.Available && !c.neutered && (cnBPIncludeCooldowneds.Checked || c.cooldownUntil < DateTime.Now && c.growingUntil < DateTime.Now)) .ToList(); } statWeights = statWeighting1.Weightings; this.chosenCreature = chosenCreature; calculateBreedingScoresAndDisplayPairs(breedingMode, newSpecies); breedingPlanNeedsUpdate = false; }
public void DetermineBestBreeding(Creature chosenCreature = null, bool forceUpdate = false, Species setSpecies = null) { if (creatureCollection == null) { return; } Species selectedSpecies = chosenCreature?.Species; bool newSpecies = false; if (selectedSpecies == null) { selectedSpecies = setSpecies ?? currentSpecies; } if (selectedSpecies != null && currentSpecies != selectedSpecies) { CurrentSpecies = selectedSpecies; newSpecies = true; EnabledColorRegions = currentSpecies?.colors.Select(n => !string.IsNullOrEmpty(n?.name)).ToArray() ?? new bool[6] { true, true, true, true, true, true }; breedingPlanNeedsUpdate = true; } if (forceUpdate || breedingPlanNeedsUpdate) { Creatures = creatureCollection.creatures .Where(c => c.speciesBlueprint == currentSpecies.blueprintPath && c.status == CreatureStatus.Available && !c.flags.HasFlag(CreatureFlags.Neutered) && (cbBPIncludeCooldowneds.Checked || !(c.cooldownUntil > DateTime.Now || c.growingUntil > DateTime.Now ) ) ) .ToList(); } statWeights = statWeighting1.Weightings; this.chosenCreature = chosenCreature; CalculateBreedingScoresAndDisplayPairs(breedingMode, newSpecies); breedingPlanNeedsUpdate = false; }
public bool loadAdditionalValues(string filename, bool showResults) { // load extra values-file that can add values or modify existing ones bool loadedSuccessful = true; // check if file exists if (!File.Exists(filename)) { MessageBox.Show("Additional Values-File '" + filename + "' not found.\nThis collection seems to have modified or added values that are saved in a separate file, which couldn't be found at the saved location. You can load it manually via the menu File - Load additional values…", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return(false); } DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Values)); System.IO.FileStream file = System.IO.File.OpenRead(filename); Values modifiedValues = new Values(); try { modifiedValues = (Values)ser.ReadObject(file); } catch (Exception e) { MessageBox.Show("File Couldn't be opened or read.\nErrormessage:\n\n" + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); loadedSuccessful = false; } file.Close(); if (!loadedSuccessful) { return(false); } _V.modValuesFile = Path.GetFileName(file.Name); int speciesUpdated = 0; int speciesAdded = 0; // update data if existing // version try { _V.modVersion = new Version(modifiedValues.ver); } catch { _V.modVersion = new Version(0, 0); } // species if (modifiedValues.species != null) { foreach (Species sp in modifiedValues.species) { if (!_V.speciesNames.Contains(sp.name)) { _V.species.Add(sp); sp.initialize(); _V.speciesNames.Add(sp.name); speciesAdded++; } else { // species already exists, update all values which are not null Species originalSpecies = _V.species[_V.speciesNames.IndexOf(sp.name)]; bool updated = false; if (sp.TamedBaseHealthMultiplier != null) { originalSpecies.TamedBaseHealthMultiplier = sp.TamedBaseHealthMultiplier; updated = true; } if (sp.NoImprintingForSpeed != null) { originalSpecies.NoImprintingForSpeed = sp.NoImprintingForSpeed; updated = true; } if (sp.statsRaw != null && sp.statsRaw.Length > 0) { for (int s = 0; s < 8 && s < sp.statsRaw.Length; s++) { if (sp.statsRaw[s] != null) { for (int si = 0; si < 5 && si < sp.statsRaw[s].Length; si++) { if (sp.statsRaw[s][si] != null) { originalSpecies.statsRaw[s][si] = sp.statsRaw[s][si]; updated = true; } } } } } if (string.IsNullOrEmpty(sp.blueprintPath)) { originalSpecies.blueprintPath = sp.blueprintPath; updated = true; } if (updated) { speciesUpdated++; } } } } // fooddata TODO // default-multiplier TODO _V.loadAliases(); _V.updateSpeciesBlueprints(); if (showResults) { MessageBox.Show("Species with changed stats: " + speciesUpdated + "\nSpecies added: " + speciesAdded, "Additional Values succesfully added", MessageBoxButtons.OK, MessageBoxIcon.Information); } return(true); }
public bool LoadModValues(List <string> modValueFiles, bool showResults, out List <Mod> mods) { loadedModsHash = 0; List <Values> modifiedValues = new List <Values>(); mods = new List <Mod>(); if (modValueFiles == null) { return(false); } foreach (string mf in modValueFiles) { string filename = FileService.GetJsonPath(Path.Combine("mods", mf)); if (TryLoadValuesFile(filename, setModFileName: true, out Values modValues)) { modifiedValues.Add(modValues); } } int speciesUpdated = 0; int speciesAdded = 0; // update data if existing foreach (Values modValues in modifiedValues) { // if mods are loaded multiple times, only keep the last mods.Remove(modValues.mod); mods.Add(modValues.mod); // species if (modValues.species != null) { foreach (Species sp in modValues.species) { if (string.IsNullOrWhiteSpace(sp.blueprintPath)) { continue; } Species originalSpecies = speciesByBlueprint(sp.blueprintPath); if (originalSpecies == null) { _V.species.Add(sp); sp.Initialize(); sp.mod = modValues.mod; speciesAdded++; if (!blueprintToSpecies.ContainsKey(sp.blueprintPath)) { blueprintToSpecies.Add(sp.blueprintPath, sp); } } else { // species already exists, update all values which are not null bool updated = false; if (sp.TamedBaseHealthMultiplier != null) { originalSpecies.TamedBaseHealthMultiplier = sp.TamedBaseHealthMultiplier; updated = true; } if (sp.NoImprintingForSpeed != null) { originalSpecies.NoImprintingForSpeed = sp.NoImprintingForSpeed; updated = true; } if (sp.fullStatsRaw != null && sp.fullStatsRaw.Length > 0) { for (int s = 0; s < statsCount && s < sp.fullStatsRaw.Length; s++) { if (sp.fullStatsRaw[s] == null) { continue; } for (int si = 0; si < 5 && si < sp.fullStatsRaw[s].Length; si++) { if (sp.fullStatsRaw[s][si] == null) { continue; } originalSpecies.fullStatsRaw[s][si] = sp.fullStatsRaw[s][si]; updated = true; } } } if (!string.IsNullOrEmpty(sp.blueprintPath)) { originalSpecies.blueprintPath = sp.blueprintPath; updated = true; } if (updated) { speciesUpdated++; } } } } } loadedModsHash = CreatureCollection.CalculateModListId(mods); // sort new species OrderSpecies(); // fooddata TODO // default-multiplier TODO _V.loadAliases(); _V.updateSpeciesBlueprints(); if (showResults) { MessageBox.Show($"Species with changed stats: {speciesUpdated}\nSpecies added: {speciesAdded}", "Additional Values succesfully added", MessageBoxButtons.OK, MessageBoxIcon.Information); } return(true); }
public static Bitmap getColoredCreature(int[] colorIds, Species species, bool[] enabledColorRegions, int size = 128, int pieSize = 64, bool onlyColors = false, bool dontCache = false) { //float[][] hsl = new float[6][]; int[][] rgb = new int[6][]; for (int c = 0; c < 6; c++) { Color cl = CreatureColors.creatureColor(colorIds[c]); rgb[c] = new int[] { cl.R, cl.G, cl.B }; } Bitmap bm = new Bitmap(size, size); using (Graphics graph = Graphics.FromImage(bm)) { graph.SmoothingMode = SmoothingMode.AntiAlias; string imgFolder = Path.Combine(FileService.GetPath(), imageFolderName); string cacheFolder = Path.Combine(FileService.GetPath(), imageFolderName, cacheFolderName); string speciesName = species?.name ?? string.Empty; string cacheFileName = Path.Combine(cacheFolder, speciesName.Substring(0, Math.Min(speciesName.Length, 5)) + "_" + (speciesName + string.Join("", colorIds.Select(i => i.ToString()).ToArray())).GetHashCode().ToString("X8") + extension); if (!onlyColors && File.Exists(Path.Combine(imgFolder, speciesName + extension)) && File.Exists(Path.Combine(imgFolder, speciesName + "_m" + extension))) { if (!File.Exists(cacheFileName)) { const int defaultSizeOfTemplates = 256; Bitmap bmC = new Bitmap(Path.Combine(imgFolder, speciesName + extension)); graph.DrawImage(new Bitmap(Path.Combine(imgFolder, speciesName + extension)), 0, 0, defaultSizeOfTemplates, defaultSizeOfTemplates); Bitmap mask = new Bitmap(defaultSizeOfTemplates, defaultSizeOfTemplates); Graphics.FromImage(mask).DrawImage(new Bitmap(Path.Combine(imgFolder, speciesName + "_m" + extension)), 0, 0, defaultSizeOfTemplates, defaultSizeOfTemplates); float o = 0; bool imageFine = false; try { for (int i = 0; i < bmC.Width; i++) { for (int j = 0; j < bmC.Height; j++) { Color bc = bmC.GetPixel(i, j); if (bc.A > 0) { int r = mask.GetPixel(i, j).R; int g = mask.GetPixel(i, j).G; int b = mask.GetPixel(i, j).B; for (int m = 0; m < 6; m++) { if (!enabledColorRegions[m] || colorIds[m] == 0) { continue; } switch (m) { case 0: o = Math.Max(0, r - g - b) / 255f; break; case 1: o = Math.Max(0, g - r - b) / 255f; break; case 2: o = Math.Max(0, b - r - g) / 255f; break; case 3: o = Math.Min(g, b) / 255f; break; case 4: o = Math.Min(r, g) / 255f; break; case 5: o = Math.Min(r, b) / 255f; break; } if (o == 0) { continue; } // using "grain merge", e.g. see https://docs.gimp.org/en/gimp-concepts-layer-modes.html int rMix = bc.R + rgb[m][0] - 128; if (rMix < 0) { rMix = 0; } else if (rMix > 255) { rMix = 255; } int gMix = bc.G + rgb[m][1] - 128; if (gMix < 0) { gMix = 0; } else if (gMix > 255) { gMix = 255; } int bMix = bc.B + rgb[m][2] - 128; if (bMix < 0) { bMix = 0; } else if (bMix > 255) { bMix = 255; } Color c = Color.FromArgb(rMix, gMix, bMix); bc = Color.FromArgb(bc.A, (int)(o * c.R + (1 - o) * bc.R), (int)(o * c.G + (1 - o) * bc.G), (int)(o * c.B + (1 - o) * bc.B)); } bmC.SetPixel(i, j, bc); } } } imageFine = true; } catch { // error during drawing, maybe mask is smaller than image } if (imageFine) { if (!Directory.Exists(cacheFolder)) { Directory.CreateDirectory(cacheFolder); } bmC.Save(cacheFileName); // safe in cache} } } } if (File.Exists(cacheFileName)) { graph.CompositingMode = CompositingMode.SourceCopy; graph.CompositingQuality = CompositingQuality.HighQuality; graph.InterpolationMode = InterpolationMode.HighQualityBicubic; graph.SmoothingMode = SmoothingMode.HighQuality; graph.PixelOffsetMode = PixelOffsetMode.HighQuality; graph.DrawImage(new Bitmap(cacheFileName), 0, 0, size, size); } else { // draw piechart int pieAngle = enabledColorRegions.Count(c => c); pieAngle = 360 / (pieAngle > 0 ? pieAngle : 1); int pieNr = 0; for (int c = 0; c < 6; c++) { if (enabledColorRegions[c]) { if (colorIds[c] > 0) { using (var b = new SolidBrush(CreatureColors.creatureColor(colorIds[c]))) { graph.FillPie(b, (size - pieSize) / 2, (size - pieSize) / 2, pieSize, pieSize, pieNr * pieAngle + 270, pieAngle); } } pieNr++; } } graph.DrawEllipse(new Pen(Color.Gray), (size - pieSize) / 2, (size - pieSize) / 2, pieSize, pieSize); } } return(bm); }
public void SetSpecies(Species species) { if (species == null || selectedSpecies == species) { return; } selectedSpecies = species; if (species.taming == null) { NoTamingData(); return; } SuspendLayout(); string speciesName = species.name; linkLabelWikiPage.Text = "Wiki: " + speciesName; linkLabelWikiPage.Tag = speciesName; // bone damage adjusters boneDamageAdjustersImmobilization = Taming.BoneDamageAdjustersImmobilization(selectedSpecies, out Dictionary <string, double> boneDamageAdjusters); int ib = 0; foreach (KeyValuePair <string, double> bd in boneDamageAdjusters) { ib++; if (ib >= rbBoneDamageAdjusters.Count) { RadioButton rbBD = new RadioButton(); flcBodyDamageMultipliers.Controls.Add(rbBD); flcBodyDamageMultipliers.SetFlowBreak(rbBD, true); rbBD.AutoSize = true; rbBoneDamageAdjusters.Add(rbBD); rbBoneDamageAdjusterValues.Add(1); rbBD.CheckedChanged += rbBoneDamage_CheckedChanged; } rbBoneDamageAdjusterValues[ib] = bd.Value; rbBoneDamageAdjusters[ib].Text = $"{Loc.s(bd.Key)} (× {bd.Value})"; rbBoneDamageAdjusters[ib].Visible = true; } for (int j = ib + 1; j < rbBoneDamageAdjusters.Count; j++) { rbBoneDamageAdjusters[j].Visible = false; } rbBoneDamageAdjusters[0].Checked = true; // bone damage adjusters adjusted updateCalculation = false; TamingData td = species.taming; kibbleRecipe = ""; // TODO replace with new kibble recipes //if (td.favoriteKibble != null && Kibbles.K.kibble.ContainsKey(td.favoriteKibble)) //{ // kibbleRecipe = "\n\nKibble:" + Kibbles.K.kibble[td.favoriteKibble].RecipeAsText(); //} foodDepletion = td.foodConsumptionBase * td.foodConsumptionMult * tamingFoodRateMultiplier; int i = 0; if (td.eats != null) { for (i = 0; i < td.eats.Count; i++) { string f = td.eats[i]; TamingFoodControl tf; if (i >= foodControls.Count) { tf = new TamingFoodControl(f); tf.valueChanged += UpdateTamingData; tf.Clicked += OnlyOneFood; foodControls.Add(tf); flpTamingFood.Controls.Add(tf); } else { tf = foodControls[i]; tf.FoodName = f; tf.Show(); } if (f == "Kibble") { tf.foodNameDisplay = $"Kibble ({td.favoriteKibble} {Loc.s("Egg")})"; } if (td.specialFoodValues != null && td.specialFoodValues.ContainsKey(f) && td.specialFoodValues[f].quantity > 1) { tf.foodNameDisplay = td.specialFoodValues[f].quantity + "× " + tf.foodNameDisplay; } } } for (int fci = foodControls.Count - 1; fci >= i; fci--) { foodControls[fci].Hide(); } if (i > 0) { foodControls[0].amount = Taming.FoodAmountNeeded(species, (int)nudLevel.Value, tamingSpeedMultiplier, foodControls[0].FoodName, td.nonViolent); } updateCalculation = true; UpdateFirstFeedingWaiting(); UpdateTamingData(); ResumeLayout(); }
/// <summary> /// Returns a bitmap image that represents the given colors. If a species color file is available, that is used, else a pic-chart like representation. /// </summary> /// <param name="colorIds"></param> /// <param name="species"></param> /// <param name="enabledColorRegions"></param> /// <param name="size"></param> /// <param name="pieSize"></param> /// <param name="onlyColors">Only return a pie-chart like color representation.</param> /// <param name="onlyImage">Only return an image of the colored creature. If that's not possible, return null.</param> /// <returns></returns> public static Bitmap GetColoredCreature(int[] colorIds, Species species, bool[] enabledColorRegions, int size = 128, int pieSize = 64, bool onlyColors = false, bool onlyImage = false, Library.Sex creatureSex = Sex.Unknown) { if (colorIds == null) { return(null); } //float[][] hsl = new float[Species.ColorRegionCount][]; int[][] rgb = new int[Species.ColorRegionCount][]; for (int c = 0; c < Species.ColorRegionCount; c++) { Color cl = CreatureColors.CreatureColor(colorIds[c]); rgb[c] = new int[] { cl.R, cl.G, cl.B }; } string imgFolder = Path.Combine(FileService.GetPath(), imageFolderName); string cacheFolder = Path.Combine(FileService.GetPath(), imageFolderName, cacheFolderName); string speciesName = species?.name ?? string.Empty; // check if there are sex specific images if (creatureSex != Sex.Unknown) { string speciesNameWithSex = null; switch (creatureSex) { case Sex.Female: speciesNameWithSex = speciesName + "F"; if (File.Exists(Path.Combine(imgFolder, speciesNameWithSex + extension))) { speciesName = speciesNameWithSex; } break; case Sex.Male: speciesNameWithSex = speciesName + "M"; if (File.Exists(Path.Combine(imgFolder, speciesNameWithSex + extension))) { speciesName = speciesNameWithSex; } break; } } string speciesBackgroundFilePath = Path.Combine(imgFolder, speciesName + extension); string cacheFileName = Path.Combine(cacheFolder, speciesName.Substring(0, Math.Min(speciesName.Length, 5)) + "_" + (speciesName + string.Join(".", colorIds.Select(i => i.ToString()))).GetHashCode().ToString("X8") + extension); string speciesColorMaskFilePath = Path.Combine(imgFolder, speciesName + "_m" + extension); if (!onlyColors && File.Exists(speciesBackgroundFilePath) && File.Exists(speciesColorMaskFilePath) && !File.Exists(cacheFileName)) { using (Bitmap bmpBackground = new Bitmap(speciesBackgroundFilePath)) using (Bitmap bmpCreature = new Bitmap(bmpBackground.Width, bmpBackground.Height, PixelFormat.Format32bppArgb)) using (Graphics graph = Graphics.FromImage(bmpCreature)) { bool imageFine = false; graph.SmoothingMode = SmoothingMode.AntiAlias; const int defaultSizeOfTemplates = 256; using (Bitmap bmpMask = new Bitmap(defaultSizeOfTemplates, defaultSizeOfTemplates)) { using (var g = Graphics.FromImage(bmpMask)) using (var bmpMaskOriginal = new Bitmap(speciesColorMaskFilePath)) g.DrawImage(bmpMaskOriginal, 0, 0, defaultSizeOfTemplates, defaultSizeOfTemplates); float o = 0; try { // shadow using (var b = new SolidBrush(Color.FromArgb(12, 0, 0, 0))) { int scx = defaultSizeOfTemplates / 2; int scy = (int)(scx * 1.6); int factor = 25; int sr = scx - 2 * factor; double heightFactor = 0.3; for (int i = 2; i >= 0; i--) { int radius = sr + i * factor; graph.FillEllipse(b, scx - radius, scy - (int)(heightFactor * .7 * radius), 2 * radius, (int)(2 * heightFactor * radius)); } } graph.DrawImage(bmpBackground, 0, 0, defaultSizeOfTemplates, defaultSizeOfTemplates); for (int i = 0; i < bmpBackground.Width; i++) { for (int j = 0; j < bmpBackground.Height; j++) { Color bc = bmpBackground.GetPixel(i, j); if (bc.A > 0) { var cl = bmpMask.GetPixel(i, j); int r = cl.R; int g = cl.G; int b = cl.B; for (int m = 0; m < Species.ColorRegionCount; m++) { if (!enabledColorRegions[m] || colorIds[m] == 0) { continue; } switch (m) { case 0: o = Math.Max(0, r - g - b) / 255f; break; case 1: o = Math.Max(0, g - r - b) / 255f; break; case 2: o = Math.Max(0, b - r - g) / 255f; break; case 3: o = Math.Min(g, b) / 255f; break; case 4: o = Math.Min(r, g) / 255f; break; case 5: o = Math.Min(r, b) / 255f; break; } if (o == 0) { continue; } // using "grain merge", e.g. see https://docs.gimp.org/en/gimp-concepts-layer-modes.html int rMix = bc.R + rgb[m][0] - 128; if (rMix < 0) { rMix = 0; } else if (rMix > 255) { rMix = 255; } int gMix = bc.G + rgb[m][1] - 128; if (gMix < 0) { gMix = 0; } else if (gMix > 255) { gMix = 255; } int bMix = bc.B + rgb[m][2] - 128; if (bMix < 0) { bMix = 0; } else if (bMix > 255) { bMix = 255; } Color c = Color.FromArgb(rMix, gMix, bMix); bc = Color.FromArgb(bc.A, (int)(o * c.R + (1 - o) * bc.R), (int)(o * c.G + (1 - o) * bc.G), (int)(o * c.B + (1 - o) * bc.B)); } bmpCreature.SetPixel(i, j, bc); } } } imageFine = true; } catch { // error during drawing, maybe mask is smaller than image } } if (imageFine) { if (!Directory.Exists(cacheFolder)) { Directory.CreateDirectory(cacheFolder); } bmpCreature.Save(cacheFileName); // safe in cache} } } } bool cacheFileExists = File.Exists(cacheFileName); if (onlyImage && !cacheFileExists) { return(null); } Bitmap bm = new Bitmap(size, size); using (Graphics graph = Graphics.FromImage(bm)) { graph.SmoothingMode = SmoothingMode.AntiAlias; if (cacheFileExists) { graph.CompositingMode = CompositingMode.SourceCopy; graph.CompositingQuality = CompositingQuality.HighQuality; graph.InterpolationMode = InterpolationMode.HighQualityBicubic; graph.SmoothingMode = SmoothingMode.HighQuality; graph.PixelOffsetMode = PixelOffsetMode.HighQuality; graph.DrawImage(new Bitmap(cacheFileName), 0, 0, size, size); } else { // draw piechart int pieAngle = enabledColorRegions.Count(c => c); pieAngle = 360 / (pieAngle > 0 ? pieAngle : 1); int pieNr = 0; for (int c = 0; c < Species.ColorRegionCount; c++) { if (enabledColorRegions[c]) { if (colorIds[c] > 0) { using (var b = new SolidBrush(CreatureColors.CreatureColor(colorIds[c]))) { graph.FillPie(b, (size - pieSize) / 2, (size - pieSize) / 2, pieSize, pieSize, pieNr * pieAngle + 270, pieAngle); } } pieNr++; } } using (var pen = new Pen(Color.Gray)) graph.DrawEllipse(pen, (size - pieSize) / 2, (size - pieSize) / 2, pieSize, pieSize); } } return(bm); }