1
0
mirror of https://github.com/soarqin/DSP_Mods.git synced 2025-12-09 00:53:39 +08:00

Compare commits

11 Commits

Author SHA1 Message Date
09cdaf3a12 balance tweaks 2025-10-30 16:03:11 +08:00
8d5bb140e1 fix crashes 2025-10-30 00:12:47 +08:00
c99c59a117 work in progress 2025-10-29 22:30:10 +08:00
61811f9a8c crash fix 2025-10-29 21:51:52 +08:00
cf3117e0da minor opt 2025-10-29 21:38:53 +08:00
dfd34601cf update for new game update 2025-10-29 20:54:41 +08:00
6934607fca WIP for logistic_hub 2025-10-29 20:45:21 +08:00
be9de43492 fix issue 2025-10-29 20:22:23 +08:00
2dbf017a5e update LogisticMiner to be compatible with latest game update 2025-10-29 15:27:53 +08:00
3c7744047c update Game dll 2025-10-29 15:27:23 +08:00
a9959a2f07 try to fix a strange issue 2025-10-29 15:27:13 +08:00
18 changed files with 984 additions and 574 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -4,7 +4,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UXAssist", "UXAssist\UXAssi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CheatEnabler", "CheatEnabler\CheatEnabler.csproj", "{F9F16B62-D1D3-466B-BE22-E64B9EA957C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LogisticMiner", "LogisticMiner\LogisticMiner.csproj", "{7149D717-C913-4153-9425-38CB9D087F83}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LogisticHub", "LogisticHub\LogisticHub.csproj", "{7149D717-C913-4153-9425-38CB9D087F83}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HideTips", "HideTips\HideTips.csproj", "{4EABD71D-477F-448B-801B-48F8745A3FA7}"
EndProject

View File

@@ -0,0 +1,57 @@
using System;
using System.Reflection;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UXAssist.Common;
namespace LogisticHub;
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
public class LogisticHub : BaseUnityPlugin
{
public new static readonly BepInEx.Logging.ManualLogSource Logger =
BepInEx.Logging.Logger.CreateLogSource(PluginInfo.PLUGIN_NAME);
private Type[] _modules;
private void Awake()
{
Module.Miner.Enabled = Config.Bind("Miner", "Enabled", true, "enable/disable this plugin");
Module.Miner.OreEnergyConsume = Config.Bind("Miner", "EnergyConsumptionForOre", 2000000L,
"Energy consumption for each ore vein group(in W)");
Module.Miner.OreMiningMultiplier = Config.Bind("Miner", "OreMiningMultiplier", 3,
new ConfigDescription("Mining multiplier for ore veins, multiplies to the number of veins in the group", new AcceptableValueRange<int>(1, 100)));
Module.Miner.OreMiningScale = Config.Bind("Miner", "OreMiningScale", 100,
"""
0 for Auto(which means having researched advanced mining machine makes mining scale 300, otherwise 100).
Mining scale(in percents) for slots below half of slot limits, and the scale reduces to 100% smoothly till reach full.
Please note that the power consumption increases by the square of the scale which is the same as Advanced Mining Machine.
""");
Module.Miner.OilEnergyConsume = Config.Bind("Miner", "EnergyConsumptionForOil", 1800000L,
"Energy consumption for each oil seep(in W)");
Module.Miner.WaterEnergyConsume = Config.Bind("Miner", "EnergyConsumptionForWater", 2000000L,
"Energy consumption for water slot(in W)");
Module.Miner.WaterSpeed = Config.Bind("Miner", "WaterMiningSpeed", 10,
"Water mining speed (count per second)");
Module.Miner.FuelIlsSlot = Config.Bind("Miner", "ILSFuelSlot", 4,
new ConfigDescription("Fuel slot for ILS, set 0 to disable.", new AcceptableValueRange<int>(0, 5)));
Module.Miner.FuelPlsSlot = Config.Bind("Miner", "PLSFuelSlot", 4,
new ConfigDescription("Fuel slot for PLS, set 0 to disable.", new AcceptableValueRange<int>(0, 4)));
_modules = Util.GetTypesFiltered(Assembly.GetExecutingAssembly(),
t => string.Equals(t.Namespace, "LogisticHub.Module", StringComparison.Ordinal));
_modules?.Do(type => type.GetMethod("Init")?.Invoke(null, null));
Harmony.CreateAndPatchAll(typeof(LogisticHub));
}
private void Start()
{
_modules?.Do(type => type.GetMethod("Start")?.Invoke(null, null));
}
private void OnDestroy()
{
_modules?.Do(type => type.GetMethod("Uninit")?.Invoke(null, null));
}
}

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<BepInExPluginGuid>org.soardev.logistichub</BepInExPluginGuid>
<Description>DSP MOD - LogisticHub</Description>
<Version>0.1.0</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<RestoreAdditionalProjectSources>https://nuget.bepinex.dev/v3/index.json</RestoreAdditionalProjectSources>
<PackageId>LogisticHub</PackageId>
<RootNamespace>LogisticHub</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BepInEx.Core" Version="5.*" />
<PackageReference Include="BepInEx.PluginInfoProps" Version="1.*" />
<PackageReference Include="UnityEngine.Modules" Version="2022.3.53" IncludeAssets="compile" />
<!-- <PackageReference Include="DysonSphereProgram.GameLibs" Version="0.10.32.*-r.*" /> -->
</ItemGroup>
<ItemGroup>
<Reference Include="Assembly-CSharp">
<HintPath>..\AssemblyFromGame\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>..\AssemblyFromGame\UnityEngine.UI.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UXAssist\UXAssist.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,33 @@
using System.Linq;
namespace LogisticHub.Module;
using UXAssist.Common;
public static class AuxData
{
public static (long, bool)[] Fuels;
public static void Init()
{
GameLogic.OnDataLoaded += () =>
{
var maxId = LDB.items.dataArray.Select(data => data.ID).Prepend(0).Max();
Fuels = new (long, bool)[maxId + 1];
foreach (var data in LDB.items.dataArray)
Fuels[data.ID] = (data.HeatValue, data.Productive);
};
}
public static int AlignUpToPowerOf2(int n)
{
if (n < 16) return 16;
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n + 1;
}
}

View File

345
LogisticHub/Module/Miner.cs Normal file
View File

@@ -0,0 +1,345 @@
using System;
using BepInEx.Configuration;
using HarmonyLib;
using UXAssist.Common;
using Random = UnityEngine.Random;
using GameLogicProc = UXAssist.Common.GameLogic;
namespace LogisticHub.Module;
public class Miner : PatchImpl<Miner>
{
public static ConfigEntry<bool> Enabled;
public static ConfigEntry<long> OreEnergyConsume;
public static ConfigEntry<int> OreMiningScale;
public static ConfigEntry<int> OreMiningMultiplier;
public static ConfigEntry<long> OilEnergyConsume;
public static ConfigEntry<long> WaterEnergyConsume;
public static ConfigEntry<int> WaterSpeed;
public static ConfigEntry<int> FuelIlsSlot;
public static ConfigEntry<int> FuelPlsSlot;
private static long _frame;
private static float _miningCostRateByTech;
private static long _miningFrames;
private static long _miningSpeedScaleLong;
private static bool _advancedMiningMachineUnlocked;
private static int _miningMultiplier = 3;
private static int _miningScale = 100;
private static uint _miningCostBarrier;
private static uint _miningCostBarrierOil;
private static int[] _mineIndex;
private static uint _miningSeed = (uint)Random.Range(0, int.MaxValue);
private static readonly (int, int)[] VeinList =
[
(0, 1000),
(1, 1001),
(2, 1002),
(3, 1003),
(4, 1004),
(5, 1005),
(6, 1006),
(7, 1007),
(11, 1011),
(12, 1012),
(13, 1013),
(14, 1014),
(15, 1015),
(16, 1016)
];
public static void Init()
{
Enabled.SettingChanged += (_, _) => { Enable(Enabled.Value); };
Enable(Enabled.Value);
}
public static void Uninit()
{
Enable(false);
}
protected override void OnEnable()
{
GameLogicProc.OnGameBegin += OnGameBegin;
}
protected override void OnDisable()
{
GameLogicProc.OnGameBegin -= OnGameBegin;
}
private static void OnGameBegin()
{
_frame = 0L;
UpdateMiningCostRate();
UpdateSpeedScale();
CheckRecipes();
}
private static int SplitIncLevel(ref int n, ref int m, int p)
{
var level = m / n;
var left = m - level * n;
n -= p;
left -= n;
m -= left > 0 ? level * p + left : level * p;
return level;
}
private static void CheckRecipes()
{
_advancedMiningMachineUnlocked = GameMain.history.recipeUnlocked.Contains(119);
_miningMultiplier = OreMiningMultiplier.Value;
if (OreMiningScale.Value == 0)
{
_miningScale = _advancedMiningMachineUnlocked ? 300 : 100;
}
else
{
_miningScale = OreMiningScale.Value;
}
}
private static void UpdateMiningCostRate()
{
_miningCostRateByTech = GameMain.history.miningCostRate;
_miningCostBarrier = (uint)(int)Math.Ceiling(2147483646.0 * _miningCostRateByTech);
_miningCostBarrierOil = (uint)(int)Math.Ceiling(2147483646.0 * _miningCostRateByTech * 0.401116669f / Math.Max(DSPGame.GameDesc.resourceMultiplier, 0.416666657f));
}
private static void UpdateSpeedScale()
{
_miningSpeedScaleLong = (long)Math.Round(GameMain.history.miningSpeedScale * 100f);
_miningFrames = _miningSpeedScaleLong * 6000L;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameHistoryData), nameof(GameHistoryData.UnlockTechFunction))]
private static void OnUnlockTech(int func)
{
switch (func)
{
case 20:
UpdateMiningCostRate();
break;
case 21:
UpdateSpeedScale();
break;
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(GameLogic), nameof(GameLogic.OnFactoryFrameBegin))]
private static void GameLogic_OnFactoryFrameBegin_Prefix()
{
var main = GameMain.instance;
if (main.isMenuDemo) return;
if (_miningSpeedScaleLong <= 0L) return;
if (main.timei % 60 != 0) return;
_frame += _miningFrames;
var frameCounter = _frame / 1200000L;
if (frameCounter <= 0) return;
_frame -= frameCounter * 1200000L;
DeepProfiler.BeginSample(DPEntry.Miner);
var data = GameMain.data;
if (_mineIndex == null || data.factoryCount > _mineIndex.Length)
Array.Resize(ref _mineIndex, data.factoryCount);
var factoryStatPool = GameMain.statistics.production.factoryStatPool;
for (var factoryIndex = data.factoryCount - 1; factoryIndex >= 0; factoryIndex--)
{
var factory = data.factories[factoryIndex];
var veins = VeinManager.GetVeins(factoryIndex);
if (veins == null) continue;
var stations = StationManager.GetStations(factoryIndex);
var planetTransport = factory.transport;
var productRegister = factoryStatPool[factoryIndex]?.productRegister;
var demands = stations.StorageIndices[1];
if (demands == null) continue;
foreach (var (itemIndex, itemId) in VeinList)
{
if (itemIndex >= demands.Length) continue;
var demandList = demands[itemIndex];
if (demandList == null) continue;
foreach (var storageIndex in demandList)
{
var station = planetTransport.stationPool[storageIndex / 100];
if (station == null)
continue;
ref var storage = ref station.storage[storageIndex % 100];
if (storage.count >= storage.max) continue;
int amount;
long energyConsume;
var miningScale = _miningScale;
if (miningScale > 100 && storage.count * 2 > storage.max)
{
miningScale = 100 + ((miningScale - 100) * (storage.max - storage.count) * 2 + storage.max - 1) / storage.max;
}
if (itemIndex > 0)
{
(amount, energyConsume) = Mine(factory, veins, itemId, miningScale, (int)frameCounter, _miningMultiplier, station.energy);
if (amount < 0) continue;
}
else
{
energyConsume = (WaterEnergyConsume.Value * frameCounter * miningScale * miningScale + 9999L) / 10000L;
if (station.energy < energyConsume) continue;
amount = WaterSpeed.Value * miningScale / 100;
}
if (amount <= 0) continue;
storage.count += amount;
if (productRegister != null)
productRegister[itemId] += amount;
station.energy -= energyConsume;
}
}
for (var i = planetTransport.stationCursor - 1; i > 0; i--)
{
var stationComponent = planetTransport.stationPool[i];
if (stationComponent == null || stationComponent.isCollector || stationComponent.isVeinCollector || stationComponent.energy * 2 >= stationComponent.energyMax) continue;
var index = (stationComponent.isStellar ? FuelIlsSlot.Value : FuelPlsSlot.Value) - 1;
var storage = stationComponent.storage;
if (index < 0 || index >= storage.Length)
continue;
var fuelCount = storage[index].count;
if (fuelCount == 0) continue;
var (heat, prod) = AuxData.Fuels[storage[index].itemId];
if (heat <= 0)
continue;
/* Sprayed fuels */
int pretendIncLevel;
if (prod && (pretendIncLevel = storage[index].inc / storage[index].count) > 0)
{
var count = (int)((stationComponent.energyMax - stationComponent.energy) * 1000L / Cargo.incTable[pretendIncLevel] / 7L);
if (count > fuelCount)
count = fuelCount;
var incLevel = SplitIncLevel(ref storage[index].count, ref storage[index].inc, count);
if (incLevel > 10)
incLevel = 10;
stationComponent.energy += heat * count * (1000L + Cargo.incTable[incLevel]) / 1000L;
}
else
{
var count = (int)((stationComponent.energyMax - stationComponent.energy) / heat);
if (count > fuelCount)
count = fuelCount;
SplitIncLevel(ref storage[index].count, ref storage[index].inc, count);
stationComponent.energy += heat * count;
}
}
}
DeepProfiler.EndSample(DPEntry.Miner);
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameHistoryData), nameof(GameHistoryData.UnlockRecipe))]
private static void OnUnlockRecipe(int recipeId)
{
if (recipeId == 119)
{
CheckRecipes();
}
}
private static (int, long) Mine(PlanetFactory factory, ProductVeinData[] allVeins, int productId, int percent, int counter, int multiplier, long energyMax)
{
var veins = allVeins[productId - 1000];
if (veins == null)
return (-1, -1L);
var veinIndices = veins.VeinIndices;
uint barrier;
int limit;
int count;
long energy;
var length = veinIndices.Count - 1;
/* if is Oil */
if (productId == 1007)
{
energy = OilEnergyConsume.Value * length;
if (energy > energyMax)
return (-1, -1L);
var countf = 0f;
var veinsPool = factory.veinPool;
for (var i = length; i > 0; i--)
{
countf += veinsPool[veinIndices[i]].amount * 4 * VeinData.oilSpeedMultiplier;
}
count = (int)(countf * (counter * percent) / 100f);
if (count == 0)
return (-1, -1L);
barrier = _miningCostBarrierOil;
limit = 2500;
}
else
{
count = (length * multiplier * counter * percent + 99) / 100;
if (count == 0)
return (-1, -1L);
energy = (OreEnergyConsume.Value * veins.GroupCount * percent * percent + 9999L) / 10000L;
if (energy > energyMax)
return (-1, -1L);
barrier = _miningCostBarrier;
limit = 0;
}
var veinsData = factory.veinPool;
var total = 0;
var factoryIndex = factory.index;
var mineIndex = _mineIndex[factoryIndex];
for (; count > 0; count--)
{
mineIndex = mineIndex % length + 1;
var index = veinIndices[mineIndex];
ref var vd = ref veinsData[index];
int groupIndex;
if (vd.amount <= 0)
{
groupIndex = vd.groupIndex;
factory.veinGroups[groupIndex].count--;
factory.RemoveVeinWithComponents(index);
factory.RecalculateVeinGroup(groupIndex);
length = veinIndices.Count - 1;
if (length <= 0) break;
continue;
}
total++;
if (vd.amount <= limit) continue;
var consume = true;
if (barrier < 2147483646u)
{
_miningSeed = (uint)((int)((ulong)((_miningSeed % 2147483646u + 1) * 48271L) % 2147483647uL) - 1);
consume = _miningSeed < barrier;
}
if (!consume) continue;
vd.amount--;
groupIndex = vd.groupIndex;
factory.veinGroups[groupIndex].amount--;
if (vd.amount > 0) continue;
factory.veinGroups[groupIndex].count--;
factory.RemoveVeinWithComponents(index);
factory.RecalculateVeinGroup(groupIndex);
length = veinIndices.Count - 1;
if (length <= 0) break;
}
_mineIndex[factoryIndex] = mineIndex;
return (total, energy);
}
}

