diff --git a/DevShortcuts/DevShortcuts.cs b/DevShortcuts/DevShortcuts.cs index 432e292..f1e4f03 100644 --- a/DevShortcuts/DevShortcuts.cs +++ b/DevShortcuts/DevShortcuts.cs @@ -14,7 +14,6 @@ public class DevShortcuts : BaseUnityPlugin { _cfgEnabled = Config.Bind("General", "Enabled", _cfgEnabled, "enable/disable this plugin").Value; if (!_cfgEnabled) return; - Logger.LogInfo($"Plugin {PluginInfo.PLUGIN_GUID} is loaded!"); Harmony.CreateAndPatchAll(typeof(DevShortcuts)); } diff --git a/LogisticMiner/LogisticMiner.cs b/LogisticMiner/LogisticMiner.cs index f5e73dc..dd45444 100644 --- a/LogisticMiner/LogisticMiner.cs +++ b/LogisticMiner/LogisticMiner.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using BepInEx; using HarmonyLib; using Random = UnityEngine.Random; @@ -10,36 +9,45 @@ namespace LogisticMiner; [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] public class LogisticMiner : BaseUnityPlugin { - private new static readonly BepInEx.Logging.ManualLogSource Logger = BepInEx.Logging.Logger.CreateLogSource(PluginInfo.PLUGIN_NAME); + private new static readonly BepInEx.Logging.ManualLogSource Logger = + BepInEx.Logging.Logger.CreateLogSource(PluginInfo.PLUGIN_NAME); - private static int _veinEnergyRatio = 200000; - private static int _waterEnergyRatio = 50000; + private static long _oreEnergyConsume = 2000000; + private static long _oilEnergyConsume = 3600000; + private static long _waterEnergyConsume = 20000000; private static int _waterSpeed = 100; private static int _miningScale = 100; private static float _frame; private static float _miningCostRate; private static uint _miningCostBarrier; + private static uint _miningCostBarrierOil; private static uint _seed = (uint)Random.Range(int.MinValue, int.MaxValue); - private static readonly Dictionary> Veins = new(); - private static readonly Dictionary FrameNext = new(); + private static readonly Dictionary PlanetVeinCacheData = new(); private bool _cfgEnabled = true; private void Awake() { _cfgEnabled = Config.Bind("General", "Enabled", _cfgEnabled, "enable/disable this plugin").Value; - _veinEnergyRatio = Config.Bind("General", "EnergyConsumptionEachVein", _veinEnergyRatio / 1000, "200 for default. Energy consumption for each vein(in kJ)").Value * 1000; - _waterEnergyRatio = Config.Bind("General", "EnergyConsumptionEachWater", _waterEnergyRatio / 1000, "50 for default. Energy consumption for each water(in kJ)").Value * 1000; - _waterSpeed = Config.Bind("General", "WaterMiningSpeed", _waterSpeed, "100 for default. Water mining speed (count per second)").Value; - _miningScale = Config.Bind("General", "MiningScale", _miningScale, "100 for default. Must not be less than 100. Mining scale(in percents) for slots nearly empty (mining scale will slowly reduce to 1 till reach half of slot limits)").Value; + _oreEnergyConsume = Config.Bind("General", "EnergyConsumptionForOre", _oreEnergyConsume / 2000, + "Energy consumption for each ore vein group(in kW)").Value * 2000; + _oilEnergyConsume = Config.Bind("General", "EnergyConsumptionForOil", _oilEnergyConsume / 2000, + "Energy consumption for each oil seep(in kW)").Value * 2000; + _waterEnergyConsume = Config.Bind("General", "EnergyConsumptionForWater", _waterEnergyConsume / 2000, + "Energy consumption for water slot(in kW)").Value * 2000; + _waterSpeed = Config.Bind("General", "WaterMiningSpeed", _waterSpeed, + "Water mining speed (count per second)").Value; + _miningScale = Config.Bind("General", "MiningScale", _miningScale, + "Must not be less than 100. Mining scale(in percents) for slots nearly empty, and the scale reduces to 1 smoothly till reach half of slot limits. Please note that the power consumption increases by the square of the scale which is the same as Advanced Mining Machine") + .Value; if (_miningScale < 100) { _miningScale = 100; } + if (!_cfgEnabled) return; - Logger.LogInfo($"Plugin {PluginInfo.PLUGIN_GUID} is loaded!"); Harmony.CreateAndPatchAll(typeof(LogisticMiner)); } @@ -49,9 +57,12 @@ public class LogisticMiner : BaseUnityPlugin private static void OnGameStart() { Logger.LogInfo("Game Start"); - FrameNext.Clear(); + PlanetVeinCacheData.Clear(); _frame = 0f; - Veins.Clear(); + /* codes reserved for future use: storage max may affect mining scale + _localStationMax = LDB.items.Select(2103).prefabDesc.stationMaxItemCount; + _remoteStationMax = LDB.items.Select(2104).prefabDesc.stationMaxItemCount; + */ } [HarmonyPostfix] @@ -63,13 +74,14 @@ public class LogisticMiner : BaseUnityPlugin { return; } + _frame++; if (_frame <= 1000000f) return; /* keep precision of floats by limiting them <= 1000000f */ _frame -= 1000000f; - foreach(var key in FrameNext.Keys.ToList()) + foreach (var pair in PlanetVeinCacheData) { - FrameNext[key] -= 1000000f; + pair.Value.FrameNext -= 1000000f; } } @@ -84,18 +96,18 @@ public class LogisticMiner : BaseUnityPlugin private static void RecalcVeins(PlanetFactory factory) { - VeinData[] veinPool = factory.veinPool; var planetId = factory.planetId; /* remove planet veins from dict */ - Veins.Keys.Where(key => (key >> 16) == planetId).ToList().ForEach(key => Veins.Remove(key)); - /* re-add all veins to dict */ - for (var i = 0; i < veinPool.Length; i++) + if (PlanetVeinCacheData.TryGetValue(planetId, out var vcd)) { - var veinData = veinPool[i]; - if (veinData.amount > 0 && veinData.type > EVeinType.None) - { - AddVeinData(planetId, veinData.productId, i); - } + vcd.GenVeins(factory); + } + else + { + vcd = new VeinCacheData(); + vcd.GenVeins(factory); + vcd.FrameNext = _frame + 120f / GameMain.history.miningSpeedScale; + PlanetVeinCacheData.Add(planetId, vcd); } } @@ -104,31 +116,37 @@ public class LogisticMiner : BaseUnityPlugin private static void Miner(FactorySystem __instance) { var history = GameMain.history; - var miningSpeedScale = history.miningSpeedScale; - if (miningSpeedScale <= 0f) + var miningSpeedScalef = history.miningSpeedScale; + var miningSpeedScale = (long)(miningSpeedScalef * 100); + if (miningSpeedScale <= 0) return; var factory = __instance.factory; var planetId = factory.planetId; - if (FrameNext.TryGetValue(planetId, out var frameNext)) + if (PlanetVeinCacheData.TryGetValue(planetId, out var vcd)) { - if (frameNext > _frame) + if (vcd.FrameNext > _frame) return; } else { - FrameNext[planetId] = _frame + 60f / miningSpeedScale; + PlanetVeinCacheData[planetId] = new VeinCacheData + { + FrameNext = _frame + 120f / miningSpeedScalef + }; return; } - var miningFrames = 60f / miningSpeedScale; + var miningFrames = 120f / miningSpeedScalef; var miningCostRate = history.miningCostRate; if (!miningCostRate.Equals(_miningCostRate)) { _miningCostRate = miningCostRate; _miningCostBarrier = (uint)(int)Math.Ceiling(2147483646.0 * miningCostRate); + _miningCostBarrierOil = + (uint)(int)Math.Ceiling(2147483646.0 * miningCostRate * 0.401116669f / + factory.gameData.gameDesc.resourceMultiplier); } - var key0 = planetId << 16; - var veinPool = factory.veinPool; + var planetTransport = __instance.planet.factory.transport; var factoryProductionStat = GameMain.statistics.production.factoryStatPool[__instance.factory.index]; @@ -152,127 +170,214 @@ public class LogisticMiner : BaseUnityPlugin stationStore.max <= stationStore.count) continue; - var isVein = Veins.TryGetValue(key0 | stationStore.itemId, out var val); + var isVein = vcd.HasVein(stationStore.itemId); var isVeinOrWater = isVein || stationStore.itemId == __instance.planet.waterItemId; if (!isVeinOrWater) continue; int amount; - int energyRatio; + long energyConsume; + isCollecting = true; + var miningScale = _miningScale; + if (miningScale > 100) + { + if (stationStore.count * 2 < stationStore.max) + miningScale = 100 + + ((miningScale - 100) * (stationStore.max - stationStore.count * 2) + + stationStore.max - 1) / stationStore.max; + else + miningScale = 100; + } + if (isVein) { - if (stationComponent.energy < _veinEnergyRatio) + (amount, energyConsume) = vcd.Mine(factory, stationStore.itemId, miningScale, miningSpeedScale, + stationComponent.energy); + if (amount < 0) { - isCollecting = true; k = int.MaxValue - 1; continue; } - - if (veinPool[val.First()].type == EVeinType.Oil) - amount = (int)val - .Where(item => item < veinPool.Length && veinPool[item].type > EVeinType.None) - .Sum(item => veinPool[item].amount * VeinData.oilSpeedMultiplier * 2f); - else - amount = val.Sum( - item => GetMine(veinPool, item, __instance.planet.factory) ? 1 : 0); - energyRatio = _veinEnergyRatio; } else { - if (stationComponent.energy < _waterEnergyRatio) + energyConsume = (_waterEnergyConsume * miningScale * miningScale + 9999L) / 100L / + miningSpeedScale; + if (stationComponent.energy < energyConsume) { - isCollecting = true; k = int.MaxValue - 1; continue; } - amount = _waterSpeed; - energyRatio = _waterEnergyRatio; - } - - if (amount == 0) continue; - isCollecting = true; - var energyConsume = (int)Math.Ceiling(energyRatio * amount / miningSpeedScale); - if (energyConsume > stationComponent.energy) - { - amount = (int)(stationComponent.energy * miningSpeedScale / energyRatio); - energyConsume = (int)Math.Ceiling(energyRatio * amount / miningSpeedScale); + amount = _waterSpeed * miningScale / 100; } + if (amount <= 0) continue; stationStore.count += amount; if (factoryProductionStat != null) productRegister[stationStore.itemId] += amount; stationComponent.energy -= energyConsume; } - if (isCollecting && stationComponent.energyMax > stationComponent.energy * 2) - { - var index = storage.Length - 2; - var fuelCount = storage[index].count; - if (fuelCount > 0) - { - var heatValue = LDB.items.Select(storage[index].itemId).HeatValue; - if (heatValue > 0) - { - var count = (int)((stationComponent.energyMax - stationComponent.energy) / - heatValue); - if (count > fuelCount) - count = fuelCount; - storage[index].count -= count; - stationComponent.energy += count * heatValue; - } - } - } + if (!isCollecting || stationComponent.energy * 2 >= stationComponent.energyMax) continue; + var index = stationComponent.isStellar ? storage.Length - 2 : storage.Length - 1; + var fuelCount = storage[index].count; + if (fuelCount == 0) continue; + var heatValue = LDB.items.Select(storage[index].itemId).HeatValue; + if (heatValue <= 0) continue; + var count = (int)((stationComponent.energyMax - stationComponent.energy) / + heatValue); + if (count > fuelCount) + count = fuelCount; + storage[index].count -= count; + stationComponent.energy += count * heatValue; } - frameNext += miningFrames; - } while (frameNext <= _frame); + vcd.FrameNext += miningFrames; + } while (vcd.FrameNext <= _frame); + PerformanceMonitor.EndSample(ECpuWorkEntry.Miner); - FrameNext[planetId] = frameNext; } - private static void AddVeinData(int planetId, int productId, int index) + private class VeinCacheData { - var key = (planetId << 16) + productId; - if (Veins.TryGetValue(key, out var val)) - val.Add(index); - else - Veins.Add(key, new List { index }); - } + public float FrameNext; + /* stores list of indices to veinData, with an extra INT which indicates cout of veinGroups at last */ + private Dictionary> _veins = new(); + private int _mineIndex = -1; - private static bool GetMine(VeinData[] veinDatas, int index, PlanetFactory factory) - { - if (veinDatas.Length == 0 || veinDatas[index].type == EVeinType.None) - return false; - - short groupIndex; - if (veinDatas[index].amount > 0) + public bool HasVein(int productId) { - bool flag = true; - if (_miningCostBarrier < 2147483646u) - { - _seed = (uint)((int)((ulong)((_seed % 2147483646u + 1) * 48271L) % 2147483647uL) - 1); - flag = _seed < _miningCostBarrier; - } - - if (flag) - { - veinDatas[index].amount--; - factory.veinGroups[veinDatas[index].groupIndex].amount--; - if (veinDatas[index].amount <= 0) - { - groupIndex = veinDatas[index].groupIndex; - factory.veinGroups[groupIndex].count--; - factory.RemoveVeinWithComponents(index); - factory.RecalculateVeinGroup(groupIndex); - } - } - - return true; + return _veins.ContainsKey(productId); } - groupIndex = veinDatas[index].groupIndex; - factory.veinGroups[groupIndex].count--; - factory.RemoveVeinWithComponents(index); - factory.RecalculateVeinGroup(groupIndex); - return false; + public void GenVeins(PlanetFactory factory) + { + _veins = new Dictionary>(); + var veinPool = factory.veinPool; + var vg = new Dictionary>(); + for (var i = 0; i < veinPool.Length; i++) + { + if (veinPool[i].amount <= 0 || veinPool[i].type == EVeinType.None) continue; + var productId = veinPool[i].productId; + if (_veins.TryGetValue(productId, out var l)) + { + l.Add(i); + } + else + { + _veins.Add(productId, new List { i }); + } + + if (vg.TryGetValue(productId, out var hs)) + { + hs.Add(veinPool[i].groupIndex); + } + else + { + vg.Add(productId, new HashSet { veinPool[i].groupIndex }); + } + } + + foreach (var pair in vg) + { + _veins[pair.Key].Add(pair.Value.Count); + } + } + + public (int, long) Mine(PlanetFactory factory, int productId, int percent, long miningSpeedScale, + long energyMax) + { + if (!_veins.TryGetValue(productId, out var veins)) + { + return (-1, -1L); + } + + uint barrier; + int limit; + int count; + long energy; + var length = veins.Count - 1; + /* if is Oil */ + if (productId == 1007) + { + energy = (_oilEnergyConsume * length * percent * percent + 9999L) / 100L / miningSpeedScale; + if (energy > energyMax) + return (-1, -1L); + float countf = 0f; + var veinsPool = factory.veinPool; + for (var i = 0; i < length; i++) + { + ref var vd = ref veinsPool[veins[i]]; + countf += vd.amount * 4 * VeinData.oilSpeedMultiplier; + } + + count = ((int)countf * percent + 99) / 100; + if (count == 0) + return (-1, -1L); + barrier = _miningCostBarrierOil; + limit = 2500; + } + else + { + count = (length * percent + 99) / 100; + if (count == 0) + return (-1, -1L); + energy = (_oreEnergyConsume * veins[length] * percent * percent + 9999L) / 100L / miningSpeedScale; + if (energy > energyMax) + return (-1, -1L); + barrier = _miningCostBarrier; + limit = 0; + } + + var veinsData = factory.veinPool; + var total = 0; + for (; count > 0; count--) + { + _mineIndex = (_mineIndex + 1) % length; + var index = veins[_mineIndex]; + ref var vd = ref veinsData[index]; + int groupIndex; + if (vd.amount > 0) + { + total++; + if (vd.amount > limit) + { + var consume = true; + if (barrier < 2147483646u) + { + _seed = (uint)((int)((ulong)((_seed % 2147483646u + 1) * 48271L) % 2147483647uL) - 1); + consume = _seed < barrier; + } + + if (consume) + { + vd.amount--; + groupIndex = vd.groupIndex; + factory.veinGroups[groupIndex].amount--; + if (vd.amount <= 0) + { + factory.veinGroups[groupIndex].count--; + factory.RemoveVeinWithComponents(index); + factory.RecalculateVeinGroup(groupIndex); + if (!_veins.TryGetValue(productId, out veins)) + break; + length = veins.Count - 1; + } + } + } + + continue; + } + + groupIndex = vd.groupIndex; + factory.veinGroups[groupIndex].count--; + factory.RemoveVeinWithComponents(index); + factory.RecalculateVeinGroup(groupIndex); + if (!_veins.TryGetValue(productId, out veins)) + break; + length = veins.Count - 1; + } + + return (total, energy); + } } } diff --git a/README.md b/README.md index 00d6585..d954d01 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,3 @@ * The same speed as normal Mining Machine for normal ores and 100/s for water. * Energy costs: 200kJ for each vein or oil, 50kJ for each water. `Veins Utilization` upgrades does not increase power consumption. * `Veins Utilization` upgrades affects mining speed and ore consume as normal. -### TODO -* Make Oil mining logic the same as normal (reduce speed periodically) -* Support mining scale in config \ No newline at end of file