/// <summary>
 /// Set text of labels for stats. Only used for header control.
 /// </summary>
 public void SetCustomStatNames(Dictionary <string, string> customStatNames = null)
 {
     for (int s = 0; s < displayedStatsCount; s++)
     {
         labels[s].Text = Utils.StatName(displayedStats[s], true, customStatNames);
         tt.SetToolTip(labels[s], Utils.StatName(displayedStats[s], customStatNames: customStatNames));
     }
 }
Beispiel #2
0
        private void setLocalizations(bool init = true)
        {
            if (init)
                initLocalization();

            // menu
            Loc.ControlText(fileToolStripMenuItem);
            Loc.ControlText(newToolStripMenuItem);
            Loc.ControlText(loadToolStripMenuItem);
            Loc.ControlText(loadAndAddToolStripMenuItem);
            Loc.ControlText(saveToolStripMenuItem);
            Loc.ControlText(saveAsToolStripMenuItem);
            Loc.ControlText(importingFromSavegameToolStripMenuItem);
            Loc.ControlText(importingFromSavegameEmptyToolStripMenuItem);
            //Loc.ControlText(runDefaultExtractionAndImportFileToolStripMenuItem);
            //Loc.ControlText(runDefaultExtractionToolStripMenuItem);
            //Loc.ControlText(importCreatedJsonfileToolStripMenuItem);
            Loc.ControlText(importExportedCreaturesToolStripMenuItem);
            //Loc.ControlText(runDefaultExtractionAndImportFileToolStripMenuItem);
            //Loc.ControlText(runDefaultExtractionToolStripMenuItem);
            //Loc.ControlText(importCreatedJsonfileToolStripMenuItem);
            Loc.ControlText(modValueManagerToolStripMenuItem);
            Loc.ControlText(settingsToolStripMenuItem);
            Loc.ControlText(openSettingsToolStripMenuItem);
            Loc.ControlText(quitToolStripMenuItem);
            Loc.ControlText(editToolStripMenuItem);
            Loc.ControlText(exportValuesToClipboardToolStripMenuItem);
            Loc.ControlText(importValuesFromClipboardToolStripMenuItem);
            Loc.ControlText(setStatusToolStripMenuItem);
            Loc.ControlText(multiSetterToolStripMenuItem);
            Loc.ControlText(deleteSelectedToolStripMenuItem);
            Loc.ControlText(findDuplicatesToolStripMenuItem);
            Loc.ControlText(copyCreatureToolStripMenuItem);
            Loc.ControlText(pasteCreatureToolStripMenuItem);
            Loc.ControlText(libraryFilterToolStripMenuItem);
            Loc.ControlText(helpToolStripMenuItem);
            Loc.ControlText(aboutToolStripMenuItem);
            Loc.ControlText(onlinehelpToolStripMenuItem);
            Loc.ControlText(BreedingPlanHelpToolStripMenuItem);
            Loc.ControlText(extractionIssuesToolStripMenuItem);
            Loc.ControlText(checkForUpdatedStatsToolStripMenuItem);
            Loc.ControlText(toolStripButtonCopy2Tester);
            Loc.ControlText(toolStripButtonCopy2Extractor);
            Loc.ControlText(toolStripButtonClear);
            Loc.ControlText(toolStripButtonAddNote);
            Loc.ControlText(toolStripButtonRemoveNote);
            Loc.ControlText(toolStripButtonDeleteExpiredIncubationTimers);
            Loc.ControlText(toolStripButtonSaveCreatureValuesTemp);
            Loc.ControlText(toolStripButtonDeleteTempCreature);
            Loc.ControlText(tsBtAddAsExtractionTest);
            Loc.ControlText(copyToMultiplierTesterToolStripButton);

            // top bar
            Loc.ControlText(cbGuessSpecies, _tt);
            Loc.ControlText(btReadValuesFromArk, _tt);
            Loc.ControlText(btImportLastExported, _tt);
            Loc.ControlText(cbToggleOverlay);

            // tester
            Loc.ControlText(tabPageStatTesting, "statTesting");
            Loc.ControlText(rbWildTester, "wild");
            Loc.ControlText(rbTamedTester, "tamed");
            Loc.ControlText(rbBredTester, "bred");
            Loc.ControlText(lbTesterWildLevel, "wildLvl");
            Loc.ControlText(lbTesterDomLevel, "domLvl");
            Loc.ControlText(lbCurrentValue, "currentValue");
            Loc.ControlText(lbBreedingValueTester, "breedingValue");
            Loc.ControlText(lbNotYetTamed);
            Loc.ControlText(gpPreviewEdit);
            Loc.ControlText(lbTestingInfo);
            Loc.ControlText(gbStatChart, "statChart");
            Loc.ControlText(lbCurrentCreature, "CurrentCreature");
            Loc.SetToolTip(lbImprintedCount, _tt);
            Loc.SetToolTip(lbTesterDomLevel, "domLevelExplanation", _tt);
            Loc.SetToolTip(lbTesterWildLevel, "wildLevelExplanation", _tt);

            // extractor
            Loc.ControlText(tabPageExtractor, "extractor");
            Loc.ControlText(lbCurrentStatEx, "currentStatValue");
            Loc.ControlText(lbExtractorWildLevel, "wildLvl");
            Loc.ControlText(lbExtractorDomLevel, "domLvl");
            Loc.ControlText(lbSum);
            Loc.ControlText(lbShouldBe);
            Loc.ControlText(lbImprintingFailInfo);
            Loc.ControlText(cbExactlyImprinting, _tt);
            Loc.ControlText(btExtractLevels);
            Loc.ControlText(cbQuickWildCheck, _tt);
            Loc.ControlText(rbWildExtractor, "wild");
            Loc.ControlText(rbTamedExtractor, "tamed");
            Loc.ControlText(rbBredExtractor, "bred");
            Loc.SetToolTip(lbImprintingCuddleCountExtractor, _tt);
            Loc.SetToolTip(lbSumWild, _tt);
            Loc.SetToolTip(lbSumDom, _tt);
            Loc.SetToolTip(lbSumDomSB, _tt);
            Loc.SetToolTip(lbListening, _tt);
            Loc.SetToolTip(lbExtractorDomLevel, "domLevelExplanation", _tt);
            Loc.SetToolTip(lbExtractorWildLevel, "wildLevelExplanation", _tt);
            var statNames = speciesSelector1.SelectedSpecies?.statNames;
            for (int si = 0; si < _statIOs.Count; si++)
            {
                _statIOs[si].Title = Utils.StatName(si, false, statNames);
                _testingIOs[si].Title = Utils.StatName(si, false, statNames);
            }

            // library
            Loc.ControlText(tabPageLibrary, "library");
            columnHeaderName.Text = Loc.S("Name");
            columnHeaderOwner.Text = Loc.S("Owner");
            columnHeaderTribe.Text = Loc.S("Tribe");
            columnHeaderNote.Text = Loc.S("Note");
            columnHeaderServer.Text = Loc.S("Server");
            columnHeaderHP.Text = Utils.StatName(StatNames.Health, true);
            columnHeaderSt.Text = Utils.StatName(StatNames.Stamina, true);
            columnHeaderOx.Text = Utils.StatName(StatNames.Oxygen, true);
            columnHeaderFo.Text = Utils.StatName(StatNames.Food, true);
            columnHeaderWe.Text = Utils.StatName(StatNames.Weight, true);
            columnHeaderDm.Text = Utils.StatName(StatNames.MeleeDamageMultiplier, true);
            columnHeaderSp.Text = Utils.StatName(StatNames.SpeedMultiplier, true);
            columnHeaderTo.Text = Utils.StatName(StatNames.Torpidity, true);
            columnHeaderWa.Text = Utils.StatName(StatNames.CraftingSpeedMultiplier, true);
            columnHeaderTemp.Text = Utils.StatName(StatNames.Temperature, true);
            columnHeaderCr.Text = Utils.StatName(StatNames.Water, true);
            columnHeaderFr.Text = Utils.StatName(StatNames.TemperatureFortitude, true);
            columnHeaderTopStatsNr.Text = Loc.S("Top");
            columnHeaderTopness.Text = Loc.S("topPercentage");
            columnHeaderGen.Text = Loc.S("Generation_Abb");
            columnHeaderLW.Text = Loc.S("LevelWild_Abb");
            columnHeaderMutations.Text = Loc.S("Mutations_Abb");
            columnHeaderAdded.Text = Loc.S("added");
            columnHeaderCooldown.Text = Loc.S("cooldownGrowing");
            columnHeaderColor0.Text = Loc.S("C0");
            columnHeaderColor1.Text = Loc.S("C1");
            columnHeaderColor2.Text = Loc.S("C2");
            columnHeaderColor3.Text = Loc.S("C3");
            columnHeaderColor4.Text = Loc.S("C4");
            columnHeaderColor5.Text = Loc.S("C5");

            // other tabs
            Loc.ControlText(tabPagePedigree, "pedigree");
            Loc.ControlText(tabPageTaming, "Taming");
            Loc.ControlText(tabPageBreedingPlan, "BreedingPlan");
            Loc.ControlText(tabPageRaising, "Raising");
            Loc.ControlText(tabPagePlayerTribes, "Player");

            // other controls
            creatureInfoInputTester.SetLocalizations();
            creatureInfoInputExtractor.SetLocalizations();
            pedigree1.SetLocalizations();
            tamingControl1.SetLocalizations();
            breedingPlan1.SetLocalizations();
        }
        /// <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}", $"{Loc.S("error")} - {Utils.ApplicationNameVersion}", 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>
        /// 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 added               = false;
            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 (_extractor.UniqueResults ||
                (alreadyExists && _extractor.ValidResults))
            {
                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);
                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} \"{creature.name}\" {(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.statNames)}: { _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);
            }

            if (_overlay != null)
            {
                _overlay.SetInfoText(infoText, textColor);
                if (Properties.Settings.Default.DisplayInheritanceInOverlay && creature != null)
                {
                    _overlay.SetInheritanceCreatures(creature, creature.Mother, creature.Father);
                }
            }

            if (added)
            {
                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)
                        ? NamePatterns.GenerateCreatureName(creature,
                                                            _creatureCollection.creatures.Where(c => c.Species == speciesSelector1.SelectedSpecies).ToArray(), null, null,
                                                            _customReplacingNamingPattern, false, -1, false, namePattern)
                        : Path.GetFileName(filePath);

                    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 (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 (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);
                }
            }
        }
