151 lines
12 KiB
C#
151 lines
12 KiB
C#
#nullable enable
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace Demo2.Domain
|
|
{
|
|
public interface IDemo2Mod
|
|
{
|
|
string Id { get; }
|
|
string Version { get; }
|
|
void Register(Demo2ContentRegistry registry);
|
|
}
|
|
|
|
public sealed class Demo2ModManifest
|
|
{
|
|
public string Id = string.Empty;
|
|
public string Version = string.Empty;
|
|
public string ContentSha256 = string.Empty;
|
|
}
|
|
|
|
public sealed class Demo2ContentRegistry
|
|
{
|
|
private readonly Dictionary<string, Demo2RuleDefinition> _rules = new(StringComparer.Ordinal);
|
|
private readonly Dictionary<int, Demo2RecipeDefinition> _recipes = new();
|
|
private readonly Dictionary<Demo2MachineKind, Demo2MachineDefinition> _machines = new();
|
|
private readonly List<Demo2WaveDefinition> _waves = new();
|
|
|
|
public IReadOnlyDictionary<string, Demo2RuleDefinition> Rules => _rules;
|
|
public IReadOnlyDictionary<int, Demo2RecipeDefinition> Recipes => _recipes;
|
|
public IReadOnlyDictionary<Demo2MachineKind, Demo2MachineDefinition> Machines => _machines;
|
|
public IReadOnlyList<Demo2WaveDefinition> Waves => _waves;
|
|
|
|
public void AddRule(Demo2RuleDefinition definition) => _rules.Add(definition.Id, definition);
|
|
public void AddRecipe(Demo2RecipeDefinition definition) => _recipes.Add(definition.Id, definition);
|
|
public void AddMachine(Demo2MachineDefinition definition) => _machines.Add(definition.Kind, definition);
|
|
public void AddWave(Demo2WaveDefinition definition) => _waves.Add(definition);
|
|
|
|
public string ComputeContentHash()
|
|
{
|
|
var text = new StringBuilder();
|
|
foreach (var rule in _rules.Values.OrderBy(value => value.Id, StringComparer.Ordinal))
|
|
{
|
|
text.Append(rule.Id).Append('|').Append(rule.Name).Append('|').Append((int)rule.Category);
|
|
foreach (var instruction in rule.Instructions)
|
|
text.Append(':').Append((int)instruction.Opcode).Append(',').Append((int)instruction.Condition).Append(',').Append(instruction.Operand);
|
|
text.AppendLine();
|
|
}
|
|
foreach (var recipe in _recipes.Values.OrderBy(value => value.Id))
|
|
text.Append(recipe.Id).Append('|').Append(recipe.Name).Append('|')
|
|
.Append((int)recipe.InputA).Append(',').Append(recipe.InputCountA).Append('|')
|
|
.Append((int)recipe.InputB).Append(',').Append(recipe.InputCountB).Append('|')
|
|
.Append((int)recipe.Output).Append(',').Append(recipe.OutputCount).Append('|').Append(recipe.ProcessTicks).AppendLine();
|
|
foreach (var machine in _machines.Values.OrderBy(value => (int)value.Kind))
|
|
text.Append("machine|").Append((int)machine.Kind).Append('|').Append(machine.Name).Append('|')
|
|
.Append(machine.Cost).Append('|').Append(machine.RecipeId).Append('|').Append(machine.Capacity).AppendLine();
|
|
foreach (var wave in _waves.OrderBy(value => value.Cycle))
|
|
text.Append("wave|").Append(wave.Cycle).Append('|').Append(wave.TotalHealth).Append('|').Append(wave.Trait).AppendLine();
|
|
using var sha = SHA256.Create();
|
|
var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(text.ToString()));
|
|
var hex = new StringBuilder(bytes.Length * 2);
|
|
for (var i = 0; i < bytes.Length; i++) hex.Append(bytes[i].ToString("x2"));
|
|
return hex.ToString();
|
|
}
|
|
}
|
|
|
|
public static class Demo2BuiltInContent
|
|
{
|
|
public static Demo2ContentRegistry Create()
|
|
{
|
|
var registry = new Demo2ContentRegistry();
|
|
AddMachinesAndRecipes(registry);
|
|
AddWaves(registry);
|
|
foreach (var rule in BuildRules()) registry.AddRule(rule);
|
|
return registry;
|
|
}
|
|
|
|
public static IReadOnlyList<Demo2RuleDefinition> BuildRules() => new[]
|
|
{
|
|
Rule("production.triple-echo", "三连复写", "连续完成三次相同配方后复制下一件产物。", Demo2RuleCategory.Production, Demo2RuleOpcode.DuplicateNextOutput, 1, Demo2RuleCondition.ThreeSameRecipes),
|
|
Rule("production.hot-ingot", "灼热锭", "无故障时,每周期增加 120 载荷。", Demo2RuleCategory.Production, Demo2RuleOpcode.AddPayload, 120, Demo2RuleCondition.NoFaults),
|
|
Rule("production.precision-gears", "精密齿轮", "首批齿轮使载荷提高 25%。", Demo2RuleCategory.Production, Demo2RuleOpcode.MultiplyPayload, 1250, Demo2RuleCondition.FirstBatch),
|
|
Rule("production.dense-ammo", "高密弹药", "结算载荷固定增加 180。", Demo2RuleCategory.Production, Demo2RuleOpcode.AddPayload, 180),
|
|
Rule("logistics.twin-stream", "双流共振", "铁矿与燃料相邻流动时,倍率增加 0.35。", Demo2RuleCategory.Logistics, Demo2RuleOpcode.AddMultiplier, 350, Demo2RuleCondition.IronFuelAdjacent),
|
|
Rule("logistics.clear-belt", "净空运输", "传送带占用低于 80% 时,倍率提高 20%。", Demo2RuleCategory.Logistics, Demo2RuleOpcode.MultiplyMultiplier, 1200, Demo2RuleCondition.BeltBelowEightyPercent),
|
|
Rule("logistics.split-balance", "均衡分流", "稳定分流为载荷增加 90。", Demo2RuleCategory.Logistics, Demo2RuleOpcode.AddPayload, 90),
|
|
Rule("logistics.long-haul", "长距惯性", "低占用且无故障时,倍率增加 0.2。", Demo2RuleCategory.Logistics, Demo2RuleOpcode.AddMultiplier, 200, Demo2RuleCondition.BeltBelowEightyPercent | Demo2RuleCondition.NoFaults),
|
|
Rule("machine.overclock", "红区超频", "载荷提高 50%,并增加 1 次故障。", Demo2RuleCategory.MachineState, new[]
|
|
{
|
|
Instruction(Demo2RuleOpcode.MultiplyPayload, 1500), Instruction(Demo2RuleOpcode.AddFaults, 1)
|
|
}),
|
|
Rule("machine.preventive-maintenance", "预防维护", "存在故障时修复 1 次并增加 60 载荷。", Demo2RuleCategory.MachineState, new[]
|
|
{
|
|
Instruction(Demo2RuleOpcode.RepairFaults, 1, Demo2RuleCondition.HasFaults), Instruction(Demo2RuleOpcode.AddPayload, 60, Demo2RuleCondition.HasFaults)
|
|
}),
|
|
Rule("machine.night-shift", "夜班协议", "无故障时倍率增加 0.25。", Demo2RuleCategory.MachineState, Demo2RuleOpcode.AddMultiplier, 250, Demo2RuleCondition.NoFaults),
|
|
Rule("machine.thermal-bank", "余热银行", "剩余能量每点转化 3 载荷。", Demo2RuleCategory.MachineState, Demo2RuleOpcode.ConvertEnergyToPayload, 3, Demo2RuleCondition.HasExcessEnergy),
|
|
Rule("cycle.energy-settlement", "能量清算", "结算时剩余能量每点转化 5 载荷。", Demo2RuleCategory.CycleSettlement, Demo2RuleOpcode.ConvertEnergyToPayload, 5, Demo2RuleCondition.HasExcessEnergy),
|
|
Rule("cycle.excess-credit", "过量授信", "每 20 点超额火力兑换 1 资金。", Demo2RuleCategory.CycleSettlement, Demo2RuleOpcode.AddMoneyFromExcess, 20),
|
|
Rule("cycle.first-batch", "首批红利", "首批完成时倍率增加 0.3。", Demo2RuleCategory.CycleSettlement, Demo2RuleOpcode.AddMultiplier, 300, Demo2RuleCondition.FirstBatch),
|
|
Rule("cycle.reserve-capacitor", "储备电容", "剩余能量使倍率提高 15%。", Demo2RuleCategory.CycleSettlement, Demo2RuleOpcode.MultiplyMultiplier, 1150, Demo2RuleCondition.HasExcessEnergy),
|
|
Rule("risk.glass-core", "玻璃核心", "核心仅剩 1 点耐久时,倍率提高 65%。", Demo2RuleCategory.RiskExchange, Demo2RuleOpcode.AddMultiplier, 650, Demo2RuleCondition.CoreAtRisk),
|
|
Rule("risk.debt-leverage", "负债杠杆", "立即增加 260 载荷,但故障计数增加 2。", Demo2RuleCategory.RiskExchange, new[]
|
|
{
|
|
Instruction(Demo2RuleOpcode.AddPayload, 260), Instruction(Demo2RuleOpcode.AddFaults, 2)
|
|
})
|
|
};
|
|
|
|
private static void AddMachinesAndRecipes(Demo2ContentRegistry registry)
|
|
{
|
|
registry.AddRecipe(new Demo2RecipeDefinition { Id = 1, Name = "采集铁矿", Output = Demo2MaterialKind.IronOre, OutputCount = 1, ProcessTicks = 20 });
|
|
registry.AddRecipe(new Demo2RecipeDefinition { Id = 2, Name = "燃料电解", Output = Demo2MaterialKind.Fuel, OutputCount = 1, ProcessTicks = 30 });
|
|
registry.AddRecipe(new Demo2RecipeDefinition { Id = 3, Name = "铁锭熔炼", InputA = Demo2MaterialKind.IronOre, InputB = Demo2MaterialKind.Fuel, Output = Demo2MaterialKind.Ingot, InputCountA = 2, InputCountB = 1, OutputCount = 1, ProcessTicks = 40 });
|
|
registry.AddRecipe(new Demo2RecipeDefinition { Id = 4, Name = "齿轮装配", InputA = Demo2MaterialKind.Ingot, Output = Demo2MaterialKind.Gear, InputCountA = 2, OutputCount = 1, ProcessTicks = 50 });
|
|
registry.AddRecipe(new Demo2RecipeDefinition { Id = 5, Name = "弹药压制", InputA = Demo2MaterialKind.Gear, InputB = Demo2MaterialKind.Energy, Output = Demo2MaterialKind.Ammo, InputCountA = 1, InputCountB = 1, OutputCount = 4, ProcessTicks = 35 });
|
|
|
|
registry.AddMachine(new Demo2MachineDefinition { Kind = Demo2MachineKind.Miner, Name = "采集器", Cost = 40, RecipeId = 1, Capacity = 8 });
|
|
registry.AddMachine(new Demo2MachineDefinition { Kind = Demo2MachineKind.Conveyor, Name = "传送带", Cost = 5, Capacity = 4 });
|
|
registry.AddMachine(new Demo2MachineDefinition { Kind = Demo2MachineKind.Splitter, Name = "分流器", Cost = 15, Capacity = 8 });
|
|
registry.AddMachine(new Demo2MachineDefinition { Kind = Demo2MachineKind.Smelter, Name = "熔炼机", Cost = 80, RecipeId = 3, Capacity = 12 });
|
|
registry.AddMachine(new Demo2MachineDefinition { Kind = Demo2MachineKind.Assembler, Name = "组装机", Cost = 110, RecipeId = 4, Capacity = 12 });
|
|
registry.AddMachine(new Demo2MachineDefinition { Kind = Demo2MachineKind.Loader, Name = "弹药装填器", Cost = 70, RecipeId = 5, Capacity = 24 });
|
|
registry.AddMachine(new Demo2MachineDefinition { Kind = Demo2MachineKind.Turret, Name = "自动炮塔", Cost = 150, Capacity = 200 });
|
|
registry.AddMachine(new Demo2MachineDefinition { Kind = Demo2MachineKind.Generator, Name = "能量站", Cost = 60, RecipeId = 2, Capacity = 16 });
|
|
}
|
|
|
|
private static void AddWaves(Demo2ContentRegistry registry)
|
|
{
|
|
var health = 500L;
|
|
var traits = new[] { "标准装甲", "分散队列", "电磁护盾", "高速突击", "核心压制" };
|
|
for (var cycle = 1; cycle <= Demo2Protocol.CycleCount; cycle++)
|
|
{
|
|
registry.AddWave(new Demo2WaveDefinition { Cycle = cycle, TotalHealth = health, Trait = traits[cycle - 1] });
|
|
health = SaturatingMath.MultiplyScaled(health, 1800);
|
|
}
|
|
}
|
|
|
|
private static Demo2RuleDefinition Rule(string id, string name, string description, Demo2RuleCategory category, Demo2RuleOpcode opcode, long operand, Demo2RuleCondition condition = Demo2RuleCondition.None)
|
|
=> Rule(id, name, description, category, new[] { Instruction(opcode, operand, condition) });
|
|
|
|
private static Demo2RuleDefinition Rule(string id, string name, string description, Demo2RuleCategory category, IEnumerable<Demo2RuleInstructionData> instructions)
|
|
=> new() { Id = id, Name = name, Description = description, Category = category, Instructions = instructions.ToList() };
|
|
|
|
private static Demo2RuleInstructionData Instruction(Demo2RuleOpcode opcode, long operand, Demo2RuleCondition condition = Demo2RuleCondition.None)
|
|
=> new() { Opcode = opcode, Operand = operand, Condition = condition };
|
|
}
|
|
}
|