XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

AutoMiningScript

【SpaceEngineer】全自动挖矿脚本

公开
关注 0 Fork 0 Star 0
UTF-8
using System;
using System.Collections.Generic;
using System.Text;
using Sandbox.ModAPI.Ingame;
using VRage.Game.ModAPI.Ingame.Utilities;

namespace AutoMiningScript
{
    public partial class Program : MyGridProgram
    {
        public class Settings
        {
            readonly MyIni ini;
            public readonly string Id, FleetId, BaseId, Role;
            public string BlockName => Role=="fleet"?L.CommonFleetBlockName:L.F(L.CommonNodeBlockName,Id);
            public Settings(string source, string defaultRole, string defaultId)
            {
                ini = new MyIni(); MyIniParseResult result;
                if (!string.IsNullOrWhiteSpace(source) && !ini.TryParse(source, out result)) throw Data.Invalid(L.F(L.CommonCustomDataInvalid, result.ToString()));
                NormalizeIni(ini);
                Role = defaultRole;
                Id = Text("System", "Id", defaultId); FleetId = Text("System", "FleetId", "mining"); BaseId = Text("System", "BaseId", "base");
                if (Id.Length == 0 || FleetId.Length == 0 || BaseId.Length == 0 || Id.Length > 64 || FleetId.Length > 64) throw Data.Invalid(L.CommonIdLength);
                if (Id.IndexOf('\n') >= 0 || FleetId.IndexOf('\n') >= 0) throw Data.Invalid(L.CommonIdNewline);
            }
            public string Text(string section, string key, string fallback) => ini.ContainsKey(section, key) ? Data.Text(ini,section, key).Trim() : fallback;
            public double Number(string section, string key, double fallback, double min, double max)
            => ReadNumber(ini,section,key,fallback,min,max);
            public static double ReadNumber(MyIni ini,string section,string key,double fallback,double min,double max)
            {
                var value = fallback;
                if (ini.ContainsKey(section, key) && !Data.TryNumber(Data.Text(ini,section, key), out value)) throw Data.Invalid(L.F(L.CommonNumberInvalid, SectionName(section), ParameterName(key)));
                CheckRange(section, key, value, min, max);
                return value;
            }
            public int Integer(string section, string key, int fallback, int min, int max)
            {
                long value = fallback;
                if (ini.ContainsKey(section, key) && !Data.TryLong(Data.Text(ini,section, key), out value)) throw Data.Invalid(L.F(L.CommonIntegerInvalid, SectionName(section), ParameterName(key)));
                CheckRange(section, key, value, min, max); return (int)value;
            }
            static void CheckRange(string section,string key,double value,double min,double max)
            { if (value < min || value > max) throw Data.Invalid(L.F(L.CommonConfigRange, SectionName(section), ParameterName(key), min, max)); }
            public bool Flag(string section, string key, bool fallback)
            => ReadFlag(ini,section,key,fallback);
            public static bool ReadFlag(MyIni ini,string section,string key,bool fallback)
            {
                if (!ini.ContainsKey(section, key)) return fallback;
                bool value; if (!bool.TryParse(Data.Text(ini,section, key), out value)) throw Data.Invalid(L.F(L.CommonConfigBool, SectionName(section), ParameterName(key))); return value;
            }
            // Only the user-facing boundary is translated. Protocol and save identifiers
            // never pass through these maps; arbitrary IDs and block names stay verbatim.
            sealed class Aliases
            {
                public readonly Dictionary<string,string> Read = new Dictionary<string,string>(StringComparer.OrdinalIgnoreCase);
                readonly Dictionary<string,string> write = new Dictionary<string,string>(StringComparer.OrdinalIgnoreCase);
                public Aliases(string source)
                {
                    foreach (var row in source.Split('|'))
                    {
                        var pair = row.Split('='); string name = pair[0], local = pair.Length > 1 ? pair[1] : name;
                        Read[name] = name; Read[local] = name;
                        if (!write.ContainsKey(name)) write[name] = local;
                    }
                }
                public string Code(string value) { string result; return Read.TryGetValue(value, out result) ? result : value; }
                public string Local(string value) { string result; return write.TryGetValue(value, out result) ? result : value; }
            }
            static readonly Aliases sections = new Aliases(L.ParamSections), keys = new Aliases(L.ParamKeys),
                pages = new Aliases(L.ParamPages), fonts = new Aliases(L.ParamFonts), ores = new Aliases(L.ParamOres), flags = new Aliases(L.ParamFlags),
                commands = new Aliases(L.ParamCommands), pageCommands = new Aliases(L.ParamPageCommands), names = new Aliases(L.ParamDefaultNames);
            public static string DefaultFleetId => names.Local("mining");
            public static string DefaultBaseId => names.Local("base");
            public static string DefaultId(string role, long entityId) => role == "fleet" ? DefaultBaseId : names.Local(role) + "-" + entityId;
            public static string DefaultName(string value) => names.Local(value);
            public static string ParameterName(string key) => keys.Local(key);
            public static string SectionName(string section) => Section(section, true);
            static string Section(string section, bool local)
            {
                var value = local ? sections.Local(section) : sections.Code(section);
                if (value != section) return value;
                int dot = section.LastIndexOf('.'), index;
                if (dot > 0 && int.TryParse(section.Substring(dot + 1), out index) && index >= 0)
                {
                    var prefix = section.Substring(0, dot);
                    if (sections.Code(prefix) == "AMS.Screen") return (local ? sections.Local("AMS.Screen") : "AMS.Screen") + section.Substring(dot);
                }
                return value;
            }
            static Aliases Values(string section, string key)
            {
                if ((section == "Display" || section == "AMS.Screen" || section.StartsWith("AMS.Screen.", Data.Ordinal)) && (key == "Page" || key == "BuiltInPage")) return pages;
                if (key == "Font") return fonts;
                if (key == "Enabled" || key == "BuiltIn" || key == "AutoPage" || key == "UsesHydrogen" || key == "AutoCalibrate") return flags;
                if ((key == "TargetOres" || key == "PriorityOres") && section == "Mining") return ores;
                return null;
            }
            static string Value(string section, string key, string value, bool local)
            {
                var map = Values(section, key); if (map == null) return value;
                if (map != ores) return local ? map.Local(value) : map.Code(value);
                var items = value.Split(',');
                for (int n = 0; n < items.Length; n++) items[n] = local ? map.Local(items[n].Trim()) : map.Code(items[n].Trim());
                return string.Join(",", items);
            }
            public static string ValueName(string section, string key, string value) => Value(section, key, value, true);
            public static void NormalizeIni(MyIni source)
            {
                var original = Data.CreateList<MyIniKey>(); source.GetKeys(original);
                var normalized = Data.CreateDictionary<MyIniKey,string>(); var origins = Data.CreateDictionary<MyIniKey,MyIniKey>();
                foreach (var field in original)
                {
                    var section = Section(field.Section, false);
                    if (!sections.Read.ContainsKey(section) && !section.StartsWith("AMS.Screen.", Data.Ordinal)) continue;
                    // Old role entries cannot override the compiled script or conflict with aliases.
                    if (section == "System" && (field.Name.Equals("Role", Data.IgnoreCase) || field.Name == L.ParamLegacyRoleKey)) continue;
                    string key = keys.Code(field.Name), value = Value(section, key, source.Get(field).ToString().Trim(), false);
                    var target = new MyIniKey(section, key); string previous;
                    if (normalized.TryGetValue(target, out previous) && previous != value)
                        throw Data.Invalid(L.F(L.ParamConflict, origins[target].Section, origins[target].Name, field.Section, field.Name));
                    normalized[target] = value; origins[target] = field;
                }
                // Check the whole input before modifying anything, including screen INI.
                foreach (var field in original)
                {
                    var section = Section(field.Section, false);
                    if (sections.Read.ContainsKey(section) || section.StartsWith("AMS.Screen.", Data.Ordinal)) source.Delete(field);
                }
                foreach (var pair in normalized) source.Set(pair.Key, pair.Value);
            }
            public static string NormalizeCommand(string command)
            {
                if (string.IsNullOrWhiteSpace(command)) return "";
                command = command.Trim(); int space = 0;
                while (space < command.Length && !char.IsWhiteSpace(command[space])) space++;
                var verb = commands.Code(command.Substring(0, space)).ToLowerInvariant();
                var tail = command.Substring(space).Trim();
                if (verb == "page") tail = pageCommands.Code(pages.Code(tail));
                else if (tail == L.ParamAll) tail = "all";
                return verb + (tail.Length > 0 ? " " + tail : "");
            }
            static string LocalTemplate(string source)
            {
                var result = new StringBuilder(); var section = "";
                foreach (var line in source.Split('\n'))
                {
                    if (line.StartsWith("[", Data.Ordinal) && line.EndsWith("]", Data.Ordinal))
                    { section = line.Substring(1, line.Length - 2); result.Append("[").Append(SectionName(section)).Append("]"); }
                    else
                    {
                        int equal = line.IndexOf('=');
                        if (equal > 0 && !line.StartsWith(";", Data.Ordinal))
                        { var key = line.Substring(0, equal); result.Append(ParameterName(key)).Append('=').Append(ValueName(section, key, line.Substring(equal + 1))); }
                        else result.Append(line);
                    }
                    result.Append('\n');
                }
                return result.ToString();
            }
            public static string Template(string role, string id)
            => LocalTemplate("; " + L.CommonTemplateIntro + "\n[System]\nId=" + id + "\nFleetId=" + DefaultFleetId + "\nBaseId=" + DefaultBaseId + "\n\n[Hardware]\nAutoCalibrate=true\n\n[Flight]\n; " + L.CommonTemplateMass + "\nDepartureMass=0\n\n[Mining]\n; " + L.CommonTemplateOres + "\nTargetOres=\n; " + L.CommonTemplatePriority + "\nPriorityOres=Iron,Cobalt,Nickel,Silicon\nFootprintWidth=6\nFootprintHeight=6\nOverlap=0.15\nHoleRadius=3\nDrillSpeed=0.5\nSortieDepthBudget=15\nMaxEntryRelief=5\nApproachDistance=20\n\n[Survey]\nBaseClearance=80\nSpacing=60\nExpansionWidth=60\nExpansionHeight=60\nMaxDepth=100\nMaxJobs=2048\n\n[Energy]\nReturnBattery=0.25\nChargeTarget=0.90\n\n[Cargo]\nReturnFill=0.95\nDepartureFill=0.05\n\n[Dock]\nMaxBaseSpeed=5\nMaxBaseAngularDeg=0.2\nMaxBaseAcceleration=0.1\nMaxTelemetryAge=0.5\nApproachDistance=30\nFinalSpeed=0.5\n\n[Fleet]\nDockGroup=" + DefaultName("AMS Docks") + "\nCargoGroup=" + DefaultName("AMS Cargo") + "\nMaxMiners=16\n\n[Display]\nTag=" + DefaultName("[AMS LCD]") + "\nPage=fleet\nBuiltIn=true\nBuiltInPage=config\nFont=Debug\nOfflineSeconds=5\nAutoPage=true\nPageInterval=8\nManualHoldSeconds=20\n\n[Watchdog]\nEnabled=false\nTimeout=3\n");
        }
    }
}