Beginner - How to add a custom script?

Various development-related discussions
Post Reply
User avatar
FiftyTifty
Noob
Posts: 9
Joined: Mon Mar 06, 2023 6:53 pm
Been thanked: 1 time

Beginner - How to add a custom script?

Post by FiftyTifty »

Found this cool project earlier today, and had a go at getting the server downloaded, set up, and compiled. The wiki instructions were on point, so it was not difficult at all.

Now I'd like to start with a custom script that does the following:

1. Print a message to the console when the server starts
2. Spawn an NPC at Lumbridge (copying the coords from man with ID 1: 3222,3221,0,1,3)
3. Make that NPC say something every 5 seconds.

Hopefully it would be simple, but I cannot find any instructions on creating a basic script for the server. The closest I could find is the tutorial for converting plugins to listeners, which are used for the player interacting with the UI (right-click -> examine, right-click -> pick up, right-click -> attack, etc). Which is a different thing entirely.

Is there any info on doing this?

I've done this sort of thing for games before, with mods I made for Elder Scrolls, Fallout, Total War, etc., but they came with pretty expansive documentation. I'd be happy to help with writing it, but I need to learn how to actually do the basics first.
User avatar
Ceikry
Site Admin
Posts: 585
Joined: Wed Aug 10, 2022 11:48 pm
Location: Draynor Village
Has thanked: 82 times
Been thanked: 103 times

Re: Beginner - How to add a custom script?

Post by Ceikry »

There's about 5 different ways to do this and 3 of them are correct depending on purpose.
Nerds
User avatar
FiftyTifty
Noob
Posts: 9
Joined: Mon Mar 06, 2023 6:53 pm
Been thanked: 1 time

Re: Beginner - How to add a custom script?

Post by FiftyTifty »

Ceikry wrote: Mon Mar 06, 2023 7:15 pm There's about 5 different ways to do this and 3 of them are correct depending on purpose.
I'm looking to do this with Java, rather than simple database or JSON entries. My end goal is to dynamically spawn unique and persistent NPCs, create sandbox behaviour for them, and remove them completely when they die.

Big goals, but obviously I need to start with baby bird steps at first, so starting with a simple Java script should be good?
User avatar
Ceikry
Site Admin
Posts: 585
Joined: Wed Aug 10, 2022 11:48 pm
Location: Draynor Village
Has thanked: 82 times
Been thanked: 103 times

Re: Beginner - How to add a custom script?

Post by Ceikry »

We use kotlin, not java. Java is strictly old code that has barely been touched in nearly 8 years.

As for the "unique/custom NPC", that's hardly in the realm of probability for you. I don't say that to demean you, I say that because it's not something particularly easy to do with runescape in general, at least not without the right tools and the right foreknowledge. It's not a simple task, essentially.
Nerds
User avatar
FiftyTifty
Noob
Posts: 9
Joined: Mon Mar 06, 2023 6:53 pm
Been thanked: 1 time

Re: Beginner - How to add a custom script?

Post by FiftyTifty »

Oh of course, I'm well aware I'm a complete noob going into a mature project blind. That's all dream stuff at this point in time. It'd be like trying to code something on the level of the adventurer bots. I'm keeping the scope real small with this first project: Spawn npc, make them say something.
User avatar
Ceikry
Site Admin
Posts: 585
Joined: Wed Aug 10, 2022 11:48 pm
Location: Draynor Village
Has thanked: 82 times
Been thanked: 103 times

Re: Beginner - How to add a custom script?

Post by Ceikry »

There's about 3 ways to do that. It depends on what you're trying to accomplish. If you want ALL NPCs with the same ID (you cannot create new IDs, so you have to use an existing NPC, and therefor an existing ID) to behave that way, you can use an NPCBehavior attached to that specific ID. If you want only *that spawned instance* to speak, then you need to write some custom logic that makes specifically that instance speak.
Nerds
User avatar
FiftyTifty
Noob
Posts: 9
Joined: Mon Mar 06, 2023 6:53 pm
Been thanked: 1 time

Re: Beginner - How to add a custom script?

Post by FiftyTifty »

Ah, I understand what you mean. Sorry, I should have clarified better.

Yes, I'd like to spawn a specific instance of an NPC ID, and give it custom logic. That's actually exactly what I'm after; creating unique instances with custom behaviour, from the same base IDs.
User avatar
FiftyTifty
Noob
Posts: 9
Joined: Mon Mar 06, 2023 6:53 pm
Been thanked: 1 time

Re: Beginner - How to add a custom script?

Post by FiftyTifty »

Right, so made a bit of progress. First up, was to add a function to ContentAPI.kt that allows you to spawn an NPC through a script. I added the following block of code:

Code: Select all

/**
 * Spawns an NPC, based off of the ::npc command
 * @param id The ID of the NPC to create an instance of
 * @param location Where to create the NPC
 * @param direction The direction the NPC will be facing when spawned
 * @param respawns (True) NPC will respawn after being killed (False) NPC will not respawn after being killed
 * @param walks (True) NPC can move about (False) NPC will stay in its initial position
 * @param aggressive (True) NPC will aggro (False) NPC will not aggro
 */
