Exemple #1
0
        public StatsMultiplierTesting()
        {
            InitializeComponent();

            statControls = new List <StatMultiplierTestingControl>();
            for (int s = 0; s < Values.STATS_COUNT; s++)
            {
                var sc = new StatMultiplierTestingControl
                {
                    StatName = "[" + s.ToString() + "]" + Utils.statName(s, true)
                };
                if (Utils.precision(s) == 3)
                {
                    sc.Percent = true;
                }
                sc.OnLevelChanged  += Sc_OnLevelChanged;
                sc.OnTECalculated  += SetTE;
                sc.OnIBCalculated  += SetIB;
                sc.OnIBMCalculated += SetIBM;
                statControls.Add(sc);
            }
            // add controls in order like ingame
            for (int s = 0; s < Values.STATS_COUNT; s++)
            {
                flowLayoutPanel1.Controls.Add(statControls[Values.statsDisplayOrder[s]]);
                flowLayoutPanel1.SetFlowBreak(statControls[Values.statsDisplayOrder[s]], true);
            }

            // set level-control to last
            flowLayoutPanel1.Controls.Add(gbLevel);

            fineAdjustmentRange = new MinMaxDouble(0);
            rbTamed.Checked     = true;
            gbFineAdjustment.Hide();
        }
        public double uniqueTE()
        {
            double eff = -2;

            if (statsWithTE.Count > 0 && results[statsWithTE[0]].Count > chosenResults[statsWithTE[0]])
            {
                for (int s = 0; s < statsWithTE.Count; s++)
                {
                    for (int ss = s + 1; ss < statsWithTE.Count; ss++)
                    {
                        // effectiveness-calculation can be a bit off due to ingame-rounding
                        if (results[statsWithTE[ss]].Count <= chosenResults[statsWithTE[ss]] ||
                            !MinMaxDouble.Overlaps(results[statsWithTE[s]][chosenResults[statsWithTE[s]]].TE,
                                                   results[statsWithTE[ss]][chosenResults[statsWithTE[ss]]].TE))
                        {
                            lastTEUnique = false;
                            return(-1); // no unique TE
                        }
                    }
                }
                // calculate most probable real TE
                // get intersection of all TE-ranges

                MinMaxDouble te = results[statsWithTE[0]][chosenResults[statsWithTE[0]]].TE.Clone();
                for (int s = 1; s < statsWithTE.Count; s++)
                {
                    // the overlap is ensured at this point
                    te.SetToIntersectionWith(results[statsWithTE[s]][chosenResults[statsWithTE[s]]].TE);
                }

                eff = te.Mean;
            }
            lastTEUnique = eff >= 0;
            return(eff);
        }
Exemple #3
0
        public bool currentlyNotValid = false; // set to true if result violates other choosen result

        public StatResult(int levelWild, int levelDom, double statValue = 0, MinMaxDouble?TE = null)
        {
            this.levelWild = levelWild;
            this.levelDom  = levelDom;
            this.TE        = TE ?? new MinMaxDouble(-1);
            this.statValue = statValue;
        }