View File

@@ -0,0 +1,325 @@
using System;
using System.Collections.Generic;
using HarmonyLib;
using UXAssist.Common;
using GameLogicProc = UXAssist.Common.GameLogic;
namespace LogisticHub.Module;
public class PlanetStations
{
public readonly List<int>[][] StorageIndices = [null, null];
public void AddStationStorage(bool demand, int id, int stationId, int storageIdx)
{
LogisticHub.Logger.LogDebug($"AddStationStorage: demand={demand}, id={id}, stationId={stationId}, storageIdx={storageIdx}");
var stations = StorageIndices[demand ? 1 : 0];
if (stations == null || id >= stations.Length)
{
Array.Resize(ref stations, AuxData.AlignUpToPowerOf2(id + 1));
StorageIndices[demand ? 1 : 0] = stations;
}
var list = stations[id];
if (list == null)
{
list = [];
stations[id] = list;
}
var value = stationId * 100 + storageIdx;
var index = list.BinarySearch(value);
if (index < 0)
list.Insert(~index, value);
}
public void RemoveStationStorage(bool demand, int id, int stationId, int storageIdx)
{
var stations = StorageIndices[demand ? 1 : 0];
if (stations == null || id >= stations.Length)
return;
var list = stations[id];
if (list == null)
return;
var value = stationId * 100 + storageIdx;
var index = list.BinarySearch(value);
if (index >= 0)
list.RemoveAt(index);
}
}
public class StationManager : PatchImpl<StationManager>
{
private static PlanetStations[] _stations;
public static void Init()
{
GameLogicProc.OnGameBegin += OnGameBegin;
Enable(true);
}
public static void Uninit()
{
GameLogicProc.OnGameBegin -= OnGameBegin;
Enable(false);
}
private static void OnGameBegin()
{
_stations = null;
var data = GameMain.data;
for (var index = data.factoryCount - 1; index >= 0; index--)
{
var factory = data.factories[index];
if (factory == null || factory.index != index) continue;
var planetIndex = factory.index;
var stations = StationsByPlanet(planetIndex);
var transport = factory.transport;
var pool = transport.stationPool;
for (var i = transport.stationCursor - 1; i > 0; i--)
{
var station = pool[i];
if (station == null || station.id != i || station.isCollector || station.isVeinCollector) continue;
UpdateStationInfo(stations, station);
}
}
}
private static int ItemIdToIndex(int itemId)
{
return itemId switch
{
>= 1000 and < 2000 => itemId - 1000,
>= 5000 and < 5050 => itemId - 5000 + 900,
>= 6000 and < 6050 => itemId - 6000 + 950,
_ => -1
};
}
public static int IndexToItemId(int index)
{
return index switch
{
< 900 => index + 1000,
< 950 => index - 900 + 5000,
< 1000 => index - 950 + 6000,
_ => -1
};
}
public static PlanetStations GetStations(int planetIndex)
{
return _stations != null && planetIndex < _stations.Length ? _stations[planetIndex] : null;
}
private static PlanetStations StationsByPlanet(int planetIndex)
{
if (_stations == null || _stations.Length <= planetIndex)
Array.Resize(ref _stations, AuxData.AlignUpToPowerOf2(planetIndex + 1));
var stations = _stations[planetIndex];
if (stations != null) return stations;
stations = new PlanetStations();
_stations[planetIndex] = stations;
return stations;
}
private static void DebugLog()
{
for (var idx = 0; idx < _stations.Length; idx++)
{
var stations = _stations[idx];
if (stations == null) continue;
LogisticHub.Logger.LogDebug($"Planet {idx}:");
for (var i = 0; i < 2; i++)
{
var storage = stations.StorageIndices[i];
if (storage == null) continue;
LogisticHub.Logger.LogDebug(i == 1 ? " Demand:" : " Supply:");
for (var j = 0; j < storage.Length; j++)
{
var list = storage[j];
if (list == null) continue;
var count = list.Count;
if (count <= 0) continue;
var itemId = IndexToItemId(j);
LogisticHub.Logger.LogDebug($" {itemId}: {string.Join(", ", list)}");
}
}
}
}
private static void UpdateStationInfo(PlanetStations stations, StationComponent station, int storageIdx = -1)
{
var storage = station.storage;
var stationId = station.id;
if (storageIdx >= 0)
{
if (storageIdx >= storage.Length) return;
var itemId = ItemIdToIndex(storage[storageIdx].itemId);
if (itemId <= 0) return;
var logic = storage[storageIdx].localLogic;
switch (logic)
{
case ELogisticStorage.Demand:
stations.AddStationStorage(true, itemId, stationId, storageIdx);
break;
case ELogisticStorage.Supply:
stations.AddStationStorage(false, itemId, stationId, storageIdx);
break;
case ELogisticStorage.None:
default:
break;
}
return;
}
for (var i = storage.Length - 1; i >= 0; i--)
{
var itemId = ItemIdToIndex(storage[i].itemId);
if (itemId <= 0) continue;
var logic = storage[i].localLogic;
switch (logic)
{
case ELogisticStorage.Demand:
stations.AddStationStorage(true, itemId, stationId, i);
break;
case ELogisticStorage.Supply:
stations.AddStationStorage(false, itemId, stationId, i);
break;
case ELogisticStorage.None:
default:
break;
}
}
}
private static void RemoveStationInfo(PlanetStations stations, StationComponent station, int storageIdx = -1)
{
var storage = station.storage;
var stationId = station.id;
if (storageIdx >= 0)
{
var itemId = ItemIdToIndex(storage[storageIdx].itemId);
if (itemId <= 0) return;
var logic = storage[storageIdx].localLogic;
switch (logic)
{
case ELogisticStorage.Demand:
stations.RemoveStationStorage(true, itemId, stationId, storageIdx);
break;
case ELogisticStorage.Supply:
stations.RemoveStationStorage(false, itemId, stationId, storageIdx);
break;
case ELogisticStorage.None:
default:
break;
}
return;
}
for (var i = storage.Length - 1; i >= 0; i--)
{
var itemId = ItemIdToIndex(storage[i].itemId);
if (itemId <= 0) continue;
var logic = storage[i].localLogic;
switch (logic)
{
case ELogisticStorage.Demand:
stations.RemoveStationStorage(true, itemId, stationId, i);
break;
case ELogisticStorage.Supply:
stations.RemoveStationStorage(false, itemId, stationId, i);
break;
case ELogisticStorage.None:
default:
break;
}
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.Init))]
private static void PlanetTransport_Init_Postfix(PlanetTransport __instance)
{
var factory = __instance.factory;
var planetIndex = factory.index;
if (_stations == null || _stations.Length <= planetIndex)
Array.Resize(ref _stations, AuxData.AlignUpToPowerOf2(planetIndex + 1));
var stations = new PlanetStations();
_stations[planetIndex] = stations;
var pool = __instance.stationPool;
for (var i = __instance.stationCursor - 1; i > 0; i--)
{
var station = pool[i];
if (station == null || station.id != i || station.isCollector || station.isVeinCollector) continue;
UpdateStationInfo(stations, station);
}
// DebugLog();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(BuildingParameters), nameof(BuildingParameters.ApplyPrebuildParametersToEntity))]
private static void BuildingParameters_ApplyPrebuildParametersToEntity_Postfix(int entityId, PlanetFactory factory)
{
if (entityId <= 0) return;
ref var entity = ref factory.entityPool[entityId];
var stationId = entity.stationId;
if (stationId <= 0) return;
var station = factory.transport.stationPool[stationId];
if (station == null || station.id != stationId || station.isCollector || station.isVeinCollector) return;
UpdateStationInfo(StationsByPlanet(factory.index), station);
// DebugLog();
}
[HarmonyPrefix]
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.RemoveStationComponent))]
private static void PlanetTransport_RemoveStationComponent_Prefix(PlanetTransport __instance, int id)
{
if (id <= 0) return;
var station = __instance.stationPool[id];
if (station == null || station.id != id || station.isCollector || station.isVeinCollector) return;
RemoveStationInfo(StationsByPlanet(__instance.factory.index), station);
// DebugLog();
}
[HarmonyPrefix]
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.SetStationStorage))]
private static void PlanetTransport_SetStationStorage_Prefix(PlanetTransport __instance, int stationId, int storageIdx, int itemId, ELogisticStorage localLogic, out bool __state)
{
var station = __instance.stationPool[stationId];
if (station == null || station.id != stationId || station.isCollector || station.isVeinCollector || storageIdx < 0 || storageIdx >= station.storage.Length)
{
__state = false;
return;
}
ref var storage = ref station.storage[storageIdx];
var oldItemId = storage.itemId;
var oldLocalLogic = storage.localLogic;
if (localLogic == oldLocalLogic && itemId == oldItemId)
{
__state = false;
return;
}
if (oldItemId > 0 && oldLocalLogic != ELogisticStorage.None)
RemoveStationInfo(StationsByPlanet(__instance.factory.index), station, storageIdx);
__state = localLogic != ELogisticStorage.None;
// if (!__state) DebugLog();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.SetStationStorage))]
private static void PlanetTransport_SetStationStorage_Postfix(PlanetTransport __instance, int stationId, int storageIdx, bool __state)
{
if (!__state) return;
var station = __instance.stationPool[stationId];
UpdateStationInfo(StationsByPlanet(__instance.factory.index), station, storageIdx);
// DebugLog();
}
}