fun spawnNPC(id: Int,
             location: Location,
             direction: Direction,
             respawns: Boolean,
             walks: Boolean,
             aggressive: Boolean)
{

    val npcToSpawn = NPC.create(id, location)

    npcToSpawn.setAttribute("spawned:npc", true)
    npcToSpawn.isRespawn = respawns
    npcToSpawn.direction = direction
    npcToSpawn.isAggressive = aggressive

    npcToSpawn.init()

    npcToSpawn.isWalks = walks

}
Now I need to add an actual script file to the server. What's involved in doing that?

Is there a specific folder I should add the .kt file to? Any other code files to edit so my script is called? The couple of tutorials just say to create a Kotin file, with no details on that.

Here's an extremely basic one I cobbled together:

Code: Select all

import api.*

class FyTy_SpawnNPCTest_Listener : InteractionListener {

	override fun defineListeners() {
	
		startup() {
		
			log(this::class.java, Log.ERR, "FyTy - Server started!")
		
		}
		
		shutdown() {
		
			log(this::class.java, Log.ERR, "FyTy - Server stopped!")
		
		}
	
	}

}
/code]
User avatar
FiftyTifty
Noob
Posts: 9
Joined: Mon Mar 06, 2023 6:53 pm
Been thanked: 1 time

Re: Beginner - How to add a custom script?

Post by FiftyTifty »

Made progress!

So to actually add a script, you can save it anywhere you like in the `src\main\core` package. I made a new package called scripts and added a new Kotin class file inside of it.

Image

For the .kt file, you need a few things:

1. Import the ContentAPI and listeners you'll be using. I wanted to listen for server startup and shutdown, so:

Code: Select all

package core.scripts

import core.api.*
import core.api.StartupListener
import core.api.ShutdownListener
import core.tools.Log
2. You then need to make a class for each listener type. Startup() and Shutdown() are in different listener files, which is why I had to import them both above.

So, after the imports:

Code: Select all

class FyTySpawnNPCTestListenerStartup : StartupListener {

    override fun startup() {

        log(this::class.java, Log.ERR, "FyTy - Server started!")

    }

}

class FyTySpawnNPCTestListenerShutdown : ShutdownListener {
    override fun shutdown() {

        log(this::class.java, Log.ERR, "FyTy - Server stopped!")

    }

}
3. You then need to save and build the server as normal.

There will be the following warnings in IdeaJ, but that's normal and should be ignored. This is because we're using listeners to run our code, rather than actually being called directly in another file's source code. Kotin magic, basically.

Image

Upon running, the server will log both lines as expected. The first when it's started, the second when it's shut down.

Image

Voila!
User avatar
FiftyTifty
Noob
Posts: 9
Joined: Mon Mar 06, 2023 6:53 pm
Been thanked: 1 time

Re: Beginner - How to add a custom script?

Post by FiftyTifty »

I got the baiscs done for the script. I'll share it here for posterity.

It has three parts.

1. OnStartup() - Spawns the NPCs, and creates two lists. One for NPCs, and another for their dialogue tick counters.

2. OnTick() - Used to iterate through the list of dialogue tick counters and reduce them by 1 every tick, with set conditions for when they should speak. When their counter is 0, it's reset back to 10.

3. OnShutdown() - Gets rid of the spawned NPCs.

Simple, basic, and a good go for a first script.

Code: Select all


package core.scripts

import core.api.*
import core.api.StartupListener
import core.api.ShutdownListener
import core.game.interaction.MovementPulse
import core.game.node.entity.npc.NPC
import core.game.world.GameWorld
import core.game.world.map.Direction
import core.tools.Log
import core.game.world.map.Location
import core.game.world.map.path.Pathfinder
import core.tools.RandomFunction
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

val locSpawn = Location.create(3225, 3220, 0)
val locDestination = Location.create(3211, 3386, 0)

val listNPCs = ArrayList<NPC>()
val listTickCounters = ArrayList<Int>()

fun FyTySpawnNPC()
{
    for (iCounter in 0..2)
    {
        listNPCs.add(spawnNPC(1, locSpawn, Direction.NORTH, false, true, false))
        listTickCounters.add(RandomFunction.random(1, 10))
    }

}

class FyTySpawnNPCTestListenerTick : TickListener {

    override fun tick()
    {

        for (iCounter in 0.rangeTo(listNPCs.count() - 1))
        {

            listTickCounters[iCounter] -= 1
            when (listTickCounters[iCounter])
            {

                3 -> {
                    listNPCs[iCounter].sendChat("It's almost harvesting season.")
                }

                5 -> {
                    listNPCs[iCounter].sendChat("EH!?")
                }

                9 -> {
                    listNPCs[iCounter].sendChat("Away with you, vile beggar!")
                }

                0 -> {
                    listTickCounters[iCounter] = 10
                }

            }


        }

    }

}

class FyTySpawnNPCTestListenerStartup : StartupListener
{

    override fun startup() {

        log(this::class.java, Log.ERR, "FyTy - Server started!")

        FyTySpawnNPC()

    }

}

class FyTySpawnNPCTestListenerShutdown : ShutdownListener
{
    override fun shutdown()
    {

        log(this::class.java, Log.ERR, "FyTy - Server stopped!")

        for (npcCurrent: NPC in listNPCs)
        {
            npcCurrent.clear()
        }

        listNPCs.clear()

    }

}

Post Reply