Beispiel #5
0
        /// <summary>
        /// Draws a chart with the given levels.
        /// </summary>
        /// <param name="levels">If null, the previous values are redrawn.</param>
        public void SetLevels(int[] levels = null)
        {
            if ((levels != null && levels.Length <= 6) || _maxR <= 5)
            {
                return;
            }

            Bitmap bmp = new Bitmap(Width, Height);

            using (Graphics g = Graphics.FromImage(bmp))
                using (Pen penLine = new Pen(Color.FromArgb(128, 255, 255, 255)))
                    using (var font = new Font("Microsoft Sans Serif", 8f))
                    {
                        g.SmoothingMode = SmoothingMode.AntiAlias;

                        // the indices of the displayed stats
                        var levelIndices = new[]
                        {
                            (int)StatNames.Health, (int)StatNames.Stamina, (int)StatNames.Oxygen, (int)StatNames.Food,
                            (int)StatNames.Weight, (int)StatNames.MeleeDamageMultiplier, (int)StatNames.SpeedMultiplier
                        };

                        for (int s = 0; s < levelIndices.Length; s++)
                        {
                            if (levels == null || levels[levelIndices[s]] != _oldLevels[s])
                            {
                                if (levels != null)
                                {
                                    _oldLevels[s] = levels[levelIndices[s]];
                                }
                                int r = _oldLevels[s] * _maxR / _maxLevel;
                                if (r < 0)
                                {
                                    r = 0;
                                }
                                if (r > _maxR)
                                {
                                    r = _maxR;
                                }
                                double angle = AnglePerStat * s - AngleOffset;
                                _ps[s] = new Point(_xm + (int)(r * Math.Cos(angle)), _ym + (int)(r * Math.Sin(angle)));
                            }
                        }

                        g.FillEllipse(_grBrushBg, _xm - _maxR, _ym - _maxR, 2 * _maxR + 1, 2 * _maxR + 1);

                        g.FillPolygon(_grBrushFg, _ps.ToArray());
                        g.DrawPolygon(penLine, _ps.ToArray());

                        double stepFactor = (double)_step / _maxLevel;
                        for (int r = 0; r < 5; r++)
                        {
                            using (var pen = new Pen(Utils.GetColorFromPercent((int)(100 * r * stepFactor), -0.4)))
                                g.DrawEllipse(pen,
                                              (int)(_xm - _maxR * r * stepFactor), (int)(_ym - _maxR * r * stepFactor),
                                              (int)(2 * _maxR * r * stepFactor + 1), (int)(2 * _maxR * r * stepFactor + 1));
                        }

                        using (var pen = new Pen(Utils.GetColorFromPercent(100, -0.4)))
                            g.DrawEllipse(pen, _xm - _maxR, _ym - _maxR, 2 * _maxR + 1, 2 * _maxR + 1);

                        using (var pen = new Pen(Color.Gray))
                            for (int s = 0; s < levelIndices.Length; s++)
                            {
                                pen.Width = 1;
                                pen.Color = Color.Gray;
                                g.DrawLine(pen, _xm, _ym, _maxPs[s].X, _maxPs[s].Y);
                                Color cl = Utils.GetColorFromPercent(100 * _oldLevels[s] / _maxLevel);
                                pen.Color = cl;
                                pen.Width = 3;
                                g.DrawLine(pen, _xm, _ym, _ps[s].X, _ps[s].Y);
                                Brush b = new SolidBrush(cl);
                                g.FillEllipse(b, _ps[s].X - 4, _ps[s].Y - 4, 8, 8);
                                g.DrawEllipse(penLine, _ps[s].X - 4, _ps[s].Y - 4, 8, 8);
                                b.Dispose();
                            }

                        using (var brush = new SolidBrush(Color.FromArgb(190, 255, 255, 255)))
                        {
                            for (int r = 1; r < 5; r++)
                            {
                                g.DrawString((_step * r).ToString("N0"), font,
                                             brush, _xm - 8, _ym - 6 + r * _maxR / 5);
                            }

                            g.DrawString((_maxLevel).ToString("N0"), font,
                                         brush, _xm - 8, _ym - 11 + _maxR);
                        }

                        g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

                        using (var brushBlack = new SolidBrush(Color.Black))
                        {
                            for (int s = 0; s < levelIndices.Length; s++)
                            {
                                double angle = AnglePerStat * s - AngleOffset;
                                g.DrawString(Utils.StatName(levelIndices[s], true), font,
                                             brushBlack, _xm - 9 + (int)((_maxR + 10) * Math.Cos(angle)),
                                             _ym - 5 + (int)((_maxR + 10) * Math.Sin(angle)));
                            }
                        }
                    }

            Image?.Dispose();
            Image = bmp;
        }
