跳到主要内容

开发信息

了解此内容信息你需要学会java或kotlin

事件

XLReloadEvent 在插件重载时调用,不可取消

DrawEvent 在玩家抽奖时调用,不可取消。

自定义抽奖动画

对于单抽和十连抽的实现是类似的,这里只以单抽为例。

abstract class SingleAnim:InventoryHolder {
abstract val name:String //动画内部名
abstract val i18nName:String //汉化名
var award:Award? = null //奖品,draw方法调用前获得
var mustBeCrate:Boolean = false //是否必须为抽奖箱动画
abstract fun draw(player: Player,lottery: Lottery,crateLocation:Location?) //抽奖方法入口
fun register() { //动画注册
AnimManager.singleAnim[name] = this.javaClass
}
open fun finish(){} //当关闭gui时会调用此块内容
}

以默认抽奖为例

class DefaultAnim:SingleAnim() {
private val inv = Bukkit.createInventory(this,3*9,Message.DefaultSingleAnimTitle.get().color())
override val name: String
get() = "Default"
override val i18nName: String
get() = Message.DefaultSingleAnimName.get().color()

override fun getInventory(): Inventory {
return inv
}
var animTask: Task? = null

override fun draw(player: Player, lottery: Lottery, crateLocation: Location?) {
crateLocation?.block?.let {
//1.16+
try {
if(NMSManager.versionToInt>=16&&it.state is Lidded){
(it.state as Lidded).open()
SchedulerManager.getScheduler(crateLocation).runTaskLater(100L){
(it.state as Lidded).close()
val firework: Firework = crateLocation.world.spawn(crateLocation, Firework::class.java)
val fireworkMeta = firework.fireworkMeta
val builder = FireworkEffect.builder()
builder.withColor(Color.AQUA)
builder.with(FireworkEffect.Type.BALL)
fireworkMeta.addEffect(builder.build())
fireworkMeta.power = 1 // 设置烟花弹的强度
firework.fireworkMeta = fireworkMeta
}
}
}catch (_:Exception){
}

}
SchedulerManager.getScheduler().runTaskAsynchronously{
val award = award
if (award==null){
Message.AwardNull.get().send(player)
return@runTaskAsynchronously
}
loadInv()
val showItemList = ArrayList<ItemStack>()

for (i in 0..40) {
val itemStack = lottery.getRandomAward()!!.toDisplayItem()
showItemList.add(itemStack)
}
//get award
showItemList[20] = award.toDisplayItem()

SchedulerManager.getScheduler().runTask{
player.openInventory(inv)
}

var j = 0
var stop = false
animTask = SchedulerManager.getScheduler().runTaskTimerAsynchronously(0L,5L){
for (i in 9..17) {
val myItem = MyItemBuilder(showItemList[i - 9 + j])
if (i == 13) inventory.setItem(i, myItem.addEnchant().getItem()) else inventory.setItem(
i,
myItem.getItem()
)
}
j++
if (j >= showItemList.size - 9) {
j = 0
}
if (j == 17) { // 中奖物品的位置是第 17 21-4
stop = true
}
val pitch = 2.0.pow(j / 18.0).toFloat()
player.playSound(player.location, Sounds.PLING.get(), 1.0f, pitch)
if (stop) {
animTask?.cancel()
player.playSound(player.location, Sounds.LEVEL_UP.get(), 1.0f, 1.0f)
}
}
}
}

override fun finish() {
animTask?.cancel()
}

private fun loadInv(){
var i = 0
while (i < 27) {
inv.setItem(i, PresetItem.BORDER_GLASS.getItem())
if (i == 8) i = 17
i++
}
inv.setItem(4, PresetItem.BORDER_GLASS2.getItem())
inv.setItem(22, PresetItem.BORDER_GLASS2.getItem())

}
}

对于方法fun draw(player: Player, lottery: Lottery, crateLocation: Location?)

传入的crateLocation,如果不为null则说明玩家是右键抽奖箱来抽奖的。 这里是为了提供未来可能的 如地形操作 粒子特效 等抽奖动画。