Exemple #4
0
        public StatsMultiplierTesting()
        {
            InitializeComponent();

            _statControls = new StatMultiplierTestingControl[Values.STATS_COUNT];
            for (int s = 0; s < Values.STATS_COUNT; s++)
            {
                var sc = new StatMultiplierTestingControl();
                if (Utils.Precision(s) == 3)
                {
                    sc.Percent = true;
                }
                sc.OnLevelChanged  += Sc_OnLevelChanged;
                sc.OnTECalculated  += SetTE;
                sc.OnIBCalculated  += SetIB;
                sc.OnIBMCalculated += SetIBM;
                _statControls[s]    = sc;
            }
            // add controls in order like in-game
            for (int s = 0; s < Values.STATS_COUNT; s++)
            {
                flowLayoutPanel1.Controls.Add(_statControls[Values.statsDisplayOrder[s]]);
                flowLayoutPanel1.SetFlowBreak(_statControls[Values.statsDisplayOrder[s]], true);
            }

            // set bottom controls to bottom
            flowLayoutPanel1.Controls.Add(gbLevel);
            flowLayoutPanel1.Controls.Add(LbAbbreviations);

            _fineAdjustmentRange = new MinMaxDouble(0);
            rbTamed.Checked      = true;
            gbFineAdjustment.Hide();
        }
        public StatsMultiplierTesting()
        {
            InitializeComponent();

            statControls = new List <StatMultiplierTestingControl>();
            for (int s = 0; s < 8; s++)
            {
                var sc = new StatMultiplierTestingControl
                {
                    StatName = Utils.statName(s, true)
                };
                sc.OnLevelChanged    += Sc_OnLevelChanged;
                sc.OnValueChangedTE  += setTE;
                sc.OnValueChangedIB  += setIB;
                sc.OnValueChangedIBM += setIBM;
                flowLayoutPanel1.Controls.Add(sc);
                flowLayoutPanel1.SetFlowBreak(sc, true);
                statControls.Add(sc);
            }
            // set level-control to last
            flowLayoutPanel1.Controls.Add(gbLevel);

            statControls[5].Percent = true;
            statControls[6].Percent = true;
            statControls[1].NoIB    = true;
            statControls[2].NoIB    = true;

            fineAdjustmentRange = new MinMaxDouble(0);
            rbTamed.Checked     = true;
            gbFineAdjustment.Hide();
        }
        public double UniqueTE()
        {
            double eff = -2;

            if (StatsWithTE.Any() && Results[StatsWithTE[0]].Count > ChosenResults[StatsWithTE[0]])
            {
                for (int s = 0; s < StatsWithTE.Count; s++)
                {
                    for (int ss = s + 1; ss < StatsWithTE.Count; ss++)
                    {
                        // effectiveness-calculation can be a bit off due to ingame-rounding
                        if (Results[StatsWithTE[ss]].Count <= ChosenResults[StatsWithTE[ss]] ||
                            !MinMaxDouble.Overlaps(Results[StatsWithTE[s]][ChosenResults[StatsWithTE[s]]].TE,
                                                   Results[StatsWithTE[ss]][ChosenResults[StatsWithTE[ss]]].TE))
                        {
                            return(-1); // no unique TE
                        }
                    }
                }
                // calculate most probable real TE
                // get intersection of all TE-ranges

                MinMaxDouble te = Results[StatsWithTE[0]][ChosenResults[StatsWithTE[0]]].TE.Clone();
                for (int s = 1; s < StatsWithTE.Count; s++)
                {
                    // the overlap is ensured at this point
                    te.SetToIntersectionWith(Results[StatsWithTE[s]][ChosenResults[StatsWithTE[s]]].TE);
                }

                eff = te.Mean;
            }
            return(eff);
        }
        public bool currentlyNotValid; // set to true if result violates other choosen result

        public StatResult(int levelWild, int levelDom, double statValue = 0, MinMaxDouble TE = null)
        {
            this.levelWild = levelWild;
            this.levelDom  = levelDom;
            if (TE == null)
            {
                this.TE = new MinMaxDouble(-1);
            }
            else
            {
                this.TE = TE;
            }
            currentlyNotValid = false;
            this.statValue    = statValue;
        }
