Raffle Ticket
Topics covered: AttachedData, item registration, custom loot, custom loot generation, and NBT usage. In this chapter, only registration code goes in startup_scripts; everything else belongs in server_scripts.
Mods and versions used:
- rhino-forge-2001.2.2-build.18
- architectury-9.2.14-forge
- kubejs-forge-2001.6.5-build.14
- probejs-6.0.1-forge
Register Raffle Ticket
js
StartupEvents.registry("item",event=>{
event.create("meng:raffle_ticket");
})Shared Raffle Ticket File
js
// priority: 5
// Base raffle item list
const basalItemList = [];
// Raffle pool list (temporary storage during draws)
let poolItemList = [];
// Ticket type mapping object
let ticketTypeObject = {
basal: basalItemList
};If you want to add other loot pools, you can do it like this:
js
const testItemList = [];
let ticketTypeObject = {
basal: basalItemList,
addTest : testItemList
};Add Raffle Loot Entries
js
function addBasalItem(itemId, weight) {
basalItemList.push({
itemId: itemId,
weight: weight
})
}
addBasalItem('minecraft:diamond', 3);
addBasalItem('minecraft:lapis_lazuli', 5)
addBasalItem('minecraft:emerald', 2)
addBasalItem('minecraft:gold_ingot', 8)
addBasalItem('minecraft:copper_ingot', 20)
addBasalItem('minecraft:iron_ingot', 20)
addBasalItem('minecraft:netherite_ingot', 1)Raffle Loot Table
js
ServerEvents.genericLootTables(event => {
for (const key in ticketTypeObject) {
event.addGeneric("meng:raffle_ticket/" + key,loot=>{
loot.addPool(pool => {
ticketTypeObject[key].forEach(value => {
pool.addItem(value.itemId).weight(value.weight);
});
});
})
}
})Start Raffle Draw
js
ItemEvents.firstRightClicked("meng:raffle_ticket", event => {
const player = event.getPlayer();
const playerAttachedData = player.getData();
if (playerAttachedData.get("lotteryState")) {
player.tell(Text.translate("text.meng.lottery_state"))
// playerAttachedData.put("lotteryState",false);
return;
}
const item = event.getItem();
const itemNbt = item.getNbt();
let ticketType;
try {
ticketType = itemNbt.getString("ticketType");
} catch (e) {
ticketType = "basal";
}
let itemList = ticketTypeObject[ticketType];
if (itemList == undefined) {
itemList = ticketTypeObject.basal;
}
let uuid = player.getUuid()
let obj = {}
obj[uuid] = MengUtils.ArrUtil.randArr(addItemList(itemList))
poolItemList.push(obj)
let lootData = event.getServer().getLootData();
let itemLoot = lootData
.getLootTable("meng:raffle_ticket/basal")
.getRandomItems(
new $LootParams(event.getLevel())
.create($LootContextParamSets.EMPTY)
)[0];
let poolList = poolItemList.find(value => Object.keys(value).includes(uuid.toString()))[uuid];
poolList[poolList.length - 2] = itemLoot.id;
playerAttachedData.add("lotteryState", true);
playerAttachedData.add("poolCountMax", poolList.length);
playerAttachedData.add("count", 0);
item.count--;
})
function addItemList(itemList) {
let list = []
itemList.forEach(value => {
for (let index = 0; index < value.weight; index++) {
list.push(value.itemId);
}
});
let listTemp = list;
while (true) {
if (list.length >= raffleTicketConfig.maxCount) {
break;
}
list = list.concat(MengUtils.ArrUtil.randArr(listTemp));
}
return list;
}Draw Animation
js
PlayerEvents.tick(event => {
let player = event.getPlayer();
let playerAttachedData = player.getData();
let tickCount = event.getServer().getTickCount();
if (playerAttachedData.get("lotteryState")) {
let countMax = playerAttachedData.get("poolCountMax");
let count = playerAttachedData.get("count");
let itemShow = itemShowFunc(player,count,countMax);
if (countMax - count <= 3) {
if (tickCount % 10 == 0){
if (count >= countMax){
playerAttachedData.add("lotteryState",false);
let poolList = poolItemList.find(value => Object.keys(value).includes(player.getUuid().toString()))[player.getUuid()];
player.give(Item.of(poolList[count-2]));
player.playNotifySound("minecraft:entity.player.levelup","players",1,1)
paintHide(player)
poolList = [];
}else{
paintShow(player,itemShow.oneItemId,itemShow.twoItemId,itemShow.threeItemId);
playerAttachedData.add("count", count + 1)
player.playNotifySound("minecraft:entity.experience_orb.pickup","players",1,1)
}
}
} else if (countMax - count <= 10) {
if (tickCount % 5 == 0){
paintShow(player,itemShow.oneItemId,itemShow.twoItemId,itemShow.threeItemId);
playerAttachedData.add("count", count + 1)
player.playNotifySound("minecraft:entity.experience_orb.pickup","players",1,2)
}
} else {
paintShow(player,itemShow.oneItemId,itemShow.twoItemId,itemShow.threeItemId);
playerAttachedData.add("count", count + 1)
player.playNotifySound("minecraft:entity.experience_orb.pickup","players",1,2)
}
}
})
function itemShowFunc(player,count,max) {
let uuid = player.getUuid();
let poolList = poolItemList.find(value => Object.keys(value).includes(uuid.toString()))[uuid];
let two_item = poolList[Number(count)];
let one_item = poolList[count - 1 == -1 ? max - 1 : count - 1];
let three_item;
if (count - 2 == -2) {
three_item = poolList[max - 2]
} else if (count - 2 == -1) {
three_item = poolList[max - 1]
} else if (count - 2 >= 0) {
three_item = poolList[count - 2]
}
return {
oneItemId:one_item,
twoItemId:two_item,
threeItemId: three_item
}
}
function paintHide(player) {
player.paint({
item_one: {
visible: false
}, item_two: {
visible: false
}, item_three: {
visible: false
}
})
}
function paintShow(player, one_item, two_item, three_item) {
player.paint({
item_one: {
type: "item",
item: two_item,
visible: true,
w: raffleTicketConfig.sideItemShowSize,
h: raffleTicketConfig.sideItemShowSize,
x: "$screenW/2-" + raffleTicketConfig.itemShowInterval,
y: "$screenH/2+sin(time()*0.5)*$screenH/32",
},
item_two: {
type: "item",
item: one_item,
visible: true,
w: raffleTicketConfig.middleItemShowSize,
h: raffleTicketConfig.middleItemShowSize,
x: "$screenW/2",
y: "$screenH/2+sin(time()*0.5)*$screenH/32",
},
item_three: {
type: "item",
item: three_item,
visible: true,
w: raffleTicketConfig.sideItemShowSize,
h: raffleTicketConfig.sideItemShowSize,
x: "$screenW/2+" + raffleTicketConfig.itemShowInterval,
y: "$screenH/2+sin(time()*0.5)*$screenH/32",
}
})
}Notes
- Different loot pools are selected by the
ticketTypein each ticket item's NBT. A default pool (basalItemList) is included as a fallback when NBT is missing. If you do not need extra pools, you can modify only the code in Add Raffle Loot Entries. - This project is only an example; many parts are not necessarily optimal and can be improved.
- If you improve this project, you can upload your revised code to the .