Skip to content

Plugin Warnings & Errors

Cliven Mitchell edited this page Feb 3, 2019 · 1 revision

Introduction

This section will explain how to use the various components of the plugin API.



What is the API?

The API is a set of properties, functions, and classes that provide scripting functionality. It acts as a bridge between Java and Kotlin, caching interpreted listeners that events will be posted to.

The rest of this section will elaborate on the specific functionality that the API provides for scripts.


Event interception functions

These kinds of functions listen for events of a specific type. They use event receivers for all functions, which means that they have direct access to all event functions and properties.

import api.predef.*

on(LoginEvent::class) {
    // We can use 'plr' directly from 'LoginEvent'
    // In other words, 'this' = 'LoginEvent'
    plr.sendMessage("You have just logged in! Welcome!")
}

The main event interception function is on. It does not terminate events by default.

import api.predef.*

// Event will continue traversing!
on(CommandEvent::class) {
    plr.sendMessage("Command entered was $name!")
}

To terminate events explicitly, use this.terminate()

import api.predef.*

on(CommandEvent::class) {
    plr.sendMessage("Command entered was $name!")
    terminate() // Event will stop traversing!
}

Implicit terminations happen when on.filter or optimized Matcher functions are used.

import api.predef.*

on(WidgetFirstItemClickEvent::class)
    .filter { widgetId == 5068 }
    .then { ... } // Run and terminate event if condition is satisfied

// Optimized matcher functions
cmd("mypos", RIGHTS_DEV) { ... }

button(4100) { ... }

However, streamlined conditions that do not terminate the event are still possible through on.condition.

import api.predef.*

// Does not terminate the event.
on(WidgetFirstItemClickEvent::class)
    .condition { widgetId == 5068 }
    .then { ... }

For more information, see the api.event package.




Helper properties & functions

Helper properties and functions are available to every script by declaring import api.predef.*.

For more information, see the api.predef package.


Attribute delegates

Attributes are temporary (transient) or permanent (persistent) values assigned to mobs. Delegates allow us to access attributes as if they were properties.

The syntax for using them is as follows

import api.attr.Attr
import io.luna.game.model.mob.Player

var Player.myAttribute by Attr<Int>("my_attribute")

fun someFunction(plr: Player) {
    plr.myAttribute = rand(1, 10)
    plr.sendMessage("my_attribute value is ${plr.myAttribute}")
}

A delegate also exists for creating compact attribute timers.

import api.attr.Stopwatch
import io.luna.game.model.mob.Player

var Player.myTimer by Stopwatch("my_timer")

fun someFunction(plr: Player) {

    // Satisfied if uninitialized or time since last reset > 2000ms
    if(plr.myTimer > 2000) {
        plr.sendMessage("someFunction can only be called once every 2000ms")

        // Reset the timer
        plr.myTimer = -1
    }
}


Shop building

The api.shop package uses receiver functions to create a compact and elegant DSL for building shops.

A simple shop declaration with no items or open listeners looks like

shop {
    name = "Test Shop"
    buy = BuyPolicy.NONE // Shop won't buy any player's items
    restock = RestockPolicy.SLOW // Items will restock very slow
    currency = Currency.COINS // Takes coins 
}

We can stock shops with items by using sell

shop("Test Store") {
    buy = BuyPolicy.NONE
    restock = RestockPolicy.SLOW
    currency = Currency.COINS

    sell {
        "Abyssal whip" x 10
        "Dragon scimitar" x 15

        noted {
             "Lobster" x 500
        }    
        ...
    }
}

And finally, we can add some event listeners using open. The properties inside are forwarded to optimized Matcher functions (and have the same names).

shop("Test Store") {
    buy = BuyPolicy.NONE
    restock = RestockPolicy.SLOW
    currency = Currency.COINS

    sell {
        "Abyssal whip" x 10
        "Dragon scimitar" x 15

        noted {
             "Lobster" x 500
        }    
        ...
    }
    
    open {
        // NPC(520) second click opens this shop
        npc2 = 520

        // Object(2772) first click opens this shop
        object1 = 2772
    }
}

For more complex shop opening logic, an explicit event listener can be declared instead.


Loot tables

Loot tables are used in situations where random item selection from a group of items weighted with varying rarities is required. These include things like

  • Treasure trails
  • Drop tables
  • Barrows
  • Crystal chest

To create a loot table, use lootTable. A basic table looks like so

lootTable {
    rarity(COMMON) { // Use 'rarity' blocks to begin listing items
        "Tinderbox" x 1
        "Hammer" x 1 // Then list item names (or identifiers) and amounts
    }

    rarity(VERY_RARE) {
        "Abyssal whip" x 1
        "Coins" x 100_000..200_000
        "Death rune" x 1000..2000
    }
}

To add noted items, use noted blocks

lootTable {
    rarity(COMMON) {
        "Tinderbox" x 1
        "Hammer" x 1
    }

    rarity(UNCOMMON) {
        "Rune kiteshield" x 1..3
        "Dragon dagger" x 1..2
    }

    rarity(VERY_RARE) {
        "Abyssal whip" x 1
        "Coins" x 100_000..200_000
        "Death rune" x 1000..2000

        // Noted block makes items noted, with underlying rarity
        noted {
            "Iron ore" x 25..50
            "Gold ore" x 5..10
        }
    }
}

To select items from a loot table, use pick and pickAll.

val myLootTable = ... ;

on(ServerLaunchEvent::class) {
    // 'pick' rolls on a single item
    println(myLootTable.pick()?.itemDef?.name)
    
    // 'pickAll' rolls on ALL items
    for(item in myLootTable.pickAll()) {
        println(item.itemDef.name)
    }
}

For more information, see the api.item package.


Continue to next section.