View File

@@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using HarmonyLib;
using UXAssist.Common;
using GameLogicProc = UXAssist.Common.GameLogic;
namespace LogisticHub.Module;
public class ProductVeinData
{
public readonly List<int> VeinIndices = [];
public readonly HashSet<int> GroupIndices = [];
public int GroupCount = 0;
}
public class VeinManager : PatchImpl<VeinManager>
{
private static ProductVeinData[][] _veins;
public static void Init()
{
Enable(true);
GameLogicProc.OnGameBegin += OnGameBegin;
}
public static void Uninit()
{
GameLogicProc.OnGameBegin -= OnGameBegin;
Enable(false);
}
private static void OnGameBegin()
{
_veins = null;
var data = GameMain.data;
for (var index = data.factoryCount - 1; index >= 0; index--)
{
var factory = data.factories[index];
if (factory == null || factory.index != index) continue;
RecalcVeins(factory);
}
}
public static ProductVeinData[] GetVeins(int planetIndex)
{
if (_veins == null || _veins.Length <= planetIndex)
return null;
return _veins[planetIndex];
}
private static ProductVeinData[] GetOrCreateVeins(int planetIndex)
{
if (_veins == null || _veins.Length <= planetIndex)
Array.Resize(ref _veins, AuxData.AlignUpToPowerOf2(planetIndex + 1));
var veins = _veins[planetIndex];
if (veins != null) return veins;
veins = new ProductVeinData[20];
_veins[planetIndex] = veins;
return veins;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlanetFactory), nameof(PlanetFactory.Init))]
[HarmonyPatch(typeof(PlanetFactory), nameof(PlanetFactory.RecalculateVeinGroup))]
[HarmonyPatch(typeof(PlanetFactory), nameof(PlanetFactory.RecalculateAllVeinGroups))]
private static void PlanetFactory_RecalculateAllVeinGroups_Postfix(PlanetFactory __instance)
{
RecalcVeins(__instance);
}
private static void DebugLog()
{
foreach (var veins in _veins)
{
if (veins == null) continue;
for (var i = 0; i < veins.Length; i++)
{
var pvd = veins[i];
if (pvd == null) continue;
LogisticHub.Logger.LogInfo($"Product {i} VeinTypeCount={pvd.VeinIndices.Count} GroupCount={pvd.GroupCount}");
}
}
}
private static void RecalcVeins(PlanetFactory factory)
{
var planetIndex = factory.index;
var veins = GetOrCreateVeins(planetIndex);
var veinPool = factory.veinPool;
foreach (var pvd in veins)
{
if (pvd == null) continue;
pvd.VeinIndices.Clear();
pvd.GroupIndices.Clear();
pvd.GroupCount = 0;
}
for (var i = factory.veinCursor - 1; i > 0; i--)
{
if (veinPool[i].id != i || veinPool[i].amount <= 0 || veinPool[i].type == EVeinType.None) continue;
var productId = veinPool[i].productId - 1000;
if (productId is < 0 or >= 20) continue;
var pvd = veins[productId];
if (pvd == null)
{
pvd = new ProductVeinData();
veins[productId] = pvd;
}
pvd.VeinIndices.Add(i);
pvd.GroupIndices.Add(veinPool[i].groupIndex);
}
foreach (var pvd in veins)
{
if (pvd == null) continue;
pvd.GroupCount = pvd.GroupIndices.Count;
}
// DebugLog();
}
}