Exemple #8
0
 private void calculateIBM()
 {
     // set TE to the value that solves the equation, assuming all other values are correct
     if (_bred && !_NoIB && _IB > 0)
     {
         var statValue = new MinMaxDouble((double)nudStatValue.Value - 0.05, (double)nudStatValue.Value + 0.05);
         statValue.Min *= (_percent ? 0.01 : 1);
         statValue.Max *= (_percent ? 0.01 : 1);
         OnValueChangedIBM?.Invoke(new MinMaxDouble(
                                       ((statValue.Min * Vd / (V * (1 + (_bred ? 1 : _TE) * (double)nudTm.Value * (nudTm.Value > 0 ? (double)nudTmM.Value * spTm : 1))) - ((double)nudTa.Value * (nudTa.Value > 0 ? (double)nudTaM.Value * spTa : 1))) / ((double)nudB.Value * (1 + (double)nudLw.Value * (double)nudIw.Value * spIw * (double)nudIwM.Value) * (double)nudTBHM.Value) - 1) * 5 / _IB,
                                       ((statValue.Max * Vd / (V * (1 + (_bred ? 1 : _TE) * (double)nudTm.Value * (nudTm.Value > 0 ? (double)nudTmM.Value * spTm : 1))) - ((double)nudTa.Value * (nudTa.Value > 0 ? (double)nudTaM.Value * spTa : 1))) / ((double)nudB.Value * (1 + (double)nudLw.Value * (double)nudIw.Value * spIw * (double)nudIwM.Value) * (double)nudTBHM.Value) - 1) * 5 / _IB
                                       ));
     }
     else
     {
         MessageBox.Show("Divide by Zero-error, e.g. IB needs to be > 0, creature has to be bred and stat has to be affected by IB.");
     }
 }
