Add NPC supply chain workers
This commit is contained in:
@@ -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) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,21 @@ npc:
|
||||
tick_interval_seconds: 20
|
||||
produce_material: BREAD
|
||||
produce_amount: 2
|
||||
roles:
|
||||
farmer:
|
||||
output_material: WHEAT
|
||||
output_amount: 4
|
||||
baker:
|
||||
output_material: BREAD
|
||||
output_amount: 2
|
||||
inputs:
|
||||
WHEAT: 3
|
||||
supply_chain:
|
||||
max_transfer_per_tick: 64
|
||||
source_reserve: 16
|
||||
worker_output_reserve: 16
|
||||
traders_accept_outputs: true
|
||||
trader_restock_target: 256
|
||||
|
||||
claims:
|
||||
resale:
|
||||
|
||||
Reference in New Issue
Block a user