Files
jgl_sunlit_patch/kubejs/server_scripts/itemEvents/pigRacing.js
T
2026-07-31 10:34:32 +09:00

634 lines
18 KiB
JavaScript

console.info("[SOCIETY] pigRacing.js loaded");
const debugSpeeds = false;
const pigColors = ["Red", "Blue", "Yellow", "Green"];
const stashyHashy = (input) => {
let hash = 5;
for (let i = 0; i < input.length; i++) {
hash = (hash * 9) ^ input.codePointAt(i);
}
return Math.abs(Math.round(hash / 512));
};
const adjustment = (enabled) => (enabled ? 0.05 : 0);
const getSpeeds = (seed, level) => {
let speeds = [];
let hashNumber;
let boostFirstSprint = false;
let sleepyFirstSprint = false;
let boostSecondSprint = false;
let sleepySecondSprint = false;
pigColors.forEach((pig) => {
hashNumber = Math.abs(stashyHashy(global.getSeasonFromLevel(level) + pig) + seed);
boostFirstSprint = hashNumber % 3 === 0;
sleepyFirstSprint = hashNumber % 4 === 0;
boostSecondSprint = hashNumber % 5 === 0;
sleepySecondSprint = hashNumber % 6 === 0;
if (debugSpeeds) {
console.log(`${pig}: Boost: ${boostFirstSprint} - ${boostSecondSprint}`);
console.log(`${pig}: Sleepy: ${sleepyFirstSprint} - ${sleepySecondSprint}`);
}
speeds.push([
Math.random() * (1.2 + adjustment(boostFirstSprint) - (0.7 - adjustment(sleepyFirstSprint))) +
1,
Math.random() *
(1.2 + adjustment(boostSecondSprint) - (0.7 - adjustment(sleepySecondSprint))) +
1,
]);
});
return speeds;
};
const clearPigPaint = (player) => {
const pigElements = ["", "Change", "Trophy"];
let removedElements = {};
let id = "";
// Spawn and clear instance of paint element to prevent warnings that they don't exist
pigColors.forEach((pig) => {
pigElements.forEach((element) => {
id = `pig${pig}${element}`;
removedElements[id] = { type: "text" };
});
});
player.paint(removedElements);
pigColors.forEach((pig) => {
pigElements.forEach((element) => {
id = `pig${pig}${element}`;
removedElements[id] = { remove: true };
});
});
player.paint(removedElements);
};
const initialPaint = (player, bet, pig) => {
player.paint({
screen: {
type: "rectangle",
y: -50,
w: 268,
h: 256,
alignX: "center",
alignY: "bottom",
texture: "society:textures/gui/pig_racing/horse_racing_screen.png",
},
pigRed: {
type: "rectangle",
x: -110,
y: -170,
z: 20,
w: 32,
h: 32,
alignX: "center",
alignY: "bottom",
texture: "society:textures/gui/pig_racing/pig_red.png",
},
pigBlue: {
type: "rectangle",
x: -110,
y: -132,
z: 20,
w: 32,
h: 32,
alignX: "center",
alignY: "bottom",
texture: "society:textures/gui/pig_racing/pig_blue.png",
},
pigYellow: {
type: "rectangle",
x: -110,
y: -94,
z: 20,
w: 32,
h: 32,
alignX: "center",
alignY: "bottom",
texture: "society:textures/gui/pig_racing/pig_yellow.png",
},
pigGreen: {
type: "rectangle",
x: -110,
y: -54,
z: 20,
w: 32,
h: 32,
alignX: "center",
alignY: "bottom",
texture: "society:textures/gui/pig_racing/pig_green.png",
},
betCoin: {
type: "item",
item: bet.id,
x: -100,
y: -197,
h: 32,
w: 32,
alignX: "center",
alignY: "bottom",
},
betCount: {
type: "text",
text: `x${bet.count}`,
scale: 3,
color: "#000000",
x: -70,
y: -207,
alignX: "center",
alignY: "bottom",
},
betPig: {
type: "rectangle",
y: -212,
x: 112,
z: 20,
w: 32,
h: 32,
alignX: "center",
alignY: "bottom",
texture: `society:textures/gui/pig_racing/pig_${pig.toLowerCase()}.png`,
},
});
};
const getCountDownPaint = (count) => {
return {
type: "rectangle",
y: -70,
z: 10,
w: 128,
h: 128,
alignX: "center",
alignY: "bottom",
texture: `society:textures/gui/pig_racing/${count}.png`,
};
};
const countDown = (player, server) => {
const delayFactor = 7;
player.paint({
number: getCountDownPaint("3"),
});
server.scheduleInTicks(delayFactor, () => {
player.paint({
number: getCountDownPaint("2"),
});
});
server.scheduleInTicks(delayFactor * 2, () => {
player.paint({
number: getCountDownPaint("1"),
});
});
server.scheduleInTicks(delayFactor * 3, () => {
player.paint({
number: {
type: "rectangle",
y: -70,
z: 10,
w: 128,
h: 128,
alignX: "center",
alignY: "bottom",
texture: `society:textures/gui/pig_racing/go.png`,
},
});
});
server.scheduleInTicks(delayFactor * 4, () => {
player.paint({
number: { remove: true },
});
});
};
const paintPig = (player, server, xOffset, speeds, halfwayPoint, ranking) => {
const yOffsets = [-170, -132, -94, -54];
let pigElement = {};
const finishLine = 214;
let id = "";
let halfWay = 0;
let secondWindCalc = 0;
let secondWindChange = false;
let calculatedOffset = 0;
let raceCompleted = false;
pigColors.forEach((color, index) => {
id = `pig${color}`;
halfWay = speeds[index][0] * halfwayPoint;
secondWindCalc = halfWay + speeds[index][1] * (xOffset - halfwayPoint) + 1;
raceCompleted = secondWindCalc >= finishLine;
calculatedOffset =
speeds[index][0] * xOffset >= halfWay
? raceCompleted
? finishLine
: secondWindCalc
: speeds[index][0] * xOffset;
if (
Math.floor(speeds[index][0] * xOffset) === Math.floor(halfWay) &&
Math.abs(speeds[index][1] - speeds[index][0]) >= 0.1
) {
secondWindChange = speeds[index][1] > speeds[index][0];
server.runCommandSilent(
`playsound ${secondWindChange ? "trials:breeze_idle" : "trials:spawner_summon"} player @a ${
player.x
} ${player.y} ${player.z}`
);
pigElement[`${id}Change`] = {
type: "rectangle",
color: "#FFFFFF",
z: 12,
x: -110 + halfWay,
y: yOffsets[index] - 8,
alignX: "center",
alignY: "bottom",
texture: `society:textures/gui/pig_racing/${secondWindChange ? "boost" : "sleepy"}.png`,
};
}
pigElement[id] = { remove: true };
player.paint(pigElement);
pigElement[id] = {
type: "rectangle",
x: -110 + calculatedOffset,
y: yOffsets[index],
z: 11,
w: 32,
h: 32,
alignX: "center",
alignY: "bottom",
texture: `society:textures/gui/pig_racing/pig_${color.toLowerCase()}.png`,
};
if (raceCompleted) {
!ranking.includes(color) && ranking.push(color);
pigElement[`${id}Trophy`] = {
type: "rectangle",
y: yOffsets[index],
x: finishLine - 130 - 16,
z: 10,
w: 32,
h: 32,
alignX: "center",
alignY: "bottom",
texture: `society:textures/gui/pig_racing/trophy_${ranking.indexOf(color) + 1}.png`,
};
}
player.paint(pigElement);
});
};
const handleSPResults = (player, server, betPig, betItem, ranking) => {
let newItem = betItem;
newItem.count = betItem.count * 2;
const wonRace = ranking[0] === betPig;
if (wonRace) {
player.give(Item.of(newItem));
server.runCommandSilent(
`playsound advancementplaques:ui.toast.goal_complete player @a ${player.x} ${player.y} ${player.z}`
);
} else {
server.runCommandSilent(
`playsound stardew_fishing:fish_escape player @a ${player.x} ${player.y} ${player.z}`
);
}
player.paint({
result: {
type: "rectangle",
y: -70,
z: 20,
w: 128,
h: 128,
alignX: "center",
alignY: "bottom",
texture: `society:textures/gui/pig_racing/${wonRace ? "win" : "lost"}.png`,
},
});
};
const handleMPResults = (player, server, betPig, betValue, poolValue, ranking) => {
const wonRace = ranking[0] === betPig;
let prizePoolCoins = 0;
ranking.forEach((rank, index) => {
if (rank === betPig) {
if (index === 0) {
prizePoolCoins = global.calculateCoinsFromValue(Math.min(poolValue, betValue * 2), []);
prizePoolCoins.forEach((coin) => {
player.give(Item.of(`${coin.count}x ${coin.id}`));
});
server.tell(
Text.translatable(
"society.pig_race.multiplay.result",
":first_place_medal:",
Text.gold(player.username),
Text.gold(global.formatPrice(Math.min(poolValue, betValue * 2))),
global.getPigColoredName(betPig)
).gray()
);
}
if (index === 1) {
prizePoolCoins = global.calculateCoinsFromValue(betValue, []);
prizePoolCoins.forEach((coin) => {
player.give(Item.of(`${Math.floor(coin.count)}x ${coin.id}`));
});
server.tell(
Text.translatable(
"society.pig_race.multiplay.result",
":second_place_medal:",
Text.gold(player.username),
Text.gold(global.formatPrice(Math.min(poolValue, betValue))),
global.getPigColoredName(betPig)
).gray()
);
}
if (index === 2) {
prizePoolCoins = global.calculateCoinsFromValue(betValue / 2, []);
prizePoolCoins.forEach((coin) => {
player.give(Item.of(`${Math.floor(coin.count)}x ${coin.id}`));
});
server.tell(
Text.translatable(
"society.pig_race.multiplay.result",
":third_place_medal:",
Text.gold(player.username),
Text.gold(global.formatPrice(Math.min(poolValue, betValue / 2))),
global.getPigColoredName(betPig)
).gray()
);
}
}
});
if (wonRace) {
server.runCommandSilent(
`playsound advancementplaques:ui.toast.goal_complete player @a ${player.x} ${player.y} ${player.z}`
);
} else {
server.runCommandSilent(
`playsound stardew_fishing:fish_escape player @a ${player.x} ${player.y} ${player.z}`
);
}
player.paint({
result: {
type: "rectangle",
y: -70,
z: 20,
w: 128,
h: 128,
alignX: "center",
alignY: "bottom",
texture: `society:textures/gui/pig_racing/${wonRace ? "win" : "lost"}.png`,
},
});
};
const resetMP = (server) => {
server.persistentData.bets = [];
server.persistentData.pigraceInProgress = false;
};
const runPigRace = (player, server, bet, betPig, ranking, pigSpeeds) => {
global.addItemCooldown(player, "society:pig_race_ticket", 350);
server.scheduleInTicks(0, () => {
server.runCommandSilent(`stopsound ${player.username}`);
server.runCommandSilent(
`playsound society:blazing_drift_fever_ex music ${player.username} ${player.x} ${player.y} ${player.z}`
);
initialPaint(player, bet, betPig);
countDown(player, server);
server.scheduleInTicks(25, () => {
const halfwayPoint = Math.floor(rnd(40, 120));
let i = 0;
let k = 0;
do {
player.server.scheduleInTicks(i, () => {
paintPig(player, server, k, pigSpeeds, halfwayPoint, ranking);
k++;
});
i++;
} while (i < 220);
});
server.scheduleInTicks(300 + 20, () => {
server.runCommandSilent(`stopsound ${player.username}`);
clearPigPaint(player);
player.paint({
screen: { remove: true },
betCoin: { remove: true },
betCount: { remove: true },
betPig: { remove: true },
result: { remove: true },
});
});
});
};
const validTicket = (e, bet) => {
const { player, server, item } = e;
const coins = [
"numismatics:crown",
"numismatics:sun",
"numismatics:neptunium_coin",
"numismatics:ancient_coin",
"numismatics:prismatic_coin",
];
if (!coins.includes(bet.id)) {
server.runCommandSilent(
global.getEmbersTextAPICommand(
player.username,
`{anchor:"BOTTOM_CENTER",background:1,align:"BOTTOM_CENTER",color:"#FFFFFFF",offsetY:-60}`,
80,
Text.translatable("society.pig_race.need_coin").toJson()
)
);
return false;
}
if (!item.nbt) {
server.runCommandSilent(
global.getEmbersTextAPICommand(
player.username,
`{anchor:"BOTTOM_CENTER",background:1,align:"BOTTOM_CENTER",color:"#FFFFFFF",offsetY:-60}`,
80,
Text.translatable("society.pig_race.need_to_select_pig").toJson()
)
);
return false;
}
return true;
};
ItemEvents.rightClicked("society:pig_race_ticket", (e) => {
const { player, server, item, level } = e;
const pigSpeeds = getSpeeds(server.worldData.worldGenOptions().seed(), level);
const bet = player.offHandItem;
let betPig;
let ranking = [];
if (!validTicket(e, bet)) return;
item.count--;
player.offHandItem = "minecraft:air";
betPig = item.nbt.bet;
runPigRace(player, server, bet, betPig, ranking, pigSpeeds);
server.scheduleInTicks(210 + 40, () => {
handleSPResults(player, server, betPig, bet, ranking);
});
});
ItemEvents.rightClicked("society:multiplayer_pig_race_ticket", (e) => {
const { player, server, item, level } = e;
const pigSpeeds = getSpeeds(server.worldData.worldGenOptions().seed(), level);
const bettingPeriod = 20 * 60 * 2;
const bet = player.offHandItem;
let raceData = server.persistentData;
let betValue;
let betPig;
let ranking = [];
let players = [];
let prizePool = 0;
if (raceData.pigraceInProgress) {
server.runCommandSilent(
global.getEmbersTextAPICommand(
player.username,
`{anchor:"BOTTOM_CENTER",background:1,align:"BOTTOM_CENTER",color:"#FFFFFFF",offsetY:-60}`,
80,
Text.translatable("society.pig_race.multiplay.in_progress").toJson()
)
);
return;
}
if (!validTicket(e, bet)) return;
resetMP(server);
item.count--;
player.offHandItem = "minecraft:air";
betValue = betValue = global.calculateCoinValue(bet);
betPig = item.nbt.bet;
server.tell(
Text.translatable(
"society.pig_race.multiplay.start",
Text.gold(player.username),
Text.gold(global.formatPrice(betValue)),
global.getPigColoredName(betPig)
)
);
raceData.pigraceInProgress = true;
raceData.ageRaceStarted = server.getTickCount();
raceData.bets = [];
raceData.bets.push({
player: player.username,
bet: betValue,
betPig: betPig,
});
server.scheduleInTicks(0, () => {
server.scheduleInTicks(bettingPeriod / 2, () => {
server.tell(
Text.translatable(
"society.pig_race.multiplay.start_in_minute",
`${bettingPeriod / (2 * 20 * 60)}`
).gray()
);
});
server.scheduleInTicks(bettingPeriod - 10 * 20, () => {
server.tell(Text.translatable("society.pig_race.multiplay.start_in_10").gray());
});
server.scheduleInTicks(bettingPeriod, () => {
raceData = server.persistentData;
if (raceData.bets.length > 1) {
raceData.pigraceInProgress = false;
for (let index = 0; index < raceData.bets.length; index++) {
prizePool += raceData.bets[index].bet;
}
level.getServer().players.forEach((p) => {
for (let index = 0; index < raceData.bets.length; index++) {
if (String(p.username) === String(raceData.bets[index].player)) {
players.push(p);
runPigRace(
p,
server,
global.calculateCoinsFromValue(prizePool, [])[0],
String(raceData.bets[index].betPig),
ranking,
pigSpeeds
);
}
}
});
server.tell(
Text.translatable(
"society.pig_race.multiplay.started",
`${global.formatPrice(prizePool)}`
).gray()
);
} else {
player.give(bet);
player.give(Item.of("society:multiplayer_pig_race_ticket"));
server.tell(Text.translatable("society.pig_race.multiplay.cancelled").gray());
clearPigPaint(player);
resetMP(server);
}
});
server.scheduleInTicks(210 + 40 + bettingPeriod, () => {
level.getServer().players.forEach((p) => {
for (let index = 0; index < raceData.bets.length; index++) {
if (String(p.username) === String(raceData.bets[index].player)) {
handleMPResults(
p,
server,
String(raceData.bets[index].betPig),
Number(raceData.bets[index].bet),
prizePool,
ranking
);
clearPigPaint(player);
}
}
});
resetMP(server);
});
});
});
ItemEvents.firstLeftClicked(
["society:pig_race_ticket", "society:multiplayer_pig_race_ticket"],
(e) => {
const { item, server, player } = e;
server.runCommandSilent(
`playsound refurbished_furniture:ui.paddle_ball.retro_click block @a ${player.x} ${player.y} ${player.z}`
);
let newNbt = item.getNbt() || { bet: 0 };
const betIndex = pigColors.indexOf(newNbt.bet);
const betPig = pigColors[betIndex === 3 ? 0 : betIndex + 1];
newNbt.bet = betPig;
item.nbt = newNbt;
const pigSelectedText = Text.translatable("society.pig_race.pig_selected", global.getPigColoredName(betPig));
const pigSelectedTextShadow = Text.translatable("society.pig_race.pig_selected", betPig).noColor();
global.renderUiText(
player,
server,
{
pigBet: {
type: "text",
x: 0,
y: -90,
text: `${pigSelectedText.toJson()}`,
color: "#AAAAAA",
alignX: "center",
alignY: "bottom",
},
pigBetShadow: {
type: "text",
x: 1,
z: -1,
y: -89,
text: `${pigSelectedTextShadow.toJson()}`,
color: "#000000",
alignX: "center",
alignY: "bottom",
},
},
["pigBet"]
);
global.addItemCooldown(player, item, 10);
}
);