Files
jgl_sunlit_patch/kubejs/startup_scripts/globalShippingBinHandlers.js
T
2026-07-31 10:34:32 +09:00

429 lines
13 KiB
JavaScript

// Priority: 1000
const calculateQualityValue = (number, quality, doubled) => {
let value;
if (quality) {
if (quality == 1.0) value = Math.round(number * (doubled ? 1.5 : 1.25));
if (quality == 2.0) value = Math.round(number * (doubled ? 2 : 1.5));
if (quality == 3.0) value = Math.round(number * (doubled ? 3 : 2));
} else {
value = number;
}
return value;
};
// Returns array of coins from price, prioritizing high value coins
const calculateCoinsFromValue = (price, output, coinMap) => {
for (let i = 0; i < coinMap.length; i++) {
let { coin, value } = coinMap[i];
if (value <= price) {
if (price % value === 0) {
output.push({ coin: coin, count: price / value });
return output;
} else {
output.push({ coin: coin, count: Math.floor(price / value) });
calculateCoinsFromValue(price % value, output, coinMap);
}
return output;
}
}
};
const textComponentToLegacy = (data) => {
const colorMap = {
black: "§0", dark_blue: "§1", dark_green: "§2", dark_aqua: "§3",
dark_red: "§4", dark_purple: "§5", gold: "§6", gray: "§7",
dark_gray: "§8", blue: "§9", green: "§a", aqua: "§b",
red: "§c", light_purple: "§d", yellow: "§e", white: "§f"
};
let prefix = "";
if (data.color) prefix += colorMap[data.color.toLowerCase()] ?? "§f";
if (data.bold) prefix += "§l";
if (data.italic) prefix += "§o";
if (data.underlined) prefix += "§n";
if (data.strikethrough) prefix += "§m";
if (data.obfuscated) prefix += "§k";
const text = data.text ?? "";
return prefix + text;
};
global.getShippingBinName = (nbt, legacy) => {
if (nbt && nbt.contains('customName')) {
let json = nbt.getString('customName');
let data = JSON.parse(json);
if (legacy) {
return textComponentToLegacy(data);
}
else {
return Text.of(data);
}
}
return null;
};
global.getAttributeMultiplier = (attributes, multiplier) => {
return (
Number(
attributes.filter((obj) => {
return obj.Name === multiplier;
})[0]?.Base
) || 1
);
};
global.processShippingBinInventory = (
inventory,
inventorySlots,
attributes,
stages,
returnRemoved,
simulated
) => {
let calculatedValue = 0;
let itemValue = 0;
let removedItems = [];
let slotItem;
let isSellable;
let isLongwing
for (let i = 0; i < inventorySlots; i++) {
slotItem = inventory.getStackInSlot(i).item;
isLongwing = ["longwings:moth", "longwings:butterfly"].includes(slotItem.id);
isSellable = global.trades.has(String(slotItem.id)) || isLongwing || ["splendid_slimes:plort", "splendid_slimes:slime_heart"].includes(slotItem.id);
if (isSellable) {
let trade = global.trades.get(String(slotItem.id));
let quality = undefined;
let doubleQuality = false;
let slotNbt = undefined;
if (inventory.getStackInSlot(i).hasNBT()) {
slotNbt = inventory.getStackInSlot(i).nbt;
}
if (slotNbt && ((slotNbt.slime && slotNbt.slime.id) || (slotNbt.plort && slotNbt.plort.id))) {
if (slotNbt.slime)
trade = global.trades.get(`${slotItem.id}/${slotNbt.slime.id}`);
if (slotNbt.plort)
trade = global.trades.get(`${slotItem.id}/${slotNbt.plort.id}`);
}
if (slotNbt && isLongwing && slotNbt.variant) {
trade = global.trades.get(`longwings:variant/${slotNbt.variant}`);
}
if (slotNbt && slotNbt.quality_food) {
quality = slotNbt.quality_food.quality;
}
// UPDATE CACHING METHOD WHEN ADDING STAGES
if (
quality &&
quality > 0 &&
stages.toString().includes("the_quality_of_the_earth") &&
trade.multiplier.equals("shippingbin:crop_sell_multiplier") &&
!Item.of(slotItem).hasTag("minecraft:fishes")
) {
doubleQuality = true;
}
itemValue = calculateQualityValue(trade.value, quality, doubleQuality);
if (
stages.toString().includes("bluegill_meridian") &&
slotItem.id == "aquaculture:bluegill"
) {
itemValue = calculateQualityValue(666, quality);
}
if (
stages.toString().includes("phenomenology_of_treasure") &&
(Item.of(slotItem).hasTag("society:artifacts") ||
Item.of(slotItem).hasTag("society:relics"))
) {
itemValue *= 3;
}
if (
stages.toString().includes("brine_and_punishment") &&
Item.of(slotItem).hasTag("society:brine_and_punishment")
) {
itemValue *= 2;
}
if (isLongwing) {
let size = 1.0;
if (slotNbt.nbt) {
if (item.nbt.size && typeof item.nbt.size !== "function") {
size = slotNbt.size;
}
}
if (stages.toString().includes("the_metamorphosize")) {
itemValue += (Math.round((size / 0.01) * 4) * 3);
} else {
itemValue += Math.round((size / 0.01) * 4)
}
}
let additionalValue = Math.round(
itemValue *
inventory.getStackInSlot(i).count *
global.getAttributeMultiplier(attributes, trade.multiplier)
);
if (isNaN(additionalValue)) {
console.log("SHIPPING BIN VALUE IS NAN! DEBUG LOG:")
console.log("itemValue: " + itemValue)
console.log("attrbMult: " + global.getAttributeMultiplier(attributes, trade.multiplier))
console.log("Trade Value: " + trade.value)
calculatedValue += itemValue * inventory.getStackInSlot(i).count
} else {
calculatedValue += additionalValue
}
}
if (isSellable && !simulated) {
if (returnRemoved) removedItems.push(i);
else inventory.setStackInSlot(i, "minecraft:air");
}
}
return { calculatedValue: calculatedValue, removedItems: removedItems };
};
const debugValueProcess = false;
global.handleShippingBinDebt = (
value,
player,
server,
block,
inventory,
extenalOutput
) => {
if (!player) return value;
let playerUUID = player.getUuid().toString();
let binDebt = 0;
let debtPaid;
let totalDebt;
let receipt;
let newValue = value;
if (!playerUUID) return value;
if (server.persistentData.debts) {
binDebt = server.persistentData.debts.filter((debt) => {
return debt.uuid === playerUUID;
});
}
if (binDebt.length > 0 && binDebt[0].amount > 0) {
totalDebt = binDebt[0].amount;
if (value >= totalDebt) {
newValue = value - totalDebt;
debtPaid = totalDebt;
server.runCommandSilent(
global.getEmbersTextAPICommand(
player.username,
`{anchor:"TOP_LEFT",background:1,color:"#55FF55",size:1,offsetY:36,offsetX:6,typewriter:1,align:"TOP_LEFT"}`,
160,
Text.translatable(
"society.shipping_bin.debt_paid_all",
global.formatPrice(debtPaid.toFixed())
).toJson()
)
);
global.setDebt(server, playerUUID, 0);
} else {
debtPaid = value;
newValue = 0;
server.runCommandSilent(
global.getEmbersTextAPICommand(
player.username,
`{anchor:"TOP_LEFT",background:1,color:"#FFFFFF",size:1,offsetY:36,offsetX:6,typewriter:1,align:"TOP_LEFT"}`,
160,
Text.translatable(
"society.shipping_bin.debt_paid",
global.formatPrice(debtPaid.toFixed())
).toJson()
)
);
global.setDebt(server, playerUUID, totalDebt - debtPaid);
}
}
if (debtPaid > 0) {
let receiptAuthor = global.translatableWithFallback("society.hospital_receipt.author", "Sunlit Valley Hospital").getString();
let receiptText = Text.translatable(
"society.shipping_bin.debt_paid_note",
player.username,
global.formatPrice(debtPaid.toFixed()),
global.formatPrice(totalDebt.toFixed())
).toJson();
let receiptTitle = global.translatableWithFallback("society.shipping_bin.debt_paid_note.title", "Debt Payment Receipt").getString();
receipt = global.getNotePaperItem(receiptAuthor, receiptText, receiptTitle);
if (extenalOutput) {
block.popItemFromFace(receipt, block.properties.get("facing"));
} else {
for (let j = 0; j < inventory.slots; j++) {
if (inventory.getStackInSlot(j) === Item.of("minecraft:air")) {
inventory.insertItem(j, receipt, false);
break;
}
}
}
}
return newValue;
};
global.processValueOutput = (
value,
slots,
removedSlots,
player,
server,
block,
inventory,
extenalOutput,
ownerUUID
) => {
if (value > 0) {
let hasRoom = false;
value = Math.round(
global.handleShippingBinDebt(
value,
player,
server,
block,
inventory,
extenalOutput
)
);
let outputs = calculateCoinsFromValue(
value,
[],
extenalOutput ? global.coinMap : basicCoinMap
);
if (!outputs) outputs = [];
if (debugValueProcess) {
console.log(`slots: ${slots}`);
console.log(`countNonEmpty: ${inventory.countNonEmpty()}`);
console.log(`RemovedSlots: ${removedSlots.length}`);
console.log(`calculateSlotsNeeded: ${calculateSlotsNeeded(outputs)}`);
}
hasRoom =
extenalOutput ||
slots -
inventory.countNonEmpty() +
removedSlots.length -
calculateSlotsNeeded(outputs) >=
0;
if (hasRoom) {
if (!block.level.hasNeighborSignal(block.pos) && player) {
server.runCommandSilent(
`playsound etcetera:item.handbell.ring block @a ${player.x} ${player.y} ${player.z} 0.3`
);
let customName = global.getShippingBinName(block.getEntityData().data, true);
let finalName = customName ? ` ${customName} ` : "";
server.runCommandSilent(
global.getEmbersTextAPICommand(
player.username,
`{anchor:"TOP_LEFT",background:1,color:"#FFFFFF",size:1,offsetY:36,offsetX:6,typewriter:1,align:"TOP_LEFT"}`,
160,
Text.translatable(
"society.shipping_bin.goods_sold",
finalName,
global.formatPrice(value.toFixed()),
).toJson()
)
);
}
if (extenalOutput) {
let facing = block.properties.get("facing");
let account = global.GLOBAL_BANK.getAccount(ownerUUID);
let card = inventory.getStackInSlot(0);
if (card && card.hasTag("numismatics:cards")) {
account = global.GLOBAL_BANK.getAccount(
card.nbt.getUUID("AccountID")
);
}
if (account && account.getBalance() + value < 2147483000) {
account.deposit(value);
} else {
outputs.forEach((output) => {
let { coin, count } = output;
for (let index = 0; index <= count; index += 64) {
let difference = count - index;
block.popItemFromFace(
`${difference > 64 ? 64 : difference}x ${coin}`,
facing
);
}
});
}
} else {
/**
* Basic Shipping Bins only remove items when they're sure there's enough room.
* Smart Shipping Bins remove items upon calculation because they output externally.
*/
for (let i = 0; i < removedSlots.length; i++) {
inventory.setStackInSlot(removedSlots[i], "minecraft:air");
}
outputs.forEach((output) => {
let { coin, count } = output;
for (let index = 0; index <= count; index += 64) {
let difference = count - index;
for (let i = 0; i < slots; i++) {
if (inventory.getStackInSlot(i).item.id === "minecraft:air") {
inventory.setStackInSlot(
i,
Item.of(`${difference > 64 ? 64 : difference}x ${coin}`)
);
break;
}
}
}
});
}
}
if (!hasRoom && player) {
server.runCommandSilent(
`playsound stardew_fishing:fish_escape block @a ${player.x} ${player.y} ${player.z} 0.3`
);
server.runCommandSilent(
global.getEmbersTextAPICommand(
player.username,
`{anchor:"TOP_LEFT",background:1,color:"#FF5555",size:1,offsetY:36,offsetX:6,typewriter:1,align:"TOP_LEFT"}`,
160,
Text.translatable("society.shipping_bin.full").toJson()
)
);
}
}
};
global.cacheShippingBin = (entity) => {
const { level, block } = entity;
const attributeMapping = [
"shippingbin:crop_sell_multiplier",
"shippingbin:wood_sell_multiplier",
"shippingbin:gem_sell_multiplier",
"shippingbin:meat_sell_multiplier",
];
const stagesToFind = [
"bluegill_meridian",
"phenomenology_of_treasure",
"brine_and_punishment",
"the_quality_of_the_earth",
"the_metamorphosize"
];
let binPlayer;
level.getServer().players.forEach((p) => {
if (p.getUuid().toString() === block.getEntityData().data.owner) {
let newAttributes = [];
let stages = [];
attributeMapping.forEach((attr) => {
newAttributes.push({
Base: Number(
p.nbt.Attributes.filter((obj) => {
return obj.Name === attr;
})[0]?.Base
),
Name: attr,
});
});
stagesToFind.forEach((stage) => {
if (p.stages.has(stage)) stages.push(stage);
});
let nbt = block.getEntityData();
nbt.merge({ data: { attributes: newAttributes, stages: stages } });
global.setBlockEntityData(block, nbt)
binPlayer = p;
}
});
return binPlayer;
};