Exemple #9
0
 private void calculateTE()
 {
     // set TE to the value that solves the equation, assuming all other values are correct
     if (nudTm.Value > 0 && (_tamed || _bred))
     {
         var statValue = new MinMaxDouble((double)nudStatValue.Value - 0.05, (double)nudStatValue.Value + 0.05);
         statValue.Min *= (_percent ? 0.01 : 1);
         statValue.Max *= (_percent ? 0.01 : 1);
         OnValueChangedTE?.Invoke(new MinMaxDouble(
                                      (statValue.Min * Vd / (V * ((double)nudB.Value * (1 + (double)nudLw.Value * (double)nudIw.Value * spIw * (double)nudIwM.Value) * (double)nudTBHM.Value * (!_NoIB && _bred ? 1 + _IB * _IBM * 0.2 : 1) + ((double)nudTa.Value * (nudTa.Value > 0 ? (double)nudTaM.Value * spTa : 1)))) - 1) / ((double)nudTm.Value * (nudTm.Value > 0 ? (double)nudTmM.Value * spTm : 1)),
                                      (statValue.Max * Vd / (V * ((double)nudB.Value * (1 + (double)nudLw.Value * (double)nudIw.Value * spIw * (double)nudIwM.Value) * (double)nudTBHM.Value * (!_NoIB && _bred ? 1 + _IB * _IBM * 0.2 : 1) + ((double)nudTa.Value * (nudTa.Value > 0 ? (double)nudTaM.Value * spTa : 1)))) - 1) / ((double)nudTm.Value * (nudTm.Value > 0 ? (double)nudTmM.Value * spTm : 1))
                                      ));
     }
     else
     {
         MessageBox.Show("Divide by Zero-error, e.g. Tm needs to be > 0, the stat has to be affected by TE and the creature has to be tamed or bred.");
     }
 }
 public void Clear()
 {
     for (int s = 0; s < Values.STATS_COUNT; s++)
     {
         ChosenResults[s] = 0;
         FixedResults[s]  = false;
         Results[s].Clear();
         _lowerBoundWilds[s] = 0;
         _lowerBoundDoms[s]  = 0;
         _upperBoundDoms[s]  = 0;
     }
     ValidResults  = false;
     UniqueResults = false;
     StatsWithTE.Clear();
     _imprintingBonusRange = new MinMaxDouble(0);
     LevelWildSum          = 0;
     LevelDomSum           = 0;
 }
 public void Clear()
 {
     for (int s = 0; s < statsCount; s++)
     {
         chosenResults[s] = 0;
         fixedResults[s]  = false;
         results[s].Clear();
         lowerBoundWilds[s] = 0;
         lowerBoundDoms[s]  = 0;
         upperBoundDoms[s]  = 0;
     }
     validResults  = false;
     uniqueResults = false;
     statsWithTE.Clear();
     imprintingBonusRange = new MinMaxDouble(0);
     levelWildSum         = 0;
     levelDomSum          = 0;
     lastTEUnique         = false;
     possibleIssues       = IssueNotes.Issue.None;
 }
        public StatsMultiplierTesting()
        {
            InitializeComponent();

            statControls = new List <StatMultiplierTestingControl>();
            for (int s = 0; s < statsCount; s++)
            {
                var sc = new StatMultiplierTestingControl
                {
                    StatName = Utils.statName(s, true)
                };
                if (Utils.precision(s) == 3)
                {
                    sc.Percent = true;
                }
                sc.OnLevelChanged    += Sc_OnLevelChanged;
                sc.OnValueChangedTE  += setTE;
                sc.OnValueChangedIB  += setIB;
                sc.OnValueChangedIBM += setIBM;
                statControls.Add(sc);
            }
            // add controls in order like ingame
            for (int s = 0; s < statsCount; s++)
            {
                flowLayoutPanel1.Controls.Add(statControls[Values.statsDisplayOrder[s]]);
                flowLayoutPanel1.SetFlowBreak(statControls[Values.statsDisplayOrder[s]], true);
            }

            // set level-control to last
            flowLayoutPanel1.Controls.Add(gbLevel);

            statControls[(int)StatNames.Stamina].NoIB = true;
            statControls[(int)StatNames.Oxygen].NoIB  = true;

            fineAdjustmentRange = new MinMaxDouble(0);
            rbTamed.Checked     = true;
            gbFineAdjustment.Hide();
        }
        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;
            }

            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) * 0.2 * 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) * 0.2 * 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) * 0.2 * 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) * 0.2 * 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;
                MinMaxDouble imprintingBonusRange = new MinMaxDouble(
                    (((torpor - 0.05) / (1 + species.stats[(int)StatNames.Torpidity].MultAffinity) - species.stats[(int)StatNames.Torpidity].AddWhenTamed) / Stats.calculateValue(species, (int)StatNames.Torpidity, torporLevel, 0, false, 0, 0) - 1) / (0.2 * imprintingBonusMultiplier),
                    (((torpor + 0.05) / (1 + species.stats[(int)StatNames.Torpidity].MultAffinity) - species.stats[(int)StatNames.Torpidity].AddWhenTamed) / Stats.calculateValue(species, (int)StatNames.Torpidity, torporLevel, 0, false, 0, 0) - 1) / (0.2 * imprintingBonusMultiplier));

                // 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) / Stats.calculateValue(species, (int)StatNames.Food, foodLevel, 0, false, 0, 0) - 1) / (0.2 * imprintingBonusMultiplier),
                        (((food + 0.05) / (1 + species.stats[(int)StatNames.Food].MultAffinity) - species.stats[(int)StatNames.Food].AddWhenTamed) / Stats.calculateValue(species, (int)StatNames.Food, foodLevel, 0, false, 0, 0) - 1) / (0.2 * imprintingBonusMultiplier));


                    // 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 (Stats.calculateValue(species, (int)StatNames.Torpidity, torporLevel, 0, true, 1, intersectionIB.Min) <= torpor &&
                            Stats.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) &&
                    Stats.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());
        }
Exemple #14
0
 private void SetIBM(MinMaxDouble IBM)
 {
     SetIBM(IBM.Mean);
     SetFineAdjustmentNUD(nudIBM, "Imprinting Bonus Multiplier (IBM)", IBM.Min, IBM.Max);
 }
Exemple #15
0
 private void SetIB(MinMaxDouble IB)
 {
     SetIB(IB.Mean);
     SetFineAdjustmentNUD(nudIB, "Imprinting Bonus (IB)", IB.Min, IB.Max);
 }
