Overhauled the Lumbridge jobs system to be more authentic

Added new class of job - bone burying
Made the Task List item functional
Overhauled the dialogues of all of the applicable employers to be more authentic to 2009
Players must now return to the employer who gave them the job to turn it in
Added development command ::clearjob to clear the player's current job
This commit is contained in:
Swizey
2023-02-14 05:30:03 +00:00
committed by Ryan
parent 0d127b109a
commit 79696dfdde
19 changed files with 993 additions and 466 deletions
+2 -2
View File
@@ -115906,8 +115906,8 @@
"bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
},
{
"destroy_message": "You can get another of these from Doric.",
"examine": "Doric's instructions for the task he gave you.",
"destroy_message": "You can get another by talking about jobs with citizens and tutors around Lumbridge.",
"examine": "This reminds me of my current job and who set me to the task.",
"durability": null,
"name": "Task list",
"tradeable": "false",
@@ -1,83 +0,0 @@
package content.global.activity.jobs
import core.api.*
import content.global.activity.jobs.impl.GatheringJobs
import content.global.activity.jobs.impl.SlayingJobs
import core.game.dialogue.FacialExpression
import core.game.node.entity.npc.NPC
import core.game.node.item.Item
import org.rs09.consts.Items
import core.game.dialogue.DialogueFile
import core.game.dialogue.Topic
import core.tools.END_DIALOGUE
import core.tools.START_DIALOGUE
class CancelJobDialogueFile : DialogueFile() {
override fun handle(componentID: Int, buttonID: Int) {
when (stage) {
START_DIALOGUE -> showTopics(
Topic(core.game.dialogue.FacialExpression.HALF_ASKING, "How am I doing on my job?", 1),
Topic(core.game.dialogue.FacialExpression.HALF_GUILTY, "I'd like to cancel my job.", 10)
)
1 -> {
val jobType = JobType.values()[getAttribute(player!!, "jobs:type", 0)]
val jobId = getAttribute(player!!, "jobs:id", 0)
val needed = getAttribute(player!!, "jobs:amount", 0)
when (jobType) {
JobType.GATHERING -> {
sendItemDialogue(
player!!,
Item(GatheringJobs.values()[jobId].itemId),
"You still need to gather $needed more."
)
}
JobType.SLAYING -> {
sendDialogue(
player!!,
"You still need to kill $needed more ${NPC(SlayingJobs.values()[jobId].ids[0]).name.lowercase()}"
)
}
}
stage = END_DIALOGUE
}
10 -> npc("It will cost 500 coins.")
.also { stage++ }
11 -> showTopics(
Topic(core.game.dialogue.FacialExpression.HAPPY, "Yes, please.", 20),
Topic(core.game.dialogue.FacialExpression.NEUTRAL, "No, thanks.", 30)
)
20 -> npc("Alright then, hand over the money.")
.also { stage++ }
21 -> if (inInventory(player!!, Items.COINS_995, 500)) {
player("Here you go.")
removeItem(player!!, Item(Items.COINS_995, 500))
removeAttribute(player!!, "jobs:id")
removeAttribute(player!!, "jobs:amount")
removeAttribute(player!!, "jobs:original_amount")
removeAttribute(player!!, "jobs:type")
stage = END_DIALOGUE
} else {
player(
core.game.dialogue.FacialExpression.HALF_WORRIED,
"Oh, I don't seem to have the money..."
).also { stage++ }
}
22 -> npc("Ah, that sucks! Get to work.")
.also { stage = END_DIALOGUE }
30 -> npc("Alright then, get to work!")
.also { stage = END_DIALOGUE }
}
}
}
@@ -0,0 +1,174 @@
package content.global.activity.jobs
import content.global.activity.jobs.impl.BoneBuryingJobs
import content.global.activity.jobs.impl.CombatJobs
import content.global.activity.jobs.impl.ProductionJobs
import core.api.addItem
import core.api.amountInInventory
import core.api.hasAnItem
import core.game.dialogue.DialogueFile
import core.game.dialogue.FacialExpression
import core.game.dialogue.IfTopic
import core.game.dialogue.Topic
import core.game.node.entity.npc.NPC
import core.game.node.item.Item
import core.tools.END_DIALOGUE
import core.tools.START_DIALOGUE
import core.tools.StringUtils
import org.rs09.consts.Items
/**
* The dialogue file to handle a player asking about work with the employer that assigned them
* their current job.
*/
class CheckJobDialogueFile : DialogueFile() {
companion object {
const val TURN_IN_ITEMS_1 = 1
const val TURN_IN_ITEMS_2 = 2
const val TURN_IN_ITEMS_3 = 3
const val TURN_IN_ITEMS_4 = 4
const val TURN_IN_ITEMS_5 = 5
const val REMIND_JOB = 101
const val GET_TASK_LIST = 201
const val GET_NEW_JOB_1 = 301
const val GET_NEW_JOB_2 = 302
const val GET_NEW_JOB_3 = 303
const val GET_NEW_JOB_4 = 304
const val GET_NEW_JOB_5 = 305
const val GET_NEW_JOB_NONE = 306
const val GET_NEW_JOB_REACHED_LIMIT = 307
}
override fun handle(componentID: Int, buttonID: Int) {
val playerJobManager: JobManager = JobManager.getInstance(player!!)
val job = playerJobManager.job ?: return
val originalAmount = playerJobManager.jobOriginalAmount
when (stage) {
START_DIALOGUE -> showTopics(
IfTopic(
FacialExpression.HAPPY,
"I have the items you wanted.",
TURN_IN_ITEMS_1,
(job.type == JobType.PRODUCTION && amountInInventory(player!!, (job as ProductionJobs).itemId) > 0)
), Topic(
FacialExpression.HALF_ASKING, "Can you remind me what job you gave me?", REMIND_JOB
), Topic(
FacialExpression.HALF_ASKING, "Can I have a task list to remind me of my job?", GET_TASK_LIST
), Topic(
FacialExpression.HALF_GUILTY, "I'd like a new job please.", GET_NEW_JOB_1
)
)
TURN_IN_ITEMS_1 -> {
job as ProductionJobs
val itemName = Item(job.itemId).name.lowercase()
val itemNamePluralized = if (playerJobManager.jobAmount > 1) StringUtils.plusS(itemName) else itemName
npcl("Splendid! Now, I needed $originalAmount $itemNamePluralized.")
stage = TURN_IN_ITEMS_2
}
TURN_IN_ITEMS_2 -> {
if (playerJobManager.jobAmount == originalAmount) {
npcl("So far, you haven't brought me any, so I'm waiting for ${playerJobManager.jobAmount}.")
} else {
npcl("So far, you have brought me ${originalAmount - playerJobManager.jobAmount}, so I'm waiting for ${playerJobManager.jobAmount}.")
}
stage = TURN_IN_ITEMS_3
}
TURN_IN_ITEMS_3 -> {
if (amountInInventory(player!!, (job as ProductionJobs).itemId) >= playerJobManager.jobAmount) {
npcl("I can see that you have plenty, would you like to hand some over to complete your job?")
} else {
npcl("I can see that you have some, would you like to hand them over?")
}
stage = TURN_IN_ITEMS_4
}
TURN_IN_ITEMS_4 -> showTopics(
Topic("Yes, I'll give you what I have.", TURN_IN_ITEMS_5, skipPlayer = true),
Topic("No, I'll keep hold of it for now.", END_DIALOGUE, skipPlayer = true)
)
TURN_IN_ITEMS_5 -> {
playerJobManager.turnInItems()
if (playerJobManager.jobAmount == 0) {
playerJobManager.rewardPlayer()
npcl("There you go, thanks for your help. You're quite a skilled worker!")
} else {
npcl("Thanks for your help. Come back when you've got more!")
}
stage = END_DIALOGUE
}
REMIND_JOB -> {
when (job.type) {
JobType.PRODUCTION -> {
job as ProductionJobs
val itemName = Item(job.itemId).name.lowercase()
val itemNamePluralized =
if (playerJobManager.jobAmount > 1) StringUtils.plusS(itemName) else itemName
npcl("You still need to gather ${playerJobManager.jobAmount} more $itemNamePluralized.")
}
JobType.BONE_BURYING -> {
job as BoneBuryingJobs
val boneName = Item(job.boneIds.first()).name.lowercase()
val boneNamePluralized =
if (playerJobManager.jobAmount > 1) StringUtils.plusS(boneName) else boneName
npcl("You still need to bury ${playerJobManager.jobAmount} more $boneNamePluralized in the ${job.buryArea.title}.")
}
JobType.COMBAT -> {
job as CombatJobs
val monsterName = NPC(job.monsterIds.first()).name.lowercase()
val monsterNamePluralized =
if (playerJobManager.jobAmount > 1) StringUtils.plusS(monsterName) else monsterName
npcl("You still need to kill ${playerJobManager.jobAmount} more $monsterNamePluralized.")
}
}
stage = END_DIALOGUE
}
GET_TASK_LIST -> {
if (hasAnItem(player!!, Items.TASK_LIST_13464).exists()) {
npcl("You already have one of those!")
} else {
when (addItem(player!!, Items.TASK_LIST_13464)) {
true -> npcl("Here you go then.")
false -> dialogue("You don't have enough space in your inventory.")
}
}
stage = END_DIALOGUE
}
GET_NEW_JOB_1 -> npcl("Let me see if I've got any work for you.").also {
if (playerJobManager.hasReachedJobLimit()) {
stage = GET_NEW_JOB_REACHED_LIMIT
} else {
playerJobManager.generate(npc!!)
stage = if (playerJobManager.hasJob()) GET_NEW_JOB_2 else GET_NEW_JOB_NONE
}
}
GET_NEW_JOB_2 -> npcl(playerJobManager.getAssignmentMessage()).also { stage = GET_NEW_JOB_3 }
// Historically, the player would also have the option to ask for an easier task
GET_NEW_JOB_3 -> playerl("Okay, off I go!").also { stage = GET_NEW_JOB_4 }
GET_NEW_JOB_4 -> npcl("There, I've updated your task list to show your new job. Best of luck!").also {
stage = GET_NEW_JOB_5
}
GET_NEW_JOB_5 -> playerl("Thanks.").also { stage = END_DIALOGUE }
GET_NEW_JOB_NONE -> npcl("I'm sorry, I don't currently have any jobs that you're qualified for.").also {
stage = END_DIALOGUE
}
GET_NEW_JOB_REACHED_LIMIT -> npcl("You've hit your limit for the day. Come back tomorrow!").also {
stage = END_DIALOGUE
}
}
}
}
@@ -0,0 +1,23 @@
package content.global.activity.jobs
import content.global.activity.jobs.impl.Employers
import core.tools.RandomFunction
/**
* The interface for a job that a player can be assigned.
*
* TODO: Once player data is stored in a database with support for migrations, implement a per-player field to store
* the player's current employer, so that multiple employers can assign the same job, but the player will only be
* able to turn the job into the employer that assigned it to them.
*/
interface Job {
val type: JobType
val lower: Int
val upper: Int
val employer: Employers
fun getAmount(): Int {
return RandomFunction.random(lower, upper + 1)
}
}
@@ -1,114 +1,215 @@
package content.global.activity.jobs
import content.global.activity.jobs.impl.BoneBuryingJobs
import content.global.activity.jobs.impl.CombatJobs
import content.global.activity.jobs.impl.ProductionJobs
import core.ServerStore
import core.ServerStore.Companion.getInt
import core.api.*
import content.global.activity.jobs.impl.GatheringJobs
import content.global.activity.jobs.impl.SlayingJobs
import core.game.event.BoneBuryEvent
import core.game.event.EventHook
import core.game.event.JobAssignmentEvent
import core.game.event.NPCKillEvent
import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.tools.StringUtils
import org.json.simple.JSONObject
import org.rs09.consts.Items
import core.ServerStore
import core.ServerStore.Companion.getInt
import java.lang.Integer.min
object JobManager {
@JvmStatic
fun updateJobRemaining(player: Player, amount: Int = 0) {
val cur = getAttribute(player, "jobs:amount", 0)
val new = cur - amount
val jobType = JobType.values()[getAttribute(player, "jobs:type", 0)]
val jobId = getAttribute(player, "jobs:id", 0)
/**
* Manages the player's job data & progression.
*
* @source https://runescape.wiki/w/Jobs?oldid=831782
*/
class JobManager(val player: Player? = null) : LoginListener, PersistPlayer {
var job: Job? = null
var jobAmount: Int = 0
var jobOriginalAmount: Int = 0
when (jobType) {
JobType.GATHERING -> { }
private fun hasLevelRequirement(job: ProductionJobs): Boolean {
if (player == null) return false
if (player.isArtificial) return false
JobType.SLAYING -> {
val job = SlayingJobs.values()[jobId]
if (new % 5 == 0 && new > 0) {
sendMessage(
player,
"You have $new ${NPC(job.ids[0]).name.lowercase()} left to go."
)
} else if (new == 0) {
sendMessage(
player,
"<col=00AA00>You have completed your job.</col>"
)
}
}
}
setAttribute(player, "jobs:amount", new)
return getStatLevel(player, job.skill) >= job.lvlReq
}
@JvmStatic
fun rewardPlayer(player: Player, npc: NPC) {
val amt = getAttribute(player, "jobs:original_amount", 0)
val jobType = JobType.values()[getAttribute(player, "jobs:type", 0)]
val jobId = getAttribute(player, "jobs:id", 0)
fun hasJob(): Boolean = this.job != null
when (jobType) {
JobType.GATHERING -> {
val it = Item(GatheringJobs.values()[jobId].itemId)
var amount = amountInInventory(player, it.id)
val needed = getAttribute(player, "jobs:amount", 0)
// Historically the job limit would be 6 jobs per hour of play-time, using 3 per day for balance purposes
fun hasReachedJobLimit(): Boolean = player?.let { getStoreFile().getInt(it.username.lowercase()) >= 3 } ?: false
if (amount == 0) {
openDialogue(player, CancelJobDialogueFile(), npc)
return
}
fun generate(npc: NPC) {
if (player == null) return
if (player.isArtificial) return
if (amount < needed) {
sendItemDialogue(
player,
GatheringJobs.values()[jobId].itemId,
"You still need to gather ${needed - amount} more."
)
val instance = getInstance(player)
removeItem(player, Item(it.id, amount))
setAttribute(player, "jobs:amount", needed - amount)
return
}
if (amount > needed) {
amount = needed
}
removeItem(player, Item(it.id, amount))
}
JobType.SLAYING -> {
val needed = getAttribute(player, "jobs:amount", 0)
if (needed > 0) {
sendDialogue(
player,
"You still need to kill $needed more ${NPC(SlayingJobs.values()[jobId].ids[0]).name.lowercase()}."
)
return
}
sendDialogue(
player,
"Excellent work, thank you! Here's your reward."
// Create a list of jobs that the player can do and are given by the NPC being interacted with
val potentialJobs: List<Job> = (
ProductionJobs.values().filter { hasLevelRequirement(it) }
+ BoneBuryingJobs.values()
+ CombatJobs.values()
)
}
}
.filter { job -> job.employer.npcId == npc.id }
addItemOrDrop(player, Items.COINS_995, 250 * amt)
val job = potentialJobs.randomOrNull() ?: return
removeAttribute(player, "jobs:id")
removeAttribute(player, "jobs:amount")
removeAttribute(player, "jobs:original_amount")
removeAttribute(player, "jobs:type")
instance.job = job
this.jobAmount = job.getAmount()
this.jobOriginalAmount = instance.jobAmount
player.dispatch(JobAssignmentEvent(job.type, npc))
getStoreFile()[player.username.lowercase()] = getStoreFile().getInt(player.username.lowercase()) + 1
}
fun getStoreFile(): JSONObject {
return ServerStore.getArchive("daily-jobs-tracking")
fun getAssignmentMessage(): String? {
if (player == null) return null
if (player.isArtificial) return null
val instance = getInstance(player)
val job = instance.job ?: return null
return when (job.type) {
JobType.PRODUCTION -> {
job as ProductionJobs
val itemName = Item(job.itemId).name.lowercase()
val itemNamePluralized = if (this.jobAmount > 1) StringUtils.plusS(itemName) else itemName
"Okay, your job is to bring me ${this.jobAmount} $itemNamePluralized."
}
JobType.BONE_BURYING -> {
job as BoneBuryingJobs
val boneName = Item(job.boneIds.first()).name.lowercase()
val boneNamePluralized = if (this.jobAmount > 1) StringUtils.plusS(boneName) else boneName
"Okay, your job is to bury ${this.jobAmount} $boneNamePluralized in the ${job.buryArea.title}."
}
JobType.COMBAT -> {
job as CombatJobs
val monsterName = NPC(job.monsterIds.first()).name.lowercase()
val monsterNamePluralized = if (this.jobAmount > 1) StringUtils.plusS(monsterName) else monsterName
"Okay, your job is to kill ${this.jobAmount} $monsterNamePluralized."
}
}
}
/**
* TODO: Allow players to turn-in noted items (authentic to 2009)
*/
fun turnInItems() {
if (player == null) return
if (player.isArtificial) return
val instance = getInstance(player)
val job = instance.job ?: return
if (job.type != JobType.PRODUCTION) return
val itemId = (job as ProductionJobs).itemId
val amountHeld = amountInInventory(player, itemId)
val amountToTurnIn = min(amountHeld, instance.jobAmount)
removeItem(player, Item(itemId, amountToTurnIn))
instance.jobAmount -= amountToTurnIn
}
fun rewardPlayer() {
if (player == null) return
if (player.isArtificial) return
val instance = getInstance(player)
addItemOrDrop(player, Items.COINS_995, 250 * instance.jobOriginalAmount)
instance.job = null
instance.jobAmount = -1
instance.jobOriginalAmount = -1
}
override fun login(player: Player) {
if (player.isArtificial) return
val jobManager = JobManager(player)
setAttribute(player, "job-manager", jobManager)
player.hook(Event.BoneBuried, object : EventHook<BoneBuryEvent> {
override fun process(entity: Entity, event: BoneBuryEvent) {
if (entity !is Player) return
if (entity.isArtificial) return
val instance = getInstance(entity)
if (!instance.hasJob()) return
if (instance.job?.type != JobType.BONE_BURYING) return
if (instance.jobAmount == 0) return
val job = (instance.job as BoneBuryingJobs)
if (inBorders(entity, job.buryArea.zoneBorder) && event.boneId in job.boneIds) {
instance.jobAmount--
}
}
})
player.hook(Event.NPCKilled, object : EventHook<NPCKillEvent> {
override fun process(entity: Entity, event: NPCKillEvent) {
if (entity !is Player) return
if (entity.isArtificial) return
val instance = getInstance(entity)
if (!instance.hasJob()) return
if (instance.job?.type != JobType.COMBAT) return
if (instance.jobAmount == 0) return
val job = (instance.job as CombatJobs)
if (event.npc.id in job.monsterIds) {
instance.jobAmount--
}
}
})
}
// TODO: Implement proper saving of job data to JSONObject once player save migration is possible
override fun savePlayer(player: Player, save: JSONObject) {
val instance = getInstance(player)
val jobId: Int = instance.job?.let {
when (it.type) {
JobType.PRODUCTION -> ProductionJobs.values().indexOf(it)
JobType.COMBAT -> CombatJobs.values().indexOf(it)
JobType.BONE_BURYING -> BoneBuryingJobs.values().indexOf(it)
}
} ?: -1
setAttribute(player, "/save:jobs:id", jobId)
setAttribute(player, "/save:jobs:type", instance.job?.type?.ordinal ?: -1)
setAttribute(player, "/save:jobs:amount", instance.jobAmount)
setAttribute(player, "/save:jobs:original_amount", instance.jobOriginalAmount)
}
// TODO: Implement proper loading of job data from JSONObject once player save migration is possible
override fun parsePlayer(player: Player, data: JSONObject) {
val instance = getInstance(player)
val jobId = getAttribute(player, "jobs:id", -1)
val jobType = getAttribute(player, "jobs:type", -1)
instance.job = when (JobType.values().getOrNull(jobType)) {
JobType.PRODUCTION -> ProductionJobs.values().getOrNull(jobId)
JobType.COMBAT -> CombatJobs.values().getOrNull(jobId)
JobType.BONE_BURYING -> BoneBuryingJobs.values().getOrNull(jobId)
else -> null
}
instance.jobAmount = getAttribute(player, "jobs:amount", -1)
instance.jobOriginalAmount = getAttribute(player, "jobs:original_amount", -1)
}
companion object {
@JvmStatic
fun getInstance(player: Player): JobManager {
return player.getAttribute("job-manager", JobManager(player))
}
fun getStoreFile(): JSONObject {
return ServerStore.getArchive("daily-jobs-tracking")
}
}
}
@@ -1,6 +1,13 @@
package content.global.activity.jobs
/**
* An enum of the different types of jobs that a player can be assigned.
*
* Note: Due to how player save files keep track of the player's current job, it is essential that
* new entries are only appended to the end of this enum, and the ordering of existing entries is not changed.
*/
enum class JobType {
GATHERING,
SLAYING
PRODUCTION,
COMBAT,
BONE_BURYING
}
@@ -0,0 +1,139 @@
package content.global.activity.jobs
import content.global.activity.jobs.impl.BoneBuryingJobs
import content.global.activity.jobs.impl.CombatJobs
import content.global.activity.jobs.impl.ProductionJobs
import core.api.addItem
import core.api.hasAnItem
import core.game.dialogue.DialogueFile
import core.game.dialogue.FacialExpression
import core.game.dialogue.Topic
import core.game.node.entity.npc.NPC
import core.game.node.item.Item
import core.tools.END_DIALOGUE
import core.tools.START_DIALOGUE
import core.tools.StringUtils
import org.rs09.consts.Items
/**
* The dialogue file to handle a player asking about work with any employer which they aren't currently working for.
*/
class StartJobDialogueFile : DialogueFile() {
companion object {
const val GET_TASK_LIST_1 = 1
const val GET_TASK_LIST_2 = 2
const val GET_TASK_LIST_YES = 3
const val GET_JOB_1 = 101
const val GET_JOB_2 = 102
const val GET_JOB_3 = 103
const val GET_JOB_4 = 104
const val GET_JOB_5 = 105
const val GET_JOB_NONE = 201
const val GET_JOB_EMPLOYED_1 = 301
const val GET_JOB_EMPLOYED_2 = 302
const val GET_JOB_LIMIT_REACHED = 401
}
override fun handle(componentID: Int, buttonID: Int) {
val playerJobManager: JobManager = JobManager.getInstance(player!!)
when (stage) {
START_DIALOGUE -> playerl(FacialExpression.ASKING, "Do you have any jobs for me?").also {
stage = if (hasAnItem(player!!, Items.TASK_LIST_13464).exists()) {
if (playerJobManager.hasJob()) {
GET_JOB_EMPLOYED_1
} else {
GET_JOB_1
}
} else {
GET_TASK_LIST_1
}
}
GET_TASK_LIST_1 -> npcl("Well, first of all, I can see that you don't have a task list to remind you about your current job. Would you like a task list now?").also {
stage = GET_TASK_LIST_2
}
GET_TASK_LIST_2 -> showTopics(
Topic("Yes, please.", GET_TASK_LIST_YES),
Topic("No, thanks.", if (playerJobManager.hasJob()) GET_JOB_EMPLOYED_1 else GET_JOB_1)
)
GET_TASK_LIST_YES -> {
when (addItem(player!!, Items.TASK_LIST_13464)) {
true -> npcl("Here you go then.").also {
stage = if (playerJobManager.hasJob()) GET_JOB_EMPLOYED_1 else GET_JOB_1
}
false -> dialogue("You don't have enough space in your inventory.").also { stage = END_DIALOGUE }
}
}
GET_JOB_1 -> npcl("Let me see if I've got any work for you.").also {
if (playerJobManager.hasReachedJobLimit()) {
stage = GET_JOB_LIMIT_REACHED
} else {
playerJobManager.generate(npc!!)
stage = if (playerJobManager.hasJob()) GET_JOB_2 else GET_JOB_NONE
}
}
GET_JOB_2 -> npcl(playerJobManager.getAssignmentMessage()).also { stage = GET_JOB_3 }
// Historically, the player would also have the option to ask for an easier task
GET_JOB_3 -> playerl("Okay, off I go!").also { stage = GET_JOB_4 }
GET_JOB_4 -> npcl("There, I've updated your task list to show your new job. Best of luck!").also {
stage = GET_JOB_5
}
GET_JOB_5 -> playerl("Thanks.").also { stage = END_DIALOGUE }
GET_JOB_EMPLOYED_1 -> {
val job = playerJobManager.job ?: return
val employerName = NPC(job.employer.npcId).name
val amount = playerJobManager.jobAmount
val originalAmount = playerJobManager.jobOriginalAmount
if (amount == 0) {
npcl("Hang about. Aren't you working for $employerName? It looks like you've complete their job, return to them for your reward.")
stage = END_DIALOGUE
} else {
when (job.type) {
JobType.PRODUCTION -> {
job as ProductionJobs
val itemName = Item(job.itemId).name.lowercase()
val itemNamePluralized = if (originalAmount > 1) StringUtils.plusS(itemName) else itemName
npcl("Hang about. Aren't you working for $employerName? You were asked for $originalAmount $itemNamePluralized and still have $amount more to go.")
}
JobType.BONE_BURYING -> {
job as BoneBuryingJobs
val boneName = Item(job.boneIds.first()).name.lowercase()
val boneNamePluralized = if (originalAmount > 1) StringUtils.plusS(boneName) else boneName
npcl("Hang about. Aren't you working for $employerName? You were asked to bury $originalAmount $boneNamePluralized in the ${job.buryArea.title} and still have $amount more to go.")
}
JobType.COMBAT -> {
job as CombatJobs
val monsterName = NPC(job.monsterIds.first()).name.lowercase()
val monsterNamePluralized = if (originalAmount > 1) StringUtils.plusS(monsterName) else monsterName
npcl("Hang about. Aren't you working for $employerName? You were asked to kill $originalAmount $monsterNamePluralized and still have $amount more to go.")
}
}
stage = GET_JOB_EMPLOYED_2
}
}
GET_JOB_EMPLOYED_2 -> showTopics(
Topic("Oh yes. I better go and finish their job first.", END_DIALOGUE),
Topic("I'd like a new job instead, please.", GET_JOB_1)
)
GET_JOB_NONE -> npcl("I'm sorry, I don't currently have any jobs that you're qualified for.").also {
stage = END_DIALOGUE
}
GET_JOB_LIMIT_REACHED -> npcl("You've hit your limit for the day. Come back tomorrow!").also {
stage = END_DIALOGUE
}
}
}
}
@@ -0,0 +1,70 @@
package content.global.activity.jobs
import content.global.activity.jobs.impl.BoneBuryingJobs
import content.global.activity.jobs.impl.CombatJobs
import content.global.activity.jobs.impl.ProductionJobs
import core.api.sendDialogue
import core.api.sendItemDialogue
import core.api.sendNPCDialogue
import core.game.interaction.InteractionListener
import core.game.node.entity.npc.NPC
import core.game.node.item.Item
import core.tools.StringUtils
import org.rs09.consts.Items
/**
* Handles the read option of the Task List item.
*/
class TaskListReadListener : InteractionListener {
override fun defineListeners() {
on(Items.TASK_LIST_13464, ITEM, "read") { player, _ ->
val playerJobManager: JobManager = JobManager.getInstance(player)
val job = playerJobManager.job
val amount = playerJobManager.jobAmount
if (job == null) {
sendDialogue(player, "I have not been assigned a job!")
return@on false
}
if(amount == 0) {
sendDialogue(
player, "I have completed my job. I should return to ${NPC(job.employer.npcId).name} for my reward."
)
return@on true
}
when (job.type) {
JobType.PRODUCTION -> {
job as ProductionJobs
val itemName = Item(job.itemId).name.lowercase()
val itemNamePluralized = if (amount > 1) StringUtils.plusS(itemName) else itemName
sendItemDialogue(
player, job.itemId, "My job is to gather $amount more $itemNamePluralized."
)
}
JobType.BONE_BURYING -> {
job as BoneBuryingJobs
val boneName = Item(job.boneIds.first()).name.lowercase()
val boneNamePluralized = if (amount > 1) StringUtils.plusS(boneName) else boneName
sendItemDialogue(
player,
job.boneIds.first(),
"My job is to bury $amount more $boneNamePluralized in the ${job.buryArea.title}."
)
}
JobType.COMBAT -> {
job as CombatJobs
val monsterName = NPC(job.monsterIds.first()).name.lowercase()
val monsterNamePluralized = if (amount > 1) StringUtils.plusS(monsterName) else monsterName
sendNPCDialogue(
player, job.monsterIds.first(), "My job is to kill $amount more $monsterNamePluralized."
)
}
}
return@on true
}
}
}
@@ -1,224 +1,45 @@
package content.global.activity.jobs
import content.global.activity.jobs.impl.GatheringJobs
import content.global.activity.jobs.impl.SlayingJobs
import core.api.*
import core.game.event.EventHook
import core.game.event.JobAssignmentEvent
import core.game.event.NPCKillEvent
import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.item.Item
import org.rs09.consts.Items
import org.rs09.consts.NPCs
import core.ServerStore.Companion.getInt
import core.api.Event
import core.game.interaction.InteractionListener
import content.global.activity.jobs.impl.Employers
import core.api.openDialogue
import core.api.sendNPCDialogue
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.npc.NPC
/**
* Handles the work-for actions for the NPCs
* @author Ceikry
* @author vddCore
*/
class WorkForInteractionListener : InteractionListener, LoginListener {
companion object {
private val WEAPON_ICONS = arrayListOf(
Items.BRONZE_SCIMITAR_1321,
Items.STEEL_SCIMITAR_1325,
Items.RUNE_SCIMITAR_1333,
Items.BRONZE_MACE_1422,
Items.MITHRIL_WARHAMMER_1343
)
private val EMPLOYERS_AND_JOBS = mapOf(
NPCs.HANS_0 to listOf(
GatheringJobs.AIR_RUNE,
GatheringJobs.COWHIDES,
GatheringJobs.WATER_RUNE
),
NPCs.AGGIE_922 to listOf(
GatheringJobs.ASHES,
GatheringJobs.AIR_RUNE,
GatheringJobs.WATER_RUNE
),
NPCs.GILLIE_GROATS_3807 to listOf(
GatheringJobs.COWHIDES
),
NPCs.COOKING_TUTOR_4899 to listOf(
GatheringJobs.CAKE,
GatheringJobs.ANCHOVY_PIZZA,
GatheringJobs.COOKED_SALMON,
GatheringJobs.COOKED_TROUT,
GatheringJobs.MEAT_PIE,
GatheringJobs.MEAT_PIZZA,
GatheringJobs.PLAIN_PIZZA
),
NPCs.FISHING_TUTOR_4901 to listOf(
GatheringJobs.RAW_SALMON,
GatheringJobs.RAW_SHRIMP,
GatheringJobs.RAW_TROUT
),
NPCs.MINING_TUTOR_4902 to listOf(
GatheringJobs.COPPER_ORE,
GatheringJobs.TIN_ORE,
GatheringJobs.COAL,
GatheringJobs.IRON_ORE,
GatheringJobs.SILVER_ORE,
GatheringJobs.GOLD_ORE
),
NPCs.PRAYER_TUTOR_4903 to listOf(
GatheringJobs.SILVER_ORE
),
NPCs.SMELTING_TUTOR_4904 to listOf(
GatheringJobs.BRONZE_BAR,
GatheringJobs.IRON_BAR,
GatheringJobs.STEEL_BAR
),
NPCs.WOODCUTTING_TUTOR_4906 to listOf(
GatheringJobs.LOG,
GatheringJobs.OAK,
GatheringJobs.WILLOW
)
)
private val EMPLOYER_JOB_TYPES = mapOf(
NPCs.HANS_0 to JobType.GATHERING,
NPCs.MELEE_TUTOR_705 to JobType.SLAYING,
NPCs.AGGIE_922 to JobType.GATHERING,
NPCs.RANGED_TUTOR_1861 to JobType.SLAYING,
NPCs.GILLIE_GROATS_3807 to JobType.GATHERING,
NPCs.MAGIC_TUTOR_4707 to JobType.SLAYING,
NPCs.COOKING_TUTOR_4899 to JobType.GATHERING,
NPCs.FISHING_TUTOR_4901 to JobType.GATHERING,
NPCs.MINING_TUTOR_4902 to JobType.GATHERING,
NPCs.PRAYER_TUTOR_4903 to JobType.GATHERING,
NPCs.SMELTING_TUTOR_4904 to JobType.GATHERING,
NPCs.WOODCUTTING_TUTOR_4906 to JobType.GATHERING
)
private val EMPLOYER_IDS = EMPLOYER_JOB_TYPES.keys.toIntArray()
}
override fun login(player: Player) {
val remaining = getAttribute(player, "jobs:amount", -1)
val jobType = JobType.values()[getAttribute(player, "jobs:type", 0)]
if (jobType == JobType.SLAYING && remaining > 0) {
player.hook(Event.NPCKilled, JobKillHook)
}
}
class WorkForInteractionListener : InteractionListener {
override fun defineListeners() {
on(EMPLOYER_IDS, IntType.NPC, "work-for") { player, node ->
if (JobManager.getStoreFile().getInt(player.username.lowercase()) == 3) {
sendNPCDialogue(player, node.id, "You've hit your limit for the day. Come back tomorrow!")
on(
Employers.values().map { employer -> employer.npcId }.toIntArray(),
IntType.NPC,
"work-for"
) { player, node ->
val playerJobManager = JobManager.getInstance(player)
val job = playerJobManager.job
// Check if the player is talking to the NPC that gave them their job
if (job != null && job.employer.npcId == node.id) {
if (playerJobManager.jobAmount == 0) {
playerJobManager.rewardPlayer()
sendNPCDialogue(
player,
job.employer.npcId,
"There you go, thanks for your help. You're quite a skilled worker!"
)
} else {
openDialogue(player, CheckJobDialogueFile(), node as NPC)
}
return@on true
}
if (getAttribute(player, "jobs:id", -1) != -1) {
JobManager.rewardPlayer(player, node.asNpc())
return@on true
}
var amount: Int
val jobType = EMPLOYER_JOB_TYPES[node.id] ?: return@on false
val jobId = when (jobType) {
JobType.GATHERING -> {
val job = EMPLOYERS_AND_JOBS[node.id]?.filter { checkRequirement(player, it) }?.randomOrNull()
if (job == null) {
sendNPCDialogue(
player,
node.id,
"I'm sorry, I don't currently have any jobs that you're qualified for.",
core.game.dialogue.FacialExpression.HALF_THINKING
)
return@on true
}
amount = job.getAmount()
job.ordinal
}
JobType.SLAYING -> {
SlayingJobs.values().random().ordinal.also {
amount = SlayingJobs.values()[it].getAmount()
}
}
}
when (jobType) {
JobType.GATHERING -> {
val job = GatheringJobs.values()[jobId]
sendItemDialogue(
player,
job.itemId,
"You are assigned to gather $amount ${Item(job.itemId).name.lowercase()}"
)
}
JobType.SLAYING -> {
val job = SlayingJobs.values()[jobId]
sendItemDialogue(
player,
WEAPON_ICONS.random(),
"You are assigned to kill $amount ${NPC(job.ids[0]).name.lowercase()}"
)
player.unhook(JobKillHook) //try to unhook just in case they already had it hooked, unlikely but possible.
player.hook(Event.NPCKilled, JobKillHook)
}
}
setAttribute(player, "/save:jobs:id", jobId)
setAttribute(player, "/save:jobs:amount", amount)
setAttribute(player, "/save:jobs:original_amount", amount)
setAttribute(player, "/save:jobs:type", jobType.ordinal)
player.dispatch(JobAssignmentEvent(jobType, node.asNpc()))
openDialogue(player, StartJobDialogueFile(), node as NPC)
return@on true
}
}
private fun checkRequirement(player: Player, jobs: GatheringJobs?): Boolean {
jobs ?: return true
if (getStatLevel(player, jobs.skill) < jobs.lvlReq) {
return false
}
return true
}
private object JobKillHook : EventHook<NPCKillEvent> {
override fun process(entity: Entity, event: NPCKillEvent) {
if (entity !is Player) return
if (entity.isArtificial) return
val job = getAttribute(entity, "jobs:id", -1)
if (job !in 0 until SlayingJobs.values().size) {
return
}
val ids = SlayingJobs.values()[job].ids
if (event.npc.id in ids) {
JobManager.updateJobRemaining(entity, 1)
}
}
}
}
@@ -0,0 +1,30 @@
package content.global.activity.jobs.impl
import content.global.activity.jobs.Job
import content.global.activity.jobs.JobType
import core.game.world.map.zone.ZoneBorders
import org.rs09.consts.Items
/**
* An enum of the possible bone-burying jobs that can be assigned to a player.
*
* Note: Due to how player save files keep track of the player's current job, it is essential that
* new entries are only appended to the end of this enum, and the ordering of existing entries is not changed.
*/
enum class BoneBuryingJobs(val buryArea: BuryArea, val boneIds: List<Int>, override val employer: Employers) : Job {
BURY_DRAYNOR(BuryArea.DRAYNOR_WHEAT_FIELD, Bones.BONES, Employers.PRAYER_TUTOR),
BURY_LUMBRIDGE_GRAVEYARD(BuryArea.LUMBRIDGE_GRAVEYARD, Bones.BONES, Employers.PRAYER_TUTOR);
override val type = JobType.BONE_BURYING
override val lower = 22
override val upper = 26
enum class BuryArea(val zoneBorder: ZoneBorders, val title: String) {
DRAYNOR_WHEAT_FIELD(ZoneBorders(3107, 3265, 3132, 3293), "wheat field east of Draynor Village"),
LUMBRIDGE_GRAVEYARD(ZoneBorders(3238, 3191, 3252, 3204), "Lumbridge Graveyard");
}
object Bones {
val BONES = listOf(Items.BONES_526, Items.BONES_2530)
}
}
@@ -0,0 +1,209 @@
package content.global.activity.jobs.impl
import content.global.activity.jobs.Job
import content.global.activity.jobs.JobType
import org.rs09.consts.NPCs
/**
* An enum of the possible combat jobs that can be assigned to a player.
*
* Note: Due to how player save files keep track of the player's current job, it is essential that
* new entries are only appended to the end of this enum, and the ordering of existing entries is not changed.
*/
enum class CombatJobs(val monsterIds: List<Int>, override val employer: Employers) : Job {
CHICKENS(Monsters.CHICKENS, Employers.RANGED_TUTOR),
COWS(Monsters.COWS, Employers.MAGIC_TUTOR),
GIANT_SPIDERS(Monsters.GIANT_SPIDERS, Employers.MAGIC_TUTOR),
SCORPIONS(Monsters.SCORPIONS, Employers.MELEE_TUTOR),
GOBLINS(Monsters.GOBLINS, Employers.MELEE_TUTOR);
override val type = JobType.COMBAT
override val lower = 22
override val upper = 28
object Monsters {
val GIANT_SPIDERS = listOf(NPCs.GIANT_SPIDER_59, NPCs.GIANT_SPIDER_60)
val SCORPIONS = listOf(NPCs.SCORPION_107, NPCs.SCORPION_1477, NPCs.SCORPION_4402, NPCs.SCORPION_4403)
val GOBLINS = listOf(
NPCs.GOBLIN_100,
NPCs.GOBLIN_101,
NPCs.GOBLIN_102,
NPCs.GOBLIN_1769,
NPCs.GOBLIN_1770,
NPCs.GOBLIN_1771,
NPCs.GOBLIN_1772,
NPCs.GOBLIN_1773,
NPCs.GOBLIN_1774,
NPCs.GOBLIN_1775,
NPCs.GOBLIN_1776,
NPCs.GOBLIN_2274,
NPCs.GOBLIN_2275,
NPCs.GOBLIN_2276,
NPCs.GOBLIN_2277,
NPCs.GOBLIN_2278,
NPCs.GOBLIN_2279,
NPCs.GOBLIN_2280,
NPCs.GOBLIN_2281,
NPCs.GOBLIN_2678,
NPCs.GOBLIN_2679,
NPCs.GOBLIN_2680,
NPCs.GOBLIN_2681,
NPCs.GOBLIN_3264,
NPCs.GOBLIN_3265,
NPCs.GOBLIN_3266,
NPCs.GOBLIN_3267,
NPCs.GOBLIN_3726,
NPCs.GOBLIN_4261,
NPCs.GOBLIN_4262,
NPCs.GOBLIN_4263,
NPCs.GOBLIN_4264,
NPCs.GOBLIN_4265,
NPCs.GOBLIN_4266,
NPCs.GOBLIN_4267,
NPCs.GOBLIN_4268,
NPCs.GOBLIN_4269,
NPCs.GOBLIN_4270,
NPCs.GOBLIN_4271,
NPCs.GOBLIN_4272,
NPCs.GOBLIN_4273,
NPCs.GOBLIN_4274,
NPCs.GOBLIN_4275,
NPCs.GOBLIN_4276,
NPCs.GOBLIN_4407,
NPCs.GOBLIN_4408,
NPCs.GOBLIN_4409,
NPCs.GOBLIN_4410,
NPCs.GOBLIN_4411,
NPCs.GOBLIN_4412,
NPCs.GOBLIN_444,
NPCs.GOBLIN_445,
NPCs.GOBLIN_4479,
NPCs.GOBLIN_4480,
NPCs.GOBLIN_4481,
NPCs.GOBLIN_4482,
NPCs.GOBLIN_4483,
NPCs.GOBLIN_4484,
NPCs.GOBLIN_4485,
NPCs.GOBLIN_4486,
NPCs.GOBLIN_4487,
NPCs.GOBLIN_4488,
NPCs.GOBLIN_4489,
NPCs.GOBLIN_4490,
NPCs.GOBLIN_4491,
NPCs.GOBLIN_4492,
NPCs.GOBLIN_4499,
NPCs.GOBLIN_4633,
NPCs.GOBLIN_4634,
NPCs.GOBLIN_4635,
NPCs.GOBLIN_4636,
NPCs.GOBLIN_4637,
NPCs.GOBLIN_5855,
NPCs.GOBLIN_5856,
NPCs.GOBLIN_6125,
NPCs.GOBLIN_6126,
NPCs.GOBLIN_6132,
NPCs.GOBLIN_6133,
NPCs.GOBLIN_6279,
NPCs.GOBLIN_6280,
NPCs.GOBLIN_6281,
NPCs.GOBLIN_6282,
NPCs.GOBLIN_6283,
NPCs.GOBLIN_6284,
NPCs.GOBLIN_6402,
NPCs.GOBLIN_6403,
NPCs.GOBLIN_6404,
NPCs.GOBLIN_6405,
NPCs.GOBLIN_6406,
NPCs.GOBLIN_6407,
NPCs.GOBLIN_6408,
NPCs.GOBLIN_6409,
NPCs.GOBLIN_6410,
NPCs.GOBLIN_6411,
NPCs.GOBLIN_6412,
NPCs.GOBLIN_6413,
NPCs.GOBLIN_6414,
NPCs.GOBLIN_6415,
NPCs.GOBLIN_6416,
NPCs.GOBLIN_6417,
NPCs.GOBLIN_6418,
NPCs.GOBLIN_6419,
NPCs.GOBLIN_6420,
NPCs.GOBLIN_6421,
NPCs.GOBLIN_6422,
NPCs.GOBLIN_6423,
NPCs.GOBLIN_6424,
NPCs.GOBLIN_6425,
NPCs.GOBLIN_6426,
NPCs.GOBLIN_6427,
NPCs.GOBLIN_6428,
NPCs.GOBLIN_6429,
NPCs.GOBLIN_6430,
NPCs.GOBLIN_6431,
NPCs.GOBLIN_6432,
NPCs.GOBLIN_6433,
NPCs.GOBLIN_6434,
NPCs.GOBLIN_6435,
NPCs.GOBLIN_6436,
NPCs.GOBLIN_6437,
NPCs.GOBLIN_6438,
NPCs.GOBLIN_6439,
NPCs.GOBLIN_6440,
NPCs.GOBLIN_6441,
NPCs.GOBLIN_6442,
NPCs.GOBLIN_6443,
NPCs.GOBLIN_6444,
NPCs.GOBLIN_6445,
NPCs.GOBLIN_6446,
NPCs.GOBLIN_6447,
NPCs.GOBLIN_6448,
NPCs.GOBLIN_6449,
NPCs.GOBLIN_6450,
NPCs.GOBLIN_6451,
NPCs.GOBLIN_6452,
NPCs.GOBLIN_6453,
NPCs.GOBLIN_6454,
NPCs.GOBLIN_6455,
NPCs.GOBLIN_6456,
NPCs.GOBLIN_6457,
NPCs.GOBLIN_6458,
NPCs.GOBLIN_6459,
NPCs.GOBLIN_6460,
NPCs.GOBLIN_6461,
NPCs.GOBLIN_6462,
NPCs.GOBLIN_6463,
NPCs.GOBLIN_6464,
NPCs.GOBLIN_6465,
NPCs.GOBLIN_6466,
NPCs.GOBLIN_6467,
NPCs.GOBLIN_6490,
NPCs.GOBLIN_6491,
NPCs.GOBLIN_6492,
NPCs.GOBLIN_6493,
NPCs.GOBLIN_6494,
NPCs.GOBLIN_6495,
NPCs.GOBLIN_7964,
NPCs.GOBLIN_7965
)
val COWS = listOf(
NPCs.COW_81,
NPCs.COW_397,
NPCs.COW_955,
NPCs.COW_1767,
NPCs.COW_3309,
NPCs.COW_CALF_1766,
NPCs.COW_CALF_1768,
NPCs.COW_CALF_2310
)
val CHICKENS = listOf(
NPCs.CHICKEN_1017,
NPCs.CHICKEN_1401,
NPCs.CHICKEN_1402,
NPCs.CHICKEN_2313,
NPCs.CHICKEN_2314,
NPCs.CHICKEN_2315,
NPCs.CHICKEN_288,
NPCs.CHICKEN_41,
NPCs.CHICKEN_951,
)
}
}
@@ -0,0 +1,24 @@
package content.global.activity.jobs.impl
import org.rs09.consts.NPCs
/**
* An enum of NPCs that can assign the player a job.
*
* TODO: Add the rest of the era-correct employers
*/
enum class Employers(val npcId: Int) {
WOODCUTTING_TUTOR(NPCs.WOODCUTTING_TUTOR_4906),
MAGIC_TUTOR(NPCs.MAGIC_TUTOR_4707),
MELEE_TUTOR(NPCs.MELEE_TUTOR_705),
RANGED_TUTOR(NPCs.RANGED_TUTOR_1861),
COOKING_TUTOR(NPCs.COOKING_TUTOR_4899),
CRAFTING_TUTOR(NPCs.CRAFTING_TUTOR_4900),
FISHING_TUTOR(NPCs.FISHING_TUTOR_4901),
MINING_TUTOR(NPCs.MINING_TUTOR_4902),
SMELTING_TUTOR(NPCs.SMELTING_TUTOR_4904),
PRAYER_TUTOR(NPCs.PRAYER_TUTOR_4903),
HANS(NPCs.HANS_0),
GILLIE_GROATS(NPCs.GILLIE_GROATS_3807),
AGGIE(NPCs.AGGIE_922);
}
@@ -1,68 +0,0 @@
package content.global.activity.jobs.impl
import core.game.node.entity.skill.Skills
import core.tools.RandomFunction
enum class GatheringJobs(val lower: Int, val upper: Int, val lvlReq: Int, val itemId: Int,val skill: Int) {
LOG(20, 28, 1, 1511, Skills.WOODCUTTING),
COWHIDES(20, 28, 1, 1739, 0),
OAK(22, 28, 15, 1521, Skills.WOODCUTTING),
WATER_RUNE(20, 28, 5, 555, Skills.RUNECRAFTING),
EARTH_RUNE(20, 28, 9, 557, Skills.RUNECRAFTING),
FIRE_RUNE(20, 28, 14, 554, Skills.RUNECRAFTING),
AIR_RUNE(20, 28, 1, 556, Skills.RUNECRAFTING),
RUNE_ESS(20, 28, 1, 1436, Skills.MINING),
BALL_OF_WOOL(20, 28, 1, 1759, Skills.CRAFTING),
LEATHER_GLOVE(20, 28, 1, 1059, Skills.CRAFTING),
WILLOW(22, 28, 30, 1519, Skills.WOODCUTTING),
LEATHER_BOOT(24, 24, 1, 1061, Skills.CRAFTING),
BRONZE_DAGGER(24, 24, 1, 1205, Skills.SMITHING),
BRONZE_HELMS(22, 22, 1, 1139, Skills.SMITHING),
BRONZE_FULL_HELM(10, 10, 7, 1155, Skills.SMITHING),
BRONZE_CHAINBODY(10, 10, 11, 1103, Skills.SMITHING),
BRONZE_2H(10, 10, 14, 1307, Skills.SMITHING),
BRONZE_SCIM(10, 10, 5, 1321, Skills.SMITHING),
BRONZE_PLATEBODY(9, 10, 18, 1117, Skills.SMITHING),
BRONZE_PLATELEGS(9, 10, 16, 1075, Skills.SMITHING),
BRONZE_PLATESKIRTS(9, 10, 16, 1087, Skills.SMITHING),
BRONZE_WARHAMMER(9, 9, 9, 1337, Skills.SMITHING),
IRON_DAGGER(13, 13, 15, 1203, Skills.SMITHING),
IRON_CHAINBODY(10, 10, 26, 1101, Skills.SMITHING),
IRON_2H(9, 9, 29, 1309, Skills.SMITHING),
STEEL_BATTLEAXE(9, 9, 40, 1365, Skills.SMITHING),
STEEL_SCIMITAR(9, 9, 35, 1325, Skills.SMITHING),
STEEL_PLATEBODY(9, 10, 48, 1119, Skills.SMITHING),
STEEL_WARHAMMER(9, 10, 39, 1339, Skills.SMITHING),
LEATHER_CHAPS(22, 28, 18, 1095, Skills.CRAFTING),
LEATHER_BODY(22, 28, 14, 1129, Skills.CRAFTING),
LEATHER_COWL(22, 28, 9, 1167, Skills.CRAFTING),
LEATHER_COIF(22, 28, 38, 1169, Skills.CRAFTING),
RAW_SHRIMP(22, 28, 1, 317, Skills.FISHING),
COOKED_SHRIMP(22, 28, 1, 315, Skills.COOKING),
RAW_CRAYFISH(22, 28, 1, 13435, Skills.FISHING),
COOKED_CRAYFISH(22, 28, 1, 13433, Skills.COOKING),
RAW_TROUT(22, 28, 20, 335, Skills.FISHING),
COOKED_TROUT(22, 28, 15, 333, Skills.COOKING),
RAW_SALMON(22, 28, 30, 331, Skills.FISHING),
COOKED_SALMON(22, 28, 25, 329, Skills.COOKING),
CAKE(12, 16, 40, 1891, Skills.COOKING),
MEAT_PIE(12, 16, 20, 2327, Skills.COOKING),
PLAIN_PIZZA(12, 16, 35, 2289, Skills.COOKING),
MEAT_PIZZA(12, 16, 45, 2293, Skills.COOKING),
ANCHOVY_PIZZA(12, 16, 55, 2297, Skills.COOKING),
REDBERRY_PIE(12, 16, 10, 2325, Skills.COOKING),
COPPER_ORE(22, 28, 1, 436, Skills.MINING),
TIN_ORE(23, 26, 1, 438, Skills.MINING),
IRON_ORE(24, 24, 15, 440, Skills.MINING),
SILVER_ORE(22, 28, 20, 442, Skills.MINING),
COAL(22, 26, 30, 453, Skills.MINING),
GOLD_ORE(22, 24, 40, 444, Skills.MINING),
BRONZE_BAR(10, 12, 1, 2349, Skills.SMITHING),
IRON_BAR(22, 28, 15, 2351, Skills.SMITHING),
STEEL_BAR(22, 28, 30, 2353, Skills.SMITHING),
ASHES(26, 26, 1, 592, 0);
fun getAmount(): Int {
return RandomFunction.random(lower,upper + 1)
}
}
@@ -0,0 +1,80 @@
package content.global.activity.jobs.impl
import content.global.activity.jobs.Job
import content.global.activity.jobs.JobType
import core.game.node.entity.skill.Skills
/**
* An enum of the possible production jobs that can be assigned to a player.
*
* Note: Due to how player save files keep track of the player's current job, it is essential that
* new entries are only appended to the end of this enum, and the ordering of existing entries is not changed.
*/
enum class ProductionJobs(
override val lower: Int,
override val upper: Int,
val lvlReq: Int,
val itemId: Int,
val skill: Int,
override val employer: Employers
) : Job {
LOG(20, 28, 1, 1511, Skills.WOODCUTTING, Employers.WOODCUTTING_TUTOR),
COWHIDES(20, 28, 1, 1739, 0, Employers.HANS),
OAK(22, 28, 15, 1521, Skills.WOODCUTTING, Employers.WOODCUTTING_TUTOR),
WATER_RUNE(20, 28, 5, 555, Skills.RUNECRAFTING, Employers.HANS),
EARTH_RUNE(20, 28, 9, 557, Skills.RUNECRAFTING, Employers.AGGIE),
FIRE_RUNE(20, 28, 14, 554, Skills.RUNECRAFTING, Employers.AGGIE),
AIR_RUNE(20, 28, 1, 556, Skills.RUNECRAFTING, Employers.HANS),
RUNE_ESS(20, 28, 1, 1436, Skills.MINING, Employers.MINING_TUTOR),
BALL_OF_WOOL(20, 28, 1, 1759, Skills.CRAFTING, Employers.GILLIE_GROATS),
LEATHER_GLOVE(20, 28, 1, 1059, Skills.CRAFTING, Employers.GILLIE_GROATS),
WILLOW(22, 28, 30, 1519, Skills.WOODCUTTING, Employers.WOODCUTTING_TUTOR),
LEATHER_BOOT(24, 24, 1, 1061, Skills.CRAFTING, Employers.HANS),
BRONZE_DAGGER(24, 24, 1, 1205, Skills.SMITHING, Employers.SMELTING_TUTOR),
BRONZE_HELMS(22, 22, 1, 1139, Skills.SMITHING, Employers.SMELTING_TUTOR),
BRONZE_FULL_HELM(10, 10, 7, 1155, Skills.SMITHING, Employers.SMELTING_TUTOR),
BRONZE_CHAINBODY(10, 10, 11, 1103, Skills.SMITHING, Employers.SMELTING_TUTOR),
BRONZE_2H(10, 10, 14, 1307, Skills.SMITHING, Employers.SMELTING_TUTOR),
BRONZE_SCIM(10, 10, 5, 1321, Skills.SMITHING, Employers.SMELTING_TUTOR),
BRONZE_PLATEBODY(9, 10, 18, 1117, Skills.SMITHING, Employers.SMELTING_TUTOR),
BRONZE_PLATELEGS(9, 10, 16, 1075, Skills.SMITHING, Employers.SMELTING_TUTOR),
BRONZE_PLATESKIRTS(9, 10, 16, 1087, Skills.SMITHING, Employers.SMELTING_TUTOR),
BRONZE_WARHAMMER(9, 9, 9, 1337, Skills.SMITHING, Employers.SMELTING_TUTOR),
IRON_DAGGER(13, 13, 15, 1203, Skills.SMITHING, Employers.SMELTING_TUTOR),
IRON_CHAINBODY(10, 10, 26, 1101, Skills.SMITHING, Employers.SMELTING_TUTOR),
IRON_2H(9, 9, 29, 1309, Skills.SMITHING, Employers.SMELTING_TUTOR),
STEEL_BATTLEAXE(9, 9, 40, 1365, Skills.SMITHING, Employers.SMELTING_TUTOR),
STEEL_SCIMITAR(9, 9, 35, 1325, Skills.SMITHING, Employers.SMELTING_TUTOR),
STEEL_PLATEBODY(9, 10, 48, 1119, Skills.SMITHING, Employers.SMELTING_TUTOR),
STEEL_WARHAMMER(9, 10, 39, 1339, Skills.SMITHING, Employers.SMELTING_TUTOR),
LEATHER_CHAPS(22, 28, 18, 1095, Skills.CRAFTING, Employers.CRAFTING_TUTOR),
LEATHER_BODY(22, 28, 14, 1129, Skills.CRAFTING, Employers.CRAFTING_TUTOR),
LEATHER_COWL(22, 28, 9, 1167, Skills.CRAFTING, Employers.CRAFTING_TUTOR),
LEATHER_COIF(22, 28, 38, 1169, Skills.CRAFTING, Employers.CRAFTING_TUTOR),
RAW_SHRIMP(22, 28, 1, 317, Skills.FISHING, Employers.FISHING_TUTOR),
COOKED_SHRIMP(22, 28, 1, 315, Skills.COOKING, Employers.COOKING_TUTOR),
RAW_CRAYFISH(22, 28, 1, 13435, Skills.FISHING, Employers.FISHING_TUTOR),
COOKED_CRAYFISH(22, 28, 1, 13433, Skills.COOKING, Employers.COOKING_TUTOR),
RAW_TROUT(22, 28, 20, 335, Skills.FISHING, Employers.FISHING_TUTOR),
COOKED_TROUT(22, 28, 15, 333, Skills.COOKING, Employers.COOKING_TUTOR),
RAW_SALMON(22, 28, 30, 331, Skills.FISHING, Employers.FISHING_TUTOR),
COOKED_SALMON(22, 28, 25, 329, Skills.COOKING, Employers.COOKING_TUTOR),
CAKE(12, 16, 40, 1891, Skills.COOKING, Employers.GILLIE_GROATS),
MEAT_PIE(12, 16, 20, 2327, Skills.COOKING, Employers.COOKING_TUTOR),
PLAIN_PIZZA(12, 16, 35, 2289, Skills.COOKING, Employers.COOKING_TUTOR),
MEAT_PIZZA(12, 16, 45, 2293, Skills.COOKING, Employers.COOKING_TUTOR),
ANCHOVY_PIZZA(12, 16, 55, 2297, Skills.COOKING, Employers.COOKING_TUTOR),
REDBERRY_PIE(12, 16, 10, 2325, Skills.COOKING, Employers.GILLIE_GROATS),
COPPER_ORE(22, 28, 1, 436, Skills.MINING, Employers.MINING_TUTOR),
TIN_ORE(23, 26, 1, 438, Skills.MINING, Employers.MINING_TUTOR),
IRON_ORE(24, 24, 15, 440, Skills.MINING, Employers.MINING_TUTOR),
SILVER_ORE(22, 28, 20, 442, Skills.MINING, Employers.PRAYER_TUTOR),
COAL(22, 26, 30, 453, Skills.MINING, Employers.MINING_TUTOR),
GOLD_ORE(22, 24, 40, 444, Skills.MINING, Employers.MINING_TUTOR),
BRONZE_BAR(10, 12, 1, 2349, Skills.SMITHING, Employers.SMELTING_TUTOR),
IRON_BAR(22, 28, 15, 2351, Skills.SMITHING, Employers.SMELTING_TUTOR),
STEEL_BAR(22, 28, 30, 2353, Skills.SMITHING, Employers.SMELTING_TUTOR),
ASHES(26, 26, 1, 592, 0, Employers.AGGIE);
override val type = JobType.PRODUCTION
}
@@ -1,15 +0,0 @@
package content.global.activity.jobs.impl
import core.tools.RandomFunction
enum class SlayingJobs(val lower: Int, val upper: Int, vararg val ids: Int) {
CHICKEN(25,26,41,1017),
COW(25,25,397,1766,81,1767),
GIANT_SPIDER(22,25,59,60),
SCORPION(25,25,107,108,109,144,4402,4403),
GOBLIN(25,25,100,101,102,298,299,444,445,489,745,1769,1770,1771,1772,1773,1774,1775,1776,2274,2275,2276,2277,2278,2279,2280,2281,2678,2679,2680,2681,3060,3264,3265,3266,3267);
fun getAmount(): Int {
return RandomFunction.random(lower,upper + 1)
}
}
@@ -1,6 +1,7 @@
package content.global.skill.prayer;
import core.cache.def.impl.ItemDefinition;
import core.game.event.BoneBuryEvent;
import core.plugin.Initializable;
import core.game.node.entity.skill.Skills;
import core.game.interaction.OptionHandler;
@@ -62,7 +63,8 @@ public final class BoneBuryingOptionPlugin extends OptionHandler {
@Override
public boolean pulse() {
player.getPacketDispatch().sendMessage("You bury the bones.");
player.getSkills().addExperience(Skills.PRAYER, bonee.equals(Bones.LAVA_DRAGON_BONES) && player.getLocation().getY() >= 3794 && player.getLocation().getY() <= 3859 ? bonee.getExperience() * 3 : bonee.getExperience(), true);
player.getSkills().addExperience(Skills.PRAYER, bonee.equals(Bones.LAVA_DRAGON_BONES) && player.getLocation().getY() >= 3794 && player.getLocation().getY() <= 3859 ? bonee.getExperience() * 3 : bonee.getExperience(), true);
player.dispatch(new BoneBuryEvent(bonee.getItemId()));
return true;
}
});
+1
View File
@@ -5,6 +5,7 @@ import core.game.event.*
object Event {
@JvmStatic val ResourceProduced = ResourceProducedEvent::class.java
@JvmStatic val NPCKilled = NPCKillEvent::class.java
@JvmStatic val BoneBuried = BoneBuryEvent::class.java
@JvmStatic val Teleported = TeleportEvent::class.java
@JvmStatic val FireLit = LitFireEvent::class.java
@JvmStatic val LightSourceLit = LitLightSourceEvent::class.java
@@ -15,6 +15,7 @@ import content.global.skill.magic.TeleportMethod
data class ResourceProducedEvent(val itemId: Int, val amount: Int, val source: Node, val original: Int = -1) : Event
data class NPCKillEvent(val npc: NPC) : Event
data class BoneBuryEvent(val boneId: Int) : Event
data class TeleportEvent(val type: TeleportType, val method: TeleportMethod, val source: Any, val location: Location) : Event
data class LitFireEvent(val logId: Int) : Event
data class LitLightSourceEvent(val litLightSourceId: Int) : Event
@@ -1,5 +1,7 @@
package core.game.system.command.sets
import content.global.activity.jobs.JobManager
import core.api.removeAttribute
import core.api.sendMessage
import core.cache.Cache
import core.cache.def.impl.DataMap
@@ -51,6 +53,15 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) {
sendMessage(player, "All achievement diaries cleared successfully.")
}
define("clearjob", Privilege.ADMIN) { player, _ ->
val playerJobManager = JobManager.getInstance(player)
playerJobManager.job = null
playerJobManager.jobAmount = -1
playerJobManager.jobOriginalAmount = -1
sendMessage(player, "Job cleared successfully.")
}
define("region", Privilege.STANDARD, "", "Prints your current Region ID.") {player, args ->
sendMessage(player, "Region ID: ${player.viewport.region.regionId}")
}