47
LogisticHub/README.md Normal file
View File

@@ -0,0 +1,47 @@
# LogisticHub
#### Cheat functions for Logistic Storages, make them mine resources on the planet and exchange items from certain buildings
#### 物流塔作弊功能,使其可以在星球上采矿并与特定建筑物交换物品
## Usage
* Miner(and fuel burning) functions
+ Inspired by [PlanetMiner](https://dsp.thunderstore.io/package/blacksnipebiu/PlanetMiner)([github](https://github.com/blacksnipebiu/PlanetMiner))
But it is heavily optimized to resolve performance, accuracy and other issues in PlanetMiner.
+ (Optimization to PlanetMiner) Only recalculate count of veins when vein chunks are changed (added/removed by foundations/Sandbox-Mode, or exhausted), so this removes Dictionary allocation on each planet for every frame.
+ (Optimization to PlanetMiner) More accurate frame counting by use float number.
+ (Optimization to PlanetMiner) Does not increase power consumptions on `Veins Utilization` upgrades.
+ (Optimization to PlanetMiner) Separate power consumptions for veins, oil seeps and water.
+ (Optimization to PlanetMiner) Power consumptions are counted by groups of veins and count of oil seeps, which is more sensible.
+ Can burn fuels in certain slot when energy below half of max.
- (Optimization to PlanetMiner) Sprayed fuels generates extra energy as normal.
+ (Optimization to PlanetMiner) All used parameters are configurable:
- LogisticHub has the same speed as normal Mining Machine for normal ores by default.
But you can set mining scale in configuration, which makes LogisticHub working like Advance Mining Machines:
power consumption increases by the square of the scale, and gradually decrease mining speed over half of the maximum count.
This applies to all of veins, oils and water.
Mining scale can be set to 0(by default), which means it is automatically set by tech unlocks, set to 300 when you reaches Advanced Mining Machine, otherwise 100.
- 100/s for water by default.
- Energy costs: 1MW/vein-group & 10MW/water-slot & 1.8MW/oil-seep(configurable)
- Fuels burning slot. Default: 4th for ILS and PLS. Set to 0 to disable it.
## 使用说明
* 采矿(和燃料燃烧)功能
+ 创意来自 [PlanetMiner](https://dsp.thunderstore.io/package/blacksnipebiu/PlanetMiner)([github](https://github.com/blacksnipebiu/PlanetMiner))
对性能重度优化并解决了PlanetMiner的精度等问题。
+ (对PlanetMiner的优化) 仅当矿堆发生变化(填埋/恢复/采完)时重新计算矿堆数据,解决每行星每计算帧要重建字典的性能问题。
+ (对PlanetMiner的优化) 用浮点数保证更精确的帧计算。
+ (对PlanetMiner的优化) 升级`矿物利用`不会提升能耗。
+ (对PlanetMiner的优化) 分开矿物,油井和水的采集能耗。
+ (对PlanetMiner的优化) 采集能耗以矿物组,油井为单位,相对更加合理。
+ 剩余电量少于一半时可以燃烧指定格子的燃料补充。
- (对PlanetMiner的优化) 喷涂了增产剂的燃料按照正常的计算方式提供更多的能量(除了原本就不增加能量输出的反物质燃料棒)。
+ (对PlanetMiner的优化) 所有参数都可以在设置文件内配置:
- 物流塔枢纽和普通矿机采矿速度一样(等同于同时采集所有对应矿物)。
你可以设置采矿倍率改变物流塔枢纽采矿速度,和高级采矿机相同地,能耗和倍率的平方成正比,并且在存储矿物量多于一半时逐渐降低采矿倍率。
此倍率对各种矿物,油井和水的采集都生效。
倍率可以设置为0(默认)此时倍率会随科技解锁而变化默认是100%解锁高级采矿机后变为300%。
- 水的采集速度默认为100/s。
- 能耗:每矿物组 1MW单格水 10MW每油井 1.8MW。
- 燃料格位置。默认星际物流塔和行星内物流塔第4格。设为0则关闭燃料补充能量功能。

View File

@@ -0,0 +1,10 @@
{
"name": "LogisticHub",
"version_number": "0.1.0",
"website_url": "https://github.com/soarqin/DSP_Mods/tree/master/LogisticHub",
"description": "Cheat functions to Logistic Storages which make them miner and items hub / 物流塔作弊功能,使其成为矿机和物品枢纽",
"dependencies": [
"xiaoye97-BepInEx-5.4.17",
"soarqin-UXAssist-1.2.0"
]
}

View File

@@ -1,485 +0,0 @@
using System;
using System.Collections.Generic;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Random = UnityEngine.Random;
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 static long _oreEnergyConsume = 2000000;
private static long _oilEnergyConsume = 3600000;
private static long _waterEnergyConsume = 20000000;
private static int _waterSpeed = 100;
private static int _miningScale;
private static int _fuelIlsSlot = 3;
private static int _fuelPlsSlot = 2;
private static float _frame;
private static float _miningCostRateByTech;
private static float _miningSpeedScaleByTech;
private static float _miningFrames;
private static long _miningSpeedScaleLong;
private static bool _advancedMiningMachineUnlocked;
private static uint _miningCostBarrier;
private static uint _miningCostBarrierOil;
private static uint _seed = (uint)Random.Range(int.MinValue, int.MaxValue);
private static readonly Dictionary<int, VeinCacheData> PlanetVeinCacheData = new();
private static readonly Dictionary<int, (long, bool)> Fuels = new();
private bool _cfgEnabled = true;
private void Awake()
{
_cfgEnabled = Config.Bind("General", "Enabled", _cfgEnabled, "enable/disable this plugin").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,
"0 for Auto(which means having researched makes mining scale 300, otherwise 100). Mining scale(in percents) for slots below half of slot limits, and the scale reduces to 100% smoothly till reach full. Please note that the power consumption increases by the square of the scale which is the same as Advanced Mining Machine")
.Value;
_fuelIlsSlot = Config.Bind("General", "ILSFuelSlot", _fuelIlsSlot + 1,
new ConfigDescription("Fuel slot for ILS, set to 0 to disable",
new AcceptableValueRange<int>(0, 5), Array.Empty<object>()))
.Value - 1;
_fuelPlsSlot = Config.Bind("General", "PLSFuelSlot", _fuelPlsSlot + 1,
new ConfigDescription("Fuel slot for PLS, set to 0 to disable",
new AcceptableValueRange<int>(0, 4), Array.Empty<object>()))
.Value - 1;
if (!_cfgEnabled) return;
Harmony.CreateAndPatchAll(typeof(LogisticMiner));
}
private static int SplitIncLevel(ref int n, ref int m, int p)
{
var level = m / n;
var left = m - level * n;
n -= p;
left -= n;
m -= left > 0 ? level * p + left : level * p;
return level;
}
private static void CheckRecipes()
{
_advancedMiningMachineUnlocked = GameMain.history.recipeUnlocked.Contains(119);
}
private static void UpdateMiningCostRate()
{
_miningCostRateByTech = GameMain.history.miningCostRate;
_miningCostBarrier = (uint)(int)Math.Ceiling(2147483646.0 * _miningCostRateByTech);
_miningCostBarrierOil =
(uint)(int)Math.Ceiling(2147483646.0 * _miningCostRateByTech * 0.401116669f /
DSPGame.GameDesc.resourceMultiplier);
}
private static void UpdateSpeedScale()
{
_miningSpeedScaleByTech = GameMain.history.miningSpeedScale;
_miningSpeedScaleLong = (long)(_miningSpeedScaleByTech * 100);
lock (PlanetVeinCacheData)
{
_miningFrames = 120f / _miningSpeedScaleByTech;
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(DSPGame), "StartGame", typeof(GameDesc))]
[HarmonyPatch(typeof(DSPGame), "StartGame", typeof(string))]
private static void OnGameStart()
{
Logger.LogInfo("Game Start");
PlanetVeinCacheData.Clear();
Fuels.Clear();
foreach (var data in LDB.items.dataArray)
{
if (data.HeatValue > 0)
{
Fuels.Add(data.ID, (data.HeatValue, data.Productive));
}
}
/* Thinking: storage max may affect mining scale?
_localStationMax = LDB.items.Select(2103).prefabDesc.stationMaxItemCount;
_remoteStationMax = LDB.items.Select(2104).prefabDesc.stationMaxItemCount;
*/
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameMain), "Start")]
private static void OnGameLoaded()
{
_frame = 0f;
UpdateMiningCostRate();
UpdateSpeedScale();
CheckRecipes();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameHistoryData), "UnlockTechFunction")]
private static void OnUnlockTech(int func)
{
switch (func)
{
case 20:
UpdateMiningCostRate();
break;
case 21:
UpdateSpeedScale();
break;
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameData), "GameTick")]
private static void FrameTick()
{
var main = GameMain.instance;
if (main.isMenuDemo)
{
return;
}
_frame++;
if (_frame <= 1000000f) return;
/* keep precision of floats by limiting them <= 1000000f */
_frame -= 1000000f;
foreach (var pair in PlanetVeinCacheData)
{
pair.Value.FrameNext -= 1000000f;
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameHistoryData), "UnlockRecipe")]
private static void OnUnlockRecipe(int recipeId)
{
if (recipeId == 119)
{
CheckRecipes();
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlanetFactory), "Init")]
[HarmonyPatch(typeof(PlanetFactory), "RecalculateVeinGroup")]
[HarmonyPatch(typeof(PlanetFactory), "RecalculateAllVeinGroups")]
private static void NeedRecalcVeins(PlanetFactory __instance)
{
RecalcVeins(__instance);
}
private static void RecalcVeins(PlanetFactory factory)
{
var planetId = factory.planetId;
lock (PlanetVeinCacheData)
{
/* remove planet veins from dict */
if (PlanetVeinCacheData.TryGetValue(planetId, out var vcd))
{
vcd.GenVeins(factory);
}
else
{
vcd = new VeinCacheData();
vcd.GenVeins(factory);
vcd.FrameNext = _frame + _miningFrames;
PlanetVeinCacheData.Add(planetId, vcd);
}
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(FactorySystem), "CheckBeforeGameTick")]
private static void FactorySystemLogisticMiner(FactorySystem __instance)
{
if (_miningSpeedScaleLong <= 0)
return;
var factory = __instance.factory;
var planetId = factory.planetId;
lock (PlanetVeinCacheData)
{
if (PlanetVeinCacheData.TryGetValue(planetId, out var vcd))
{
if (vcd.FrameNext > _frame)
return;
}
else
{
PlanetVeinCacheData[planetId] = new VeinCacheData
{
FrameNext = _frame + _miningFrames
};
return;
}
var planetTransport = __instance.planet.factory.transport;
var factoryProductionStat =
GameMain.statistics.production.factoryStatPool[__instance.factory.index];
var productRegister = factoryProductionStat?.productRegister;
PerformanceMonitor.BeginSample(ECpuWorkEntry.Miner);
do
{
for (var j = 1; j < planetTransport.stationCursor; j++)
{
var stationComponent = planetTransport.stationPool[j];
if (stationComponent == null) continue;
/* skip Orbital Collectors and Advanced Mining Machines */
if (stationComponent.isCollector || stationComponent.isVeinCollector) continue;
var storage = stationComponent.storage;
if (storage == null) continue;
var isCollecting = false;
for (var k = 0; k < stationComponent.storage.Length; k++)
{
ref var stationStore = ref storage[k];
if (stationStore.localLogic != ELogisticStorage.Demand ||
stationStore.max <= stationStore.count)
continue;
var isVein = vcd.HasVein(stationStore.itemId);
var isVeinOrWater = isVein || stationStore.itemId == __instance.planet.waterItemId;
if (!isVeinOrWater) continue;
int amount;
long energyConsume;
isCollecting = true;
var miningScale = _miningScale;
if (miningScale == 0)
{
miningScale = _advancedMiningMachineUnlocked ? 300 : 100;
}
if (miningScale > 100 && stationStore.count * 2 > stationStore.max)
{
miningScale = 100 +
((miningScale - 100) * (stationStore.max - stationStore.count) * 2 +
stationStore.max - 1) / stationStore.max;
}
if (isVein)
{
(amount, energyConsume) = vcd.Mine(factory, stationStore.itemId, miningScale,
_miningSpeedScaleLong,
stationComponent.energy);
if (amount < 0)
{
k = int.MaxValue - 1;
continue;
}
}
else
{
energyConsume = (_waterEnergyConsume * miningScale * miningScale + 9999L) / 100L /
_miningSpeedScaleLong;
if (stationComponent.energy < energyConsume)
{
k = int.MaxValue - 1;
continue;
}
amount = _waterSpeed * miningScale / 100;
}
if (amount <= 0) continue;
stationStore.count += amount;
if (factoryProductionStat != null)
productRegister[stationStore.itemId] += amount;
stationComponent.energy -= energyConsume;
}
if (!isCollecting || stationComponent.energy * 2 >= stationComponent.energyMax) continue;
var index = stationComponent.isStellar ? _fuelIlsSlot : _fuelPlsSlot;
if (index < 0 || index >= storage.Length)
continue;
var fuelCount = storage[index].count;
if (fuelCount == 0) continue;
if (!Fuels.TryGetValue(storage[index].itemId, out var val) || val.Item1 <= 0)
continue;
/* Sprayed fuels */
int pretendIncLevel;
if (val.Item2 && (pretendIncLevel = storage[index].inc / storage[index].count) > 0)
{
var count = (int)((stationComponent.energyMax - stationComponent.energy) * 1000L /
Cargo.incTable[pretendIncLevel] / 7L);
if (count > fuelCount)
count = fuelCount;
var incLevel = SplitIncLevel(ref storage[index].count, ref storage[index].inc, count);
if (incLevel > 10)
incLevel = 10;
stationComponent.energy += val.Item1 * count * (1000L + Cargo.incTable[incLevel]) / 1000L;
}
else
{
var count = (int)((stationComponent.energyMax - stationComponent.energy) / val.Item1);
if (count > fuelCount)
count = fuelCount;
SplitIncLevel(ref storage[index].count, ref storage[index].inc, count);
stationComponent.energy += val.Item1 * count;
}
}
vcd.FrameNext += _miningFrames;
} while (vcd.FrameNext <= _frame);
PerformanceMonitor.EndSample(ECpuWorkEntry.Miner);
}
}
private class VeinCacheData
{
public float FrameNext;
/* stores list of indices to veinData, with an extra INT which indicates cout of veinGroups at last */
private Dictionary<int, List<int>> _veins = new();
private int _mineIndex = -1;
public bool HasVein(int productId)
{
return _veins.ContainsKey(productId);
}
public void GenVeins(PlanetFactory factory)
{
_veins = new Dictionary<int, List<int>>();
var veinPool = factory.veinPool;
var vg = new Dictionary<int, HashSet<int>>();
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, [i]);
}
if (vg.TryGetValue(productId, out var hs))
{
hs.Add(veinPool[i].groupIndex);
}
else
{
vg.Add(productId, [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);
}
}
}