Exemple #16
0
        public void extractLevels(int speciesI, int level, List <StatIO> statIOs, double lowerTEBound, double upperTEBound, bool autoDetectTamed,
                                  bool tamed, bool bred, double imprintingBonusRounded, bool adjustImprinting, bool allowMoreThanHundredImprinting, double imprintingBonusMultiplier, double cuddleIntervalMultiplier,
                                  bool considerWildLevelSteps, int wildLevelSteps, out bool imprintingChanged)
        {
            List <CreatureStat> stats = Values.V.species[speciesI].stats;

            validResults           = true;
            imprintingChanged      = false;
            considerWildLevelSteps = considerWildLevelSteps && !bred;

            this.bred = bred;
            if (bred)
            {
                postTamed = true;
            }
            else
            {
                postTamed = tamed;
            }

            List <double> imprintingBonusList = new List <double>()
            {
                0
            };

            if (bred)
            {
                if (!adjustImprinting)
                {
                    imprintingBonusList[0] = imprintingBonusRounded;
                }
                else
                {
                    imprintingBonusList = CalculateImprintingBonus(imprintingBonusRounded, imprintingBonusMultiplier, cuddleIntervalMultiplier, stats, speciesI, statIOs[7].Input, statIOs[3].Input);
                }
            }

            for (int IBi = 0; IBi < imprintingBonusList.Count; IBi++)
            {
                imprintingBonus = imprintingBonusList[IBi];
                if (!allowMoreThanHundredImprinting && imprintingBonus > 1)
                {
                    imprintingBonus = 1;
                }
                if (imprintingBonus < 0)
                {
                    imprintingBonus = 0;
                }
                imprintingChanged = (Math.Abs(imprintingBonusRounded - imprintingBonus) > 0.01);
                double imprintingMultiplier = (1 + imprintingBonus * imprintingBonusMultiplier * .2);

                levelWildSum = (int)Math.Round((statIOs[7].Input / imprintingMultiplier - (postTamed ? stats[7].AddWhenTamed : 0) - stats[7].BaseValue) / (stats[7].BaseValue * stats[7].IncPerWildLevel), 0);
                levelDomSum  = Math.Max(0, level - 1 - levelWildSum);

                levelsUndeterminedWild = levelWildSum;
                levelsUndeterminedDom  = levelDomSum;

                if (bred)
                {
                    // bred creatures always have 100% TE
                    lowerTEBound = 1;
                    upperTEBound = 1;
                }

                // check all possible level-combinations
                for (int s = 0; s < 8; s++)
                {
                    if (stats[s].BaseValue > 0 && activeStats[s]) // if stat is used (oxygen sometimes is not)
                    {
                        statIOs[s].postTame = postTamed;
                        // double precision makes it necessary to give a bit more tolerance (hence 0.050001 instead of just 0.05 etc.)
                        MinMaxDouble inputValue    = new MinMaxDouble(statIOs[s].Input - (Utils.precision(s) == 3 ? 0.00050001 : 0.050001), statIOs[s].Input + (Utils.precision(s) == 3 ? 0.00050001 : 0.050001));
                        double       statBaseValue = stats[s].BaseValue;
                        if (postTamed)
                        {
                            statBaseValue *= (s == 0 ? (double)Values.V.species[speciesI].TamedBaseHealthMultiplier : 1);
                        }

                        bool withTEff = (postTamed && stats[s].MultAffinity > 0);
                        if (withTEff)
                        {
                            statsWithTE.Add(s);
                        }
                        int minLW = 0, maxLW = 0;
                        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 && stats[s].BaseValue > 0 && 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 (Stats.calculateValue(speciesI, s, ww, 0, true, lowerTEBound, 0) <= 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;
                        }

                        double statImprintingMultiplier = (!bred || s == 1 || s == 2 || (s == 6 && Values.V.species[speciesI].NoImprintingForSpeed == true) ? 1 : imprintingMultiplier);

                        // if dom levels have no effect, just calculate the wild level
                        if (stats[s].IncPerTamedLevel == 0)
                        {
                            if (minLW == -1)
                            {
                                results[s].Add(new StatResult(-1, 0, inputValue.Mean));
                            }
                            else
                            {
                                MinMaxDouble lwRange = new MinMaxDouble(((inputValue.Min / (postTamed ? 1 + stats[s].MultAffinity : 1) - (postTamed ? stats[s].AddWhenTamed : 0)) / (statBaseValue * statImprintingMultiplier) - 1) / stats[s].IncPerWildLevel,
                                                                        ((inputValue.Max / (postTamed ? 1 + stats[s].MultAffinity : 1) - (postTamed ? stats[s].AddWhenTamed : 0)) / (statBaseValue * statImprintingMultiplier) - 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)
                            double valueWODom = statBaseValue * (1 + stats[s].IncPerWildLevel * lw) * statImprintingMultiplier + (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 / (valueWODom * (postTamed ? 1 + stats[s].MultAffinity : 1)) - 1) / stats[s].IncPerTamedLevel,
                                                                            (inputValue.Max / (valueWODom * (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) - valueWODom) / (valueWODom * stats[s].MultAffinity),
                                                                                        (inputValue.Max / (1 + stats[s].IncPerTamedLevel * ld) - valueWODom) / (valueWODom * 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((int)Math.Ceiling(levelPostTame / (1 + tamingEffectiveness.Max / 2)),
                                                                                    (int)Math.Ceiling(levelPostTame / (1 + tamingEffectiveness.Min / 2)));

                                        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));
                                }
                            }
                        }
                    }
                    else
                    {
                        results[s].Add(new StatResult(-1, 0));
                    }
                }
                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
                        break;
                    }
                    else if (IBi < imprintingBonusList.Count - 1)
                    {
                        // not all stats got a result, clear results for the next round
                        Clear();
                        validResults = true;
                    }
                }
            }
        }
        public bool currentlyNotValid = false; // set to true if result violates other chosen result

        public StatResult(int levelWild, int levelDom, MinMaxDouble?TE = null)
        {
            this.levelWild = levelWild;
            this.levelDom  = levelDom;
            this.TE        = TE ?? new MinMaxDouble(-1);
        }
        public void extractLevels(Species species, int level, List <StatIO> statIOs, double lowerTEBound, double upperTEBound, bool autoDetectTamed,
                                  bool tamed, bool bred, double imprintingBonusRounded, bool adjustImprinting, bool allowMoreThanHundredImprinting, double imprintingBonusMultiplier, double cuddleIntervalMultiplier,
                                  bool considerWildLevelSteps, int wildLevelSteps, 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 imprintingMultiplierRange = new MinMaxDouble(1 + imprintingBonusRange.Min * imprintingBonusMultiplier * .2, 1 + imprintingBonusRange.Max * imprintingBonusMultiplier * .2);

                var levelWildSumRange = new MinMaxInt((int)Math.Round((statIOs[(int)StatNames.Torpidity].Input / imprintingMultiplierRange.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 / imprintingMultiplierRange.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;
                    upperTEBound += 0.0006;
                }

                // check all possible level-combinations
                for (int s = 0; s < statsCount; s++)
                {
                    if (stats[s].BaseValue > 0 && statIOs[s].Input > 0) // if stat is used (oxygen sometimes is not)
                    {
                        statIOs[s].postTame = postTamed;
                        // double precision makes it necessary to give a bit more tolerance (hence 0.050001 instead of just 0.05 etc.)
                        // the rounding still makes issues, trying +-0.1 in the input often helps. setting the tolerance from the expected 0.05 to 0.06
                        MinMaxDouble inputValue    = new MinMaxDouble(statIOs[s].Input - (Utils.precision(s) == 3 ? 0.00060001 : 0.060001), statIOs[s].Input + (Utils.precision(s) == 3 ? 0.00060001 : 0.060001));
                        double       statBaseValue = stats[s].BaseValue;
                        if (postTamed && s == 0)
                        {
                            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, maxLW = 0;
                        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 && stats[s].BaseValue > 0 && 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 (Stats.calculateValue(species, s, ww, 0, true, lowerTEBound, 0) <= 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 && s != (int)StatNames.Stamina && s != (int)StatNames.Oxygen && s != (int)StatNames.CraftingSpeedMultiplier && (s != (int)StatNames.SpeedMultiplier || species.NoImprintingForSpeed == false))
                        {
                            statImprintingMultiplierRange = imprintingMultiplierRange.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 (minLW == -1)
                            {
                                //results[s].Add(new StatResult(-1, 0, inputValue.Mean));
                                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((int)Math.Ceiling(levelPostTame / (1 + tamingEffectiveness.Max / 2)),
                                                                                    (int)Math.Ceiling(levelPostTame / (1 + tamingEffectiveness.Min / 2)));

                                        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));
                                }
                            }
                        }
                    }
                    else
                    {
                        results[s].Add(new StatResult(-1, 0));
                    }
                }
                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;
                    }
                }
            }
        }
