Add NPC supply chain workers

This commit is contained in:
CoalGov Deploy
2026-05-06 02:28:32 +00:00
parent be82f0b486
commit 3fb2b5c323
5 changed files with 214 additions and 6 deletions

View File

@@ -12,12 +12,15 @@ import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -140,7 +143,8 @@ public final class NpcService {
public void tickWorkers() {
try {
for (CoalNpc coalNpc : repository.listNpcs()) {
List<CoalNpc> npcs = repository.listNpcs();
for (CoalNpc coalNpc : npcs) {
if (!coalNpc.worker()) {
continue;
}
@@ -161,10 +165,9 @@ public final class NpcService {
npc.getNavigator().setTarget(target);
}
}
Material material = Material.matchMaterial(plugin.getConfig().getString("npc.worker.produce_material", "BREAD"));
int amount = plugin.getConfig().getInt("npc.worker.produce_amount", 2);
if (material != null && amount > 0) {
repository.addInventory(coalNpc.id(), material, amount);
WorkerRecipe recipe = recipeFor(coalNpc);
if (recipe != null && produce(coalNpc, recipe, npcs)) {
supplyTraders(coalNpc, recipe, npcs);
}
}
} catch (SQLException exception) {
@@ -172,6 +175,123 @@ public final class NpcService {
}
}
private WorkerRecipe recipeFor(CoalNpc npc) {
ConfigurationSection roles = plugin.getConfig().getConfigurationSection("npc.worker.roles");
if (roles != null) {
String npcName = npc.name().toLowerCase();
for (String role : roles.getKeys(false)) {
if (npcName.contains(role.toLowerCase())) {
WorkerRecipe recipe = recipe(role, roles.getConfigurationSection(role));
if (recipe != null) {
return recipe;
}
}
}
}
Material material = Material.matchMaterial(plugin.getConfig().getString("npc.worker.produce_material", "BREAD"));
int amount = plugin.getConfig().getInt("npc.worker.produce_amount", 2);
return material == null || amount <= 0 ? null : new WorkerRecipe("default", material, amount, Map.of());
}
private WorkerRecipe recipe(String name, ConfigurationSection section) {
if (section == null) {
return null;
}
Material output = Material.matchMaterial(section.getString("output_material", ""));
int outputAmount = section.getInt("output_amount", 0);
if (output == null || outputAmount <= 0) {
return null;
}
Map<Material, Long> inputs = new LinkedHashMap<>();
ConfigurationSection inputSection = section.getConfigurationSection("inputs");
if (inputSection != null) {
for (String key : inputSection.getKeys(false)) {
Material material = Material.matchMaterial(key);
long amount = inputSection.getLong(key);
if (material != null && amount > 0L) {
inputs.put(material, amount);
}
}
}
return new WorkerRecipe(name, output, outputAmount, inputs);
}
private boolean produce(CoalNpc worker, WorkerRecipe recipe, List<CoalNpc> npcs) throws SQLException {
for (var input : recipe.inputs().entrySet()) {
long stocked = repository.inventoryAmount(worker.id(), input.getKey());
long missing = input.getValue() - stocked;
if (missing > 0L) {
requestInput(worker, input.getKey(), missing, npcs);
}
if (repository.inventoryAmount(worker.id(), input.getKey()) < input.getValue()) {
return false;
}
}
for (var input : recipe.inputs().entrySet()) {
if (!repository.removeInventory(worker.id(), input.getKey(), input.getValue())) {
return false;
}
}
repository.addInventory(worker.id(), recipe.output(), recipe.outputAmount());
return true;
}
private void requestInput(CoalNpc requester, Material material, long missing, List<CoalNpc> npcs) throws SQLException {
long remaining = missing;
long reserve = Math.max(0L, plugin.getConfig().getLong("npc.supply_chain.source_reserve", 16L));
long maxTransfer = Math.max(1L, plugin.getConfig().getLong("npc.supply_chain.max_transfer_per_tick", 64L));
for (CoalNpc source : npcs) {
if (remaining <= 0L) {
return;
}
if (source.id() == requester.id() || source.zoneId() != requester.zoneId()) {
continue;
}
long available = Math.max(0L, repository.inventoryAmount(source.id(), material) - reserve);
long moved = Math.min(Math.min(available, remaining), maxTransfer);
if (moved <= 0L || !repository.removeInventory(source.id(), material, moved)) {
continue;
}
repository.addInventory(requester.id(), material, moved);
remaining -= moved;
}
}
private void supplyTraders(CoalNpc worker, WorkerRecipe recipe, List<CoalNpc> npcs) throws SQLException {
if (!plugin.getConfig().getBoolean("npc.supply_chain.traders_accept_outputs", true)) {
return;
}
long reserve = Math.max(0L, plugin.getConfig().getLong("npc.supply_chain.worker_output_reserve", 16L));
long target = Math.max(0L, plugin.getConfig().getLong("npc.supply_chain.trader_restock_target", 256L));
long maxTransfer = Math.max(1L, plugin.getConfig().getLong("npc.supply_chain.max_transfer_per_tick", 64L));
long available = Math.max(0L, repository.inventoryAmount(worker.id(), recipe.output()) - reserve);
if (available <= 0L) {
return;
}
for (CoalNpc trader : tradersInZone(worker, npcs)) {
long needed = target - repository.inventoryAmount(trader.id(), recipe.output());
long moved = Math.min(Math.min(needed, available), maxTransfer);
if (moved <= 0L || !repository.removeInventory(worker.id(), recipe.output(), moved)) {
continue;
}
repository.addInventory(trader.id(), recipe.output(), moved);
available -= moved;
if (available <= 0L) {
return;
}
}
}
private List<CoalNpc> tradersInZone(CoalNpc worker, List<CoalNpc> npcs) {
List<CoalNpc> traders = new ArrayList<>();
for (CoalNpc npc : npcs) {
if (npc.trader() && npc.zoneId() == worker.zoneId()) {
traders.add(npc);
}
}
return traders;
}
private Location randomPoint(NpcZone zone) {
World world = Bukkit.getWorld(zone.world());
if (world == null) {
@@ -215,4 +335,7 @@ public final class NpcService {
public record HaggleQuote(String mode, Material material, int amount, long offer, long floor, long ceiling,
long counter, boolean accepted, String reason) {
}
private record WorkerRecipe(String name, Material output, int outputAmount, Map<Material, Long> inputs) {
}
}