View File

@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<AssemblyName>LogisticMiner</AssemblyName>
<BepInExPluginGuid>org.soardev.logisticminer</BepInExPluginGuid>
<Description>DSP MOD - LogisticMiner</Description>
<Version>0.1.0</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<RestoreAdditionalProjectSources>https://nuget.bepinex.dev/v3/index.json</RestoreAdditionalProjectSources>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BepInEx.Core" Version="5.*" />
<PackageReference Include="BepInEx.PluginInfoProps" Version="1.*" />
<PackageReference Include="DysonSphereProgram.GameLibs" Version="0.10.32.*-r.*" />
<PackageReference Include="UnityEngine.Modules" Version="2018.4.12" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup>
</Project>

View File

@@ -1,50 +0,0 @@
# LogisticMiner
#### Logistic Storages can mine all ores/water on current planet
#### 物流塔可以采集当前星球的全部矿产(以及水)
## Usage
* Inspired
by [PlanetMiner](https://dsp.thunderstore.io/package/blacksnipebiu/PlanetMiner)([github](https://github.com/blacksnipebiu/PlanetMiner))
But it is heavily optimized to resolve performance, accuracy and other issues in PlanetMiner.
* (Optimization to PlanetMiner) Only recalculate count of veins when vein chunks are changed (added/removed by foundations/Sandbox-Mode, or
exhausted), so this removes Dictionary allocation on each planet for every frame.
* (Optimization to PlanetMiner) More accurate frame counting by use float number.
* (Optimization to PlanetMiner) Does not increase power consumptions on `Veins Utilization` upgrades.
* (Optimization to PlanetMiner) Separate power consumptions for veins, oil seeps and water.
* (Optimization to PlanetMiner) Power consumptions are counted by groups of veins and count of oil seeps, which is more sensible.
* Can burn fuels in certain slot when energy below half of max.
* (Optimization to PlanetMiner) Sprayed fuels generates extra energy as normal.
* (Optimization to PlanetMiner) All used parameters are configurable:
* Logistic Miner has the same speed as normal Mining Machine for normal ores by default.
But you can set mining scale in configuration, which makes Logistic Miner working like Advance Mining Machines:
power
consumption increases by the square of the scale, and gradually decrease mining speed over half of the maximum
count.
This applies to all of veins, oils and water.
Mining scale can be set to 0(by default), which means it is automatically set by tech unlocks, set to 300 when you
reaches Advanced Mining Machine, otherwise 100.
* 100/s for water by default.
* Energy costs: 1MW/vein-group & 10MW/water-slot & 1.8MW/oil-seep(configurable)
* Fuels burning slot. Default: 4th for ILS, 3rd for PLS. Set to 0 to disable it.
## 使用说明
* 创意来自 [PlanetMiner](https://dsp.thunderstore.io/package/blacksnipebiu/PlanetMiner)([github](https://github.com/blacksnipebiu/PlanetMiner))
对性能重度优化并解决了PlanetMiner的精度等问题。
* (对PlanetMiner的优化) 仅当矿堆发生变化(填埋/恢复/采完)时重新计算矿堆数据,解决每行星每计算帧要重建字典的性能问题。
* (对PlanetMiner的优化) 用浮点数保证更精确的帧计算。
* (对PlanetMiner的优化) 升级`矿物利用`不会提升能耗。
* (对PlanetMiner的优化) 分开矿物,油井和水的采集能耗。
* (对PlanetMiner的优化) 采集能耗以矿物组,油井为单位,相对更加合理。
* 剩余电量少于一半时可以燃烧指定格子的燃料补充。
* (对PlanetMiner的优化) 喷涂了增产剂的燃料按照正常的计算方式提供更多的能量(除了原本就不增加能量输出的反物质燃料棒)。
* (对PlanetMiner的优化) 所有参数都可以在设置文件内配置:
* 物流塔矿机和普通矿机采矿速度一样(等同于同时采集所有对应矿物)。
你可以设置采矿倍率改变物流塔矿机采矿速度,和高级采矿机相同地,能耗和倍率的平方成正比,并且在存储矿物量多于一半时逐渐降低采矿倍率。
此倍率对各种矿物,油井和水的采集都生效。
倍率可以设置为0(默认)此时倍率会随科技解锁而变化默认是100%解锁高级采矿机后变为300%。
* 水的采集速度默认为100/s。
* 能耗:每矿物组 1MW单格水 10MW每油井 1.8MW。
* 燃料格位置。默认星际物流塔第4格行星内物流塔第3格。设为0则关闭燃料补充能量功能。

View File

@@ -1,9 +0,0 @@
{
"name": "LogisticMiner",
"version_number": "0.1.0",
"website_url": "https://github.com/soarqin/DSP_Mods/tree/master/LogisticMiner",
"description": "Logistic Storages can mine all ores/water on current planet / 物流塔可以采集当前星球的全部矿产(以及水)",
"dependencies": [
"xiaoye97-BepInEx-5.4.17"
]
}

View File

@@ -9,10 +9,10 @@ Add various cheat functions while disabling abnormal determinants
Moved [to another repo](https://github.com/soarqin/DSP_Mods_TO/tree/master/CompressSave)
## [LogisticMiner](LogisticMiner)
## [LogisticHub](LogisticHub)
Logistic Storages can mine all ores/water on current planet
物流塔可以采集当前星球的全部矿产(以及水)
Cheat functions for Logistic Storages, make them mine resources on the planet and exchange items from certain buildings
物流塔作弊功能,使其可以在星球上采矿并与特定建筑物交换物品
## [HideTips](HideTips)

View File

@@ -1069,10 +1069,10 @@ public static class LogisticsPatch
var localPlanet = GameMain.data?.localPlanet;
if (localPlanet == null || !localPlanet.factoryLoaded)
{
_stationTipsRoot.SetActive(false);
if (_lastPlanetId == 0) return;
RecycleStationTips();
_lastPlanetId = 0;
_stationTipsRoot.SetActive(false);
return;
}