然后在finish填入取消任务,如果玩家在动画结束前提前关闭了gui。

另外,你需要注意的是。 如果你的抽奖动画打开了gui,那么就不要手动给与award了。 因为在gui关闭时会统一发放奖品。

最后,别忘了DefaultAnim().register()

自定义抽奖计算器

如果你想要无放回的保底抽奖,那么来尝试自己写一个吧!

那么第一步 你还是需要继承抽象类 cn.xgpjun.xgplottery2.lottery.calculator.Calculator

abstract class Calculator {
abstract fun getAward(player: Player,lottery: Lottery):Award? //获得奖品的方法, 你需要实现它
fun register(name:String){ // 注册, 别忘了注册!
DrawManager.calculators[name] = this
}
}

好,仍以我的保底为例。

class GuaranteedCalculator:Calculator() {
override fun getAward(player: Player, lottery: Lottery): Award? {
val playerData = DatabaseManager.getPlayerData(player.uniqueId)
val nonGuaranteedCount = playerData.customData.getOrDefault("nonGuaranteed${lottery.name}",0).int() + 1
playerData.addCount(lottery)
val guaranteedCount = lottery.getGuaranteedCount()
//奖池拥有保底机制
if (guaranteedCount is Int && guaranteedCount>0 ){
//满足保底情况
if (nonGuaranteedCount>=guaranteedCount){
playerData.customData["nonGuaranteed${lottery.name}"] = 0
val guaranteedList = lottery.awards.values.filter { it.isGuaranteed() }
val totalWeight = guaranteedList.sumOf { it.weight }
val randomWeight = (1..totalWeight).random()
var cumulativeWeight = 0
for (award in guaranteedList){
cumulativeWeight += award.weight
if (randomWeight <= cumulativeWeight){
return award
}
}
}else{
playerData.customData["nonGuaranteed${lottery.name}"] = nonGuaranteedCount
}
}
//其他情况、 提前出了
val award = NormalCalculator.onlyGetAward(lottery)
if (award?.isGuaranteed() == true){
playerData.customData["nonGuaranteed${lottery.name}"] = 0
}
return award
}

}

你可以读取与存放一些数据在customData节点内,例如本例中的 ”距离上次未保底数“ 和 ”奖品是否为保底“ 等数据。

如果你希望进行无放回的抽奖,那么一点思路是在满足保底情况时,在filter { it.isGuaranteed() } 里面添加一些条件, 比如已经存放在了玩家的customData中的某些数值。。 当然 也可以是其他方法。

自定义粒子特效

这里你需要继承抽象类 "cn.xgpjun.xgplottery2.crate.particle.CrateParticleObject"

abstract class CrateParticleObject(val crate: Crate?) {
abstract fun show()
abstract fun clear()
fun register(name:String){ //注册
ParticleManager.particles[name] = this.javaClass
}
}

这里就很简单了, 只有显示和隐藏两个方法。 你可以在里面做你爱做的事情。

本插件使用了莫老的particlelib

class DefaultParticle(crate: Crate?):CrateParticleObject(crate){
var task : Task? =null

override fun show(){
val c = Circle2(crate?.getLocation()!!.clone().add(0.5,0.0,0.5))
c.setPeriod(2L)
c.color = Color.AQUA
c.radius = 2.0.pow(0.5)/2
task = SchedulerManager.getScheduler().runTaskTimerAsynchronously(0L,4L){
for (i in 0..10) {
c.playNextPoint()
}
}
}
override fun clear(){
task?.cancel()
}
}

class Circle2(location: Location): Circle(location){
private var currentAngle = 0.0
override fun playNextPoint() {
currentAngle += step
val radians = Math.toRadians(currentAngle)
val x = radius * cos(radians)
val z = radius * sin(radians)
val y = (currentAngle/angle)*2
spawnParticle(origin.clone().add(x, y, z))
spawnParticle(origin.clone().add(-x, y, -z))

// 进行重置
if (currentAngle > angle) {
currentAngle = 0.0
}
}
}