Beispiel #6
0
        public void setLevels(int[] levels)
        {
            if (levels != null && levels.Length > 6 && maxR > 5)
            {
                Bitmap   bmp = new Bitmap(Width, Height);
                Graphics g   = Graphics.FromImage(bmp);
                g.SmoothingMode = SmoothingMode.AntiAlias;

                const double angleSeven = Math.PI / 3.5;
                const double offset     = Math.PI / 2;

                // the indices of the displayed stats
                var levelIndices = new int[] { (int)StatNames.Health, (int)StatNames.Stamina, (int)StatNames.Oxygen, (int)StatNames.Food, (int)StatNames.Weight, (int)StatNames.MeleeDamageMultiplier, (int)StatNames.SpeedMultiplier };

                for (int s = 0; s < levelIndices.Length; s++)
                {
                    if (levels[levelIndices[s]] != oldLevels[s])
                    {
                        oldLevels[s] = levels[levelIndices[s]];
                        int r = oldLevels[s] * maxR / maxLevel;
                        if (r < 0)
                        {
                            r = 0;
                        }
                        if (r > maxR)
                        {
                            r = maxR;
                        }
                        double angle = angleSeven * s - offset;
                        ps[s] = new Point(xm + (int)(r * Math.Cos(angle)), ym + (int)(r * Math.Sin(angle)));
                    }
                }
                g.FillEllipse(grBrushBG, xm - maxR, ym - maxR, 2 * maxR + 1, 2 * maxR + 1);

                Pen penL = new Pen(Color.FromArgb(128, 255, 255, 255));
                g.FillPolygon(grBrushFG, ps.ToArray());
                g.DrawPolygon(penL, ps.ToArray());

                double stepFactor = (double)step / maxLevel;
                for (int r = 0; r < 5; r++)
                {
                    g.DrawEllipse(new Pen(Utils.GetColorFromPercent((int)(100 * r * stepFactor), -0.4)),
                                  (int)(xm - maxR * r * stepFactor), (int)(ym - maxR * r * stepFactor),
                                  (int)(2 * maxR * r * stepFactor + 1), (int)(2 * maxR * r * stepFactor + 1));
                }
                g.DrawEllipse(new Pen(Utils.GetColorFromPercent(100, -0.4)), xm - maxR, ym - maxR, 2 * maxR + 1, 2 * maxR + 1);

                Pen pen = new Pen(Color.Black);
                for (int s = 0; s < levelIndices.Length; s++)
                {
                    pen.Width = 1;
                    pen.Color = Color.Gray;
                    g.DrawLine(pen, xm, ym, maxPs[s].X, maxPs[s].Y);
                    Color cl = Utils.GetColorFromPercent(100 * oldLevels[s] / maxLevel);
                    pen.Color = cl;
                    pen.Width = 3;
                    g.DrawLine(pen, xm, ym, ps[s].X, ps[s].Y);
                    Brush b = new SolidBrush(cl);
                    g.FillEllipse(b, ps[s].X - 4, ps[s].Y - 4, 8, 8);
                    g.DrawEllipse(penL, ps[s].X - 4, ps[s].Y - 4, 8, 8);
                    b.Dispose();
                }
                for (int r = 1; r < 5; r++)
                {
                    g.DrawString((step * r).ToString("N0"), new Font("Microsoft Sans Serif", 8f), new SolidBrush(Color.FromArgb(190, 255, 255, 255)), xm - 8, ym - 6 + r * maxR / 5);
                }
                g.DrawString((maxLevel).ToString("N0"), new Font("Microsoft Sans Serif", 8f), new SolidBrush(Color.FromArgb(190, 255, 255, 255)), xm - 8, ym - 11 + maxR);

                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                for (int s = 0; s < levelIndices.Length; s++)
                {
                    double angle = angleSeven * s - offset;
                    g.DrawString(Utils.StatName(levelIndices[s], true), new Font("Microsoft Sans Serif", 8f), new SolidBrush(Color.Black), xm - 9 + (int)((maxR + 10) * Math.Cos(angle)), ym - 5 + (int)((maxR + 10) * Math.Sin(angle)));
                }

                g.Dispose();

                Image = bmp;
            }
        }