Exemple #19
0
        /// <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="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,
                                  bool considerWildLevelSteps, int wildLevelSteps, bool highPrecisionInputs, out bool imprintingChanged)
        {
            var stats = species.stats;

            ValidResults           = true;
            imprintingChanged      = false;
            considerWildLevelSteps = considerWildLevelSteps &&
                                     !bred &&
                                     species.name.Substring(0, 3) != "Tek" &&
                                     species.name != "Jerboa"
            ;

            _bred     = bred;
            PostTamed = bred || tamed;

            List <MinMaxDouble> imprintingBonusList = null;

            if (bred)
            {
                if (!adjustImprinting)
                {
                    imprintingBonusList = new List <MinMaxDouble> {
                        new MinMaxDouble(imprintingBonusRounded)
                    };
                }
                else
                {
                    imprintingBonusList = CalculateImprintingBonus(species, imprintingBonusRounded, imprintingBonusMultiplier, statIOs[(int)StatNames.Torpidity].Input, statIOs[(int)StatNames.Food].Input);
                }
            }
            if (imprintingBonusList == null)
            {
                imprintingBonusList = new List <MinMaxDouble> {
                    new MinMaxDouble(0)
                }
            }
            ;

            for (int IBi = 0; IBi < imprintingBonusList.Count; IBi++)
            {
                _imprintingBonusRange = imprintingBonusList[IBi];
                // don't cut off too much possible values, consider a margin of 0.01 to not sort out possible correct values
                _imprintingBonusRange.SetToIntersectionWith(-.01, allowMoreThanHundredImprinting ? 5 : 1.01); // it's assumed that a valid IB will not be larger than 500%
                ImprintingBonus = Math.Max(0, Math.Min(allowMoreThanHundredImprinting ? 5 : 1, _imprintingBonusRange.Mean));

                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));
                            }
                        }
                        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));
                            }
                        }
                        // 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));
                                }
                            }
                            else
                            {
                                Results[s].Add(new StatResult(lw, 0));
                            }
                        }
                        else
                        {
                            for (int ld = 0; ld <= maxLD; ld++)
                            {
                                // taming bonus is dependent 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 dependent 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-dependent stats)
                                    if (StatsWithTE.Count > 1)
                                    {
                                        bool teExistent = false;
                                        for (int er = 0; er < Results[StatsWithTE[0]].Count; er++)
                                        {
                                            if (tamingEffectiveness.Overlaps(Results[StatsWithTE[0]][er].TE))
                                            {
                                                teExistent = true;
                                                break;
                                            }
                                        }
                                        if (!teExistent)
                                        {
                                            continue;
                                        }
                                    }
                                }

                                Results[s].Add(new StatResult(lw, ld, 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;
                    }
                }
            }
        }
Exemple #20
0
 private void SetTE(MinMaxDouble TE)
 {
     SetTE(TE.Mean);
     SetFineAdjustmentNUD(nudTE, "Taming Effectiveness (TE)", TE.Min, TE.Max);
 }