Implemented auto splitting of excessively long dialogue lines

Many new farming-related player messages added, and some existing messages updated
Raking animation updated
Herb picking animation updated
Digging up farming patch animation updated
Plant cure animation updated
A scarecrow can be retrieved from a flower patch by digging it up with a spade
Picking fruit/berries stops when running out of inventory space
The player can no longer dig up a tree they have planted before chopping it down
This commit is contained in:
zsrv 2024-02-14 11:33:13 +00:00 committed by Ryan
parent 38c50c465d
commit 1d12dd741f
23 changed files with 728 additions and 520 deletions

View file

@ -2,7 +2,6 @@ package content.global.skill.farming
import core.api.* import core.api.*
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.player.link.audio.Audio
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import core.game.node.item.Item import core.game.node.item.Item
import core.tools.RandomFunction import core.tools.RandomFunction
@ -26,14 +25,19 @@ class CompostBin(val player: Player, val bin: CompostBins) {
fun close() { fun close() {
isClosed = true isClosed = true
sendMessage(player, "You close the compost bin.")
// TODO: Add animation - https://youtu.be/B50dwm8fdcQ?t=225 https://youtu.be/BHYgNDLx0s4?t=488
playAudio(player, Sounds.COMPOST_CLOSE_2428) playAudio(player, Sounds.COMPOST_CLOSE_2428)
sendMessage(player, "The contents have begun to rot.")
finishedTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(RandomFunction.random(35,50).toLong()) finishedTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(RandomFunction.random(35,50).toLong())
updateBit() updateBit()
} }
fun open(){ fun open(){
isClosed = false isClosed = false
// TODO: Add animation
playAudio(player, Sounds.COMPOST_OPEN_2429) playAudio(player, Sounds.COMPOST_OPEN_2429)
sendMessage(player, "You open the compost bin.")
updateBit() updateBit()
} }

View file

@ -1,5 +1,6 @@
package content.global.skill.farming package content.global.skill.farming
import core.api.*
import core.cache.def.impl.SceneryDefinition import core.cache.def.impl.SceneryDefinition
import core.game.interaction.OptionHandler import core.game.interaction.OptionHandler
import core.game.node.Node import core.game.node.Node
@ -27,14 +28,14 @@ class CompostBinOptionHandler : OptionHandler() {
val bin = cBin.getBinForPlayer(player) val bin = cBin.getBinForPlayer(player)
when (option) { when (option) {
"close" -> if(!bin.isFull()) player.sendMessage("This shouldn't be happening. Report this.") else bin.close() "close" -> if (!bin.isFull()) sendMessage(player, "This shouldn't be happening. Report this.") else bin.close()
"open" -> if(!bin.isFinished) player.sendMessage("I should probably wait until it is done to open it.") else bin.open() "open" -> if (!bin.isFinished) sendMessage(player, "I should probably wait until it is done to open it.") else bin.open()
"take-tomato" -> { "take-tomato" -> {
if (!bin.isTomatoes || !bin.isFinished) { if (!bin.isTomatoes || !bin.isFinished) {
player.sendMessage("This shouldn't be happening. Report this.") sendMessage(player, "This shouldn't be happening. Report this.")
} else { } else {
if (player.inventory.isFull) { if (player.inventory.isFull) {
player.sendMessage("You don't have enough inventory space to do this.") sendMessage(player, "You don't have enough inventory space to do this.")
} else { } else {
val reward = bin.takeItem() val reward = bin.takeItem()
if (reward != null) if (reward != null)

View file

@ -2,6 +2,6 @@ package content.global.skill.farming
enum class CompostType { enum class CompostType {
NONE, NONE,
NORMAL, COMPOST,
SUPER SUPERCOMPOST
} }

View file

@ -15,7 +15,7 @@ import core.plugin.Plugin
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.Sounds import org.rs09.consts.Sounds
val livesBased = arrayOf(PatchType.HERB, PatchType.CACTUS, PatchType.BELLADONNA, PatchType.HOPS, PatchType.ALLOTMENT,PatchType.EVIL_TURNIP) val livesBased = arrayOf(PatchType.HERB_PATCH, PatchType.CACTUS_PATCH, PatchType.BELLADONNA_PATCH, PatchType.HOPS_PATCH, PatchType.ALLOTMENT, PatchType.EVIL_TURNIP_PATCH)
@Initializable @Initializable
class CropHarvester : OptionHandler() { class CropHarvester : OptionHandler() {
@ -34,8 +34,10 @@ class CropHarvester : OptionHandler() {
val fPatch = FarmingPatch.forObject(node.asScenery()) val fPatch = FarmingPatch.forObject(node.asScenery())
fPatch ?: return null fPatch ?: return null
val patch = fPatch.getPatchFor(player) val patch = fPatch.getPatchFor(player)
val patchName = patch.patch.type.displayName()
val plantable = patch.plantable val plantable = patch.plantable
plantable ?: return null plantable ?: return null
var firstHarvest = true
return object : Pulse(0) { return object : Pulse(0) {
override fun pulse(): Boolean { override fun pulse(): Boolean {
@ -45,23 +47,23 @@ class CropHarvester : OptionHandler() {
if (familiar != null && familiar is GiantEntNPC) { if (familiar != null && familiar is GiantEntNPC) {
familiar.modifyFarmingReward(fPatch, reward) familiar.modifyFarmingReward(fPatch, reward)
} }
if(!player.inventory.hasSpaceFor(reward)){ if (!hasSpaceFor(player, reward)) {
player.sendMessage("You don't have enough inventory space for that.") sendMessage(player, "You have run out of inventory space.")
return true return true
} }
var requiredItem = when (fPatch.type) { var requiredItem = when (fPatch.type) {
PatchType.HERB, PatchType.TREE -> Items.SECATEURS_5329 PatchType.TREE_PATCH -> Items.SECATEURS_5329
else -> Items.SPADE_952 else -> Items.SPADE_952
} }
if (requiredItem == Items.SECATEURS_5329) { if (requiredItem == Items.SECATEURS_5329) {
if(player.inventory.containsAtLeastOneItem(Items.MAGIC_SECATEURS_7409)){ if (inInventory(player, Items.MAGIC_SECATEURS_7409)) {
requiredItem = Items.MAGIC_SECATEURS_7409 requiredItem = Items.MAGIC_SECATEURS_7409
} }
} }
val anim = when (requiredItem) { val anim = when (requiredItem) {
Items.SPADE_952 -> Animation(830) Items.SPADE_952 -> if (fPatch.type == PatchType.HERB_PATCH) Animation(2282) else Animation(830)
Items.SECATEURS_5329 -> if (fPatch.type == PatchType.TREE) Animation(2277) else Animation(7227) Items.SECATEURS_5329 -> if (fPatch.type == PatchType.TREE_PATCH) Animation(2277) else Animation(7227)
Items.MAGIC_SECATEURS_7409 -> if (fPatch.type == PatchType.TREE) Animation(3340) else Animation(7228) Items.MAGIC_SECATEURS_7409 -> if (fPatch.type == PatchType.TREE_PATCH) Animation(3340) else Animation(7228)
else -> Animation(0) else -> Animation(0)
} }
val sound = when (requiredItem) { val sound = when (requiredItem) {
@ -70,15 +72,19 @@ class CropHarvester : OptionHandler() {
Items.MAGIC_SECATEURS_7409 -> Sounds.FARMING_PICK_2437 Items.MAGIC_SECATEURS_7409 -> Sounds.FARMING_PICK_2437
else -> 0 else -> 0
} }
if(!player.inventory.containsItem(Item(requiredItem))){ if (!inInventory(player, requiredItem)) {
player.sendMessage("You lack the needed tool to harvest these crops.") sendMessage(player, "You lack the needed tool to harvest these crops.")
return true return true
} }
player.animator.animate(anim) if (firstHarvest) {
sendMessage(player, "You begin to harvest the $patchName.")
firstHarvest = false
}
animate(player, anim)
playAudio(player, sound) playAudio(player, sound)
delay = 2 delay = 2
player.inventory.add(reward) addItem(player, reward.id)
player.skills.addExperience(Skills.FARMING,plantable.harvestXP) rewardXP(player, Skills.FARMING, plantable.harvestXP)
if (patch.patch.type in livesBased) { if (patch.patch.type in livesBased) {
patch.rollLivesDecrement( patch.rollLivesDecrement(
getDynLevel(player, Skills.FARMING), getDynLevel(player, Skills.FARMING),
@ -90,6 +96,9 @@ class CropHarvester : OptionHandler() {
patch.clear() patch.clear()
} }
} }
if (patch.cropLives <= 0 || patch.harvestAmt <= 0) {
sendMessage(player, "The $patchName is now empty.")
}
return patch.cropLives <= 0 || patch.harvestAmt <= 0 return patch.cropLives <= 0 || patch.harvestAmt <= 0
} }
} }
@ -105,13 +114,18 @@ class CropHarvester : OptionHandler() {
val plantable = patch.plantable val plantable = patch.plantable
plantable ?: return false plantable ?: return false
if(patch.isWeedy()){ if (patch.isWeedy() || patch.isEmptyAndWeeded()) {
player.sendMessage("Something seems to have gone wrong here. Report this.") sendMessage(player, "Something seems to have gone wrong here. Report this.")
return true
}
if (!hasSpaceFor(player, Item(plantable.harvestItem))) {
sendMessage(player, "You don't have enough inventory space to do that.")
return true return true
} }
val pulse = harvestPulse(player, node, plantable.harvestItem) ?: return false val pulse = harvestPulse(player, node, plantable.harvestItem) ?: return false
player.pulseManager.run(pulse) submitIndividualPulse(player, pulse)
return true return true
} }

View file

@ -1,13 +1,9 @@
package content.global.skill.farming package content.global.skill.farming
import core.api.playAudio import core.api.*
import core.game.dialogue.DialoguePlugin import core.game.dialogue.DialoguePlugin
import core.game.interaction.QueueStrength
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.player.link.audio.Audio
import core.game.node.item.GroundItemManager
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.update.flag.context.Animation
import core.plugin.Initializable import core.plugin.Initializable
import org.rs09.consts.Sounds import org.rs09.consts.Sounds
@ -21,12 +17,20 @@ class DigUpPatchDialogue(player: Player? = null) : DialoguePlugin(player) {
override fun open(vararg args: Any?): Boolean { override fun open(vararg args: Any?): Boolean {
patch = args[0] as Patch patch = args[0] as Patch
if(patch?.isWeedy() == true){ if (patch?.isWeedy() == true || patch?.isEmptyAndWeeded() == true) {
player.dialogueInterpreter.sendDialogue("Use a rake to get rid of weeds.") sendDialogue(player, "There aren't any crops in this patch to dig up.")
stage = 1000 stage = 1000
return true return true
} }
player.dialogueInterpreter.sendOptions("Dig up this patch?","Yes","No") if (patch?.patch?.type == PatchType.TREE_PATCH) {
val isTreeStump = patch?.getCurrentState() == patch?.plantable!!.value + patch?.plantable!!.stages + 2
if (!isTreeStump) {
sendMessage(player, "You need to chop this tree down first.") // this message is not authentic
stage = 1000
return true
}
}
sendDialogueOptions(player, "Are you sure you want to dig up this patch?", "Yes, I want to clear it for new crops.", "No, I want to leave it as it is.")
stage = 0 stage = 0
return true return true
} }
@ -36,21 +40,36 @@ class DigUpPatchDialogue(player: Player? = null) : DialoguePlugin(player) {
0 -> when (buttonId) { 0 -> when (buttonId) {
1 -> { 1 -> {
end() end()
player.animator.animate(Animation(830)) val anim = getAnimation(830)
sendMessage(player, "You start digging the farming patch...")
queueScript(player, 0, QueueStrength.WEAK) { stage: Int ->
when (stage) {
0 -> {
animate(player, anim)
playAudio(player, Sounds.DIGSPADE_1470) playAudio(player, Sounds.DIGSPADE_1470)
player.pulseManager.run(object : Pulse(){ return@queueScript delayScript(player,anim.duration + 2)
override fun pulse(): Boolean { }
if(patch?.patch?.type == PatchType.TREE){ 1 -> {
if(patch?.getCurrentState() == (patch?.plantable?.value ?: 0) + (patch?.plantable?.stages ?: 0) + 2 && patch?.isWeedy() != true){ animate(player, anim)
if(!player.inventory.add(Item(patch?.plantable?.harvestItem ?: 0))){ playAudio(player, Sounds.DIGSPADE_1470)
GroundItemManager.create(Item(patch?.plantable?.harvestItem ?: 0),player) return@queueScript delayScript(player, anim.duration + 1)
}
2 -> {
if (patch?.patch?.type == PatchType.TREE_PATCH) {
if (patch?.getCurrentState() == (patch?.plantable?.value ?: 0) + (patch?.plantable?.stages ?: 0) + 2 && patch?.isWeedy() != true && patch?.isEmptyAndWeeded() != true) {
addItemOrDrop(player, patch?.plantable?.harvestItem ?: 0)
} }
} }
if (patch?.plantable == Plantable.SCARECROW) {
addItemOrDrop(player, patch?.plantable?.harvestItem ?: 0)
} }
patch?.clear() patch?.clear()
return true sendMessage(player, "You have successfully cleared this patch for new crops.")
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
} }
})
} }
else -> end() else -> end()
} }

View file

@ -2,7 +2,6 @@ package content.global.skill.farming
import core.api.* import core.api.*
import core.cache.def.impl.SceneryDefinition import core.cache.def.impl.SceneryDefinition
import core.cache.def.impl.VarbitDefinition
import core.game.node.scenery.Scenery import core.game.node.scenery.Scenery
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import content.global.skill.farming.timers.CropGrowth import content.global.skill.farming.timers.CropGrowth
@ -20,55 +19,55 @@ enum class FarmingPatch(val varbit: Int, val type: PatchType) {
HARMONY_ISLAND_ALLOTMENT(3402,PatchType.ALLOTMENT), HARMONY_ISLAND_ALLOTMENT(3402,PatchType.ALLOTMENT),
//Herb //Herb
CATHERBY_HERB_CE(781,PatchType.HERB), CATHERBY_HERB_CE(781,PatchType.HERB_PATCH),
S_FALADOR_HERB_NE(780,PatchType.HERB), S_FALADOR_HERB_NE(780,PatchType.HERB_PATCH),
ARDOUGNE_HERB_CE(782,PatchType.HERB), ARDOUGNE_HERB_CE(782,PatchType.HERB_PATCH),
PORT_PHAS_HERB_NE(783,PatchType.HERB), PORT_PHAS_HERB_NE(783,PatchType.HERB_PATCH),
TROLL_STRONGHOLD_HERB(2788,PatchType.HERB), TROLL_STRONGHOLD_HERB(2788,PatchType.HERB_PATCH),
//Flower //Flower
S_FALADOR_FLOWER_C(728,PatchType.FLOWER), S_FALADOR_FLOWER_C(728,PatchType.FLOWER_PATCH),
CATHERBY_FLOWER_C(729,PatchType.FLOWER), CATHERBY_FLOWER_C(729,PatchType.FLOWER_PATCH),
ARDOUGNE_FLOWER_C(730,PatchType.FLOWER), ARDOUGNE_FLOWER_C(730,PatchType.FLOWER_PATCH),
PORT_PHAS_FLOWER_C(731,PatchType.FLOWER), PORT_PHAS_FLOWER_C(731,PatchType.FLOWER_PATCH),
WILDERNESS_FLOWER(5067,PatchType.FLOWER), WILDERNESS_FLOWER(5067,PatchType.FLOWER_PATCH),
//Tree //Tree
N_FALADOR_TREE(701,PatchType.TREE), N_FALADOR_TREE(701,PatchType.TREE_PATCH),
TAVERLY_TREE(700,PatchType.TREE), TAVERLY_TREE(700,PatchType.TREE_PATCH),
GNOME_STRONGHOLD_TREE(2953,PatchType.TREE), GNOME_STRONGHOLD_TREE(2953,PatchType.TREE_PATCH),
LUMBRIDGE_TREE(703,PatchType.TREE), LUMBRIDGE_TREE(703,PatchType.TREE_PATCH),
VARROCK_TREE(702,PatchType.TREE), VARROCK_TREE(702,PatchType.TREE_PATCH),
//Fruit Tree //Fruit Tree
GNOME_STRONGHOLD_FRUIT_TREE(704,PatchType.FRUIT_TREE), GNOME_STRONGHOLD_FRUIT_TREE(704,PatchType.FRUIT_TREE_PATCH),
CATHERBY_FRUIT_TREE(707,PatchType.FRUIT_TREE), CATHERBY_FRUIT_TREE(707,PatchType.FRUIT_TREE_PATCH),
TREE_GNOME_VILLAGE_FRUIT_TREE(705,PatchType.FRUIT_TREE), TREE_GNOME_VILLAGE_FRUIT_TREE(705,PatchType.FRUIT_TREE_PATCH),
BRIMHAVEN_FRUIT_TREE(706,PatchType.FRUIT_TREE), BRIMHAVEN_FRUIT_TREE(706,PatchType.FRUIT_TREE_PATCH),
LLETYA_FRUIT_TREE(4317,PatchType.FRUIT_TREE), LLETYA_FRUIT_TREE(4317,PatchType.FRUIT_TREE_PATCH),
//Hops //Hops
ENTRANA_HOPS(717,PatchType.HOPS), ENTRANA_HOPS(717,PatchType.HOPS_PATCH),
LUMBRIDGE_HOPS(718,PatchType.HOPS), LUMBRIDGE_HOPS(718,PatchType.HOPS_PATCH),
MCGRUBOR_HOPS(719,PatchType.HOPS), MCGRUBOR_HOPS(719,PatchType.HOPS_PATCH),
YANILLE_HOPS(716,PatchType.HOPS), YANILLE_HOPS(716,PatchType.HOPS_PATCH),
//Bushes //Bushes
CHAMPIONS_GUILD_BUSH(732,PatchType.BUSH), CHAMPIONS_GUILD_BUSH(732,PatchType.BUSH_PATCH),
RIMMINGTON_BUSH(733,PatchType.BUSH), RIMMINGTON_BUSH(733,PatchType.BUSH_PATCH),
ARDOUGNE_BUSH(735,PatchType.BUSH), ARDOUGNE_BUSH(735,PatchType.BUSH_PATCH),
ETCETERIA_BUSH(734,PatchType.BUSH), ETCETERIA_BUSH(734,PatchType.BUSH_PATCH),
//Spirit Tree //Spirit Tree
ETCETERIA_SPIRIT_TREE(722,PatchType.SPIRIT_TREE), ETCETERIA_SPIRIT_TREE(722,PatchType.SPIRIT_TREE_PATCH),
PORT_SARIM_SPIRIT_TREE(720,PatchType.SPIRIT_TREE), PORT_SARIM_SPIRIT_TREE(720,PatchType.SPIRIT_TREE_PATCH),
KARAMJA_SPIRIT_TREE(724,PatchType.SPIRIT_TREE), KARAMJA_SPIRIT_TREE(724,PatchType.SPIRIT_TREE_PATCH),
//Other //Other
DRAYNOR_BELLADONNA(748, PatchType.BELLADONNA), DRAYNOR_BELLADONNA(748, PatchType.BELLADONNA_PATCH),
CANIFIS_MUSHROOM(746, PatchType.MUSHROOM), CANIFIS_MUSHROOM(746, PatchType.MUSHROOM_PATCH),
ALKHARID_CACTUS(744, PatchType.CACTUS), ALKHARID_CACTUS(744, PatchType.CACTUS_PATCH),
EVIL_TURNIP(4291, PatchType.EVIL_TURNIP); EVIL_TURNIP(4291, PatchType.EVIL_TURNIP_PATCH);
companion object { companion object {

View file

@ -46,12 +46,12 @@ class FruitAndBerryPicker : OptionHandler() {
val animation = Animation(2281) val animation = Animation(2281)
if (patch.getFruitOrBerryCount() <= 0) { if (patch.getFruitOrBerryCount() <= 0) {
player.sendMessage("This shouldn't be happening. Please report this.") sendMessage(player, "This shouldn't be happening. Please report this.")
return true return true
} }
if(!player.inventory.hasSpaceFor(Item(plantable.harvestItem))){ if (!hasSpaceFor(player, Item(plantable.harvestItem))) {
player.sendMessage("You do not have enough inventory space for this.") sendMessage(player, "You don't have enough inventory space to do that.")
return true return true
} }
@ -59,21 +59,33 @@ class FruitAndBerryPicker : OptionHandler() {
patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(45) patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(45)
} }
player.pulseManager.run(object : Pulse(animation.duration){ submitIndividualPulse(player, object : Pulse(animation.duration) {
override fun pulse(): Boolean { override fun pulse(): Boolean {
val reward = Item(plantable.harvestItem, 1) val reward = Item(plantable.harvestItem, 1)
if (!hasSpaceFor(player, reward)) {
sendMessage(player, "You have run out of inventory space.")
return true
}
val familiar = player.familiarManager.familiar val familiar = player.familiarManager.familiar
if (familiar != null && familiar is GiantEntNPC) { if (familiar != null && familiar is GiantEntNPC) {
familiar.modifyFarmingReward(fPatch, reward) familiar.modifyFarmingReward(fPatch, reward)
} }
player.animator.animate(animation) animate(player, animation)
playAudio(player, Sounds.FARMING_PICK_2437) playAudio(player, Sounds.FARMING_PICK_2437)
addItemOrDrop(player, reward.id, reward.amount) addItemOrDrop(player, reward.id, reward.amount)
player.skills.addExperience(Skills.FARMING,plantable.harvestXP) rewardXP(player, Skills.FARMING, plantable.harvestXP)
patch.setCurrentState(patch.getCurrentState() - 1) patch.setCurrentState(patch.getCurrentState() - 1)
if (patch.patch.type == PatchType.CACTUS_PATCH) {
sendMessage(player, "You carefully pick a spine from the cactus.")
} else {
val determiner = if (patch.patch.type == PatchType.BUSH_PATCH) "some" else "a"
sendMessage(player, "You pick $determiner ${reward.name.lowercase()}.")
}
return patch.getFruitOrBerryCount() == 0 return patch.getFruitOrBerryCount() == 0
} }
}) })

View file

@ -1,12 +1,11 @@
package content.global.skill.farming package content.global.skill.farming
import core.api.log import core.api.*
import core.cache.def.impl.SceneryDefinition import core.cache.def.impl.SceneryDefinition
import core.game.interaction.OptionHandler import core.game.interaction.OptionHandler
import core.game.node.Node import core.game.node.Node
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import core.tools.SystemLogger
import core.plugin.Initializable import core.plugin.Initializable
import core.plugin.Plugin import core.plugin.Plugin
import core.tools.Log import core.tools.Log
@ -27,24 +26,27 @@ class HealthChecker : OptionHandler(){
val patch = fPatch.getPatchFor(player) val patch = fPatch.getPatchFor(player)
val type = patch.patch.type val type = patch.patch.type
if(type != PatchType.BUSH && type != PatchType.FRUIT_TREE && type != PatchType.TREE && type != PatchType.CACTUS){ if (type != PatchType.BUSH_PATCH && type != PatchType.FRUIT_TREE_PATCH && type != PatchType.TREE_PATCH && type != PatchType.CACTUS_PATCH) {
player.sendMessage("This shouldn't be happening. Please report this.") sendMessage(player, "This shouldn't be happening. Please report this.")
return true return true
} }
if (!patch.isCheckHealth) return true if (!patch.isCheckHealth) return true
player.skills.addExperience(Skills.FARMING,patch.plantable?.checkHealthXP ?: 0.0) rewardXP(player, Skills.FARMING, patch.plantable?.checkHealthXP ?: 0.0)
patch.isCheckHealth = false patch.isCheckHealth = false
when (type) { when (type) {
PatchType.TREE -> patch.setCurrentState(patch.getCurrentState() + 1) PatchType.TREE_PATCH -> patch.setCurrentState(patch.getCurrentState() + 1)
PatchType.FRUIT_TREE -> patch.setCurrentState(patch.getCurrentState() - 14) PatchType.FRUIT_TREE_PATCH -> patch.setCurrentState(patch.getCurrentState() - 14)
PatchType.BUSH -> patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 4) PatchType.BUSH_PATCH -> {
PatchType.CACTUS -> patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 3) sendMessage(player, "You examine the bush for signs of disease and find that it's in perfect health.")
else -> log(this::class.java, Log.ERR, "Unreachable patch type from when(type) switch in HealthChecker.kt line 36") patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 4)
}
PatchType.CACTUS_PATCH -> patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 3)
else -> log(this::class.java, Log.ERR, "Unreachable patch type from when(type) switch in HealthChecker.kt")
} }
if(type == PatchType.FRUIT_TREE){ if (type == PatchType.FRUIT_TREE_PATCH) {
patch.nextGrowth = TimeUnit.MINUTES.toMillis(45) patch.nextGrowth = TimeUnit.MINUTES.toMillis(45)
} }

View file

@ -1,11 +1,13 @@
package content.global.skill.farming package content.global.skill.farming
import core.api.*
import core.cache.def.impl.SceneryDefinition import core.cache.def.impl.SceneryDefinition
import core.game.interaction.OptionHandler import core.game.interaction.OptionHandler
import core.game.node.Node import core.game.node.Node
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.plugin.Initializable import core.plugin.Initializable
import core.plugin.Plugin import core.plugin.Plugin
import core.tools.prependArticle
@Initializable @Initializable
class InspectionHandler : OptionHandler() { class InspectionHandler : OptionHandler() {
@ -19,26 +21,34 @@ class InspectionHandler : OptionHandler() {
player ?: return false player ?: return false
val patch = FarmingPatch.forObject(node.asScenery()) val patch = FarmingPatch.forObject(node.asScenery())
if (patch == null) { if (patch == null) {
player.sendMessage("This is an improperly handled inspect option. Report this please.") sendMessage(player, "This is an improperly handled inspect option. Report this please.")
} else { return true
}
val p = patch.getPatchFor(player) val p = patch.getPatchFor(player)
val status1 = if(p.getCurrentState() <= 2) "This patch needs weeding." else if(p.getCurrentState() == 3) "This patch is weed-free." else { val patchName = p.patch.type.displayName()
if(p.isDiseased && !p.isDead) "This patch has become diseased."
else if(p.isDead) "The crops in this patch are dead." val statusPatchType = if (patch == FarmingPatch.TROLL_STRONGHOLD_HERB) "This is a very special herb patch."
else if(p.plantable == Plantable.SCARECROW) "There is a scarecrow in this patch." else "This is ${prependArticle(patchName)}."
else "This patch has something growing in it."
} val statusCompost = if (p.compost == CompostType.NONE) "The soil has not been treated."
val status2 = if(patch.type == PatchType.ALLOTMENT || patch.type == PatchType.FLOWER || patch.type == PatchType.HOPS){ else "The soil has been treated with ${p.compost.name.lowercase()}."
if(p.isWatered) {
"This patch has been watered." val statusStage = if (p.plantable == Plantable.SCARECROW) ""
} else if(p.getCurrentState() > 3 && !p.isGrown() && !p.isDead && !p.isDiseased) { else if (p.isWeedy()) "The patch needs weeding."
"This patch could use some water." else if (p.isEmptyAndWeeded()) "The patch is empty and weeded."
} else "" else if (p.isDiseased && !p.isDead) "The patch is diseased and needs attending to before it dies."
} else "" else if (p.isDead) "The patch has become infected by disease and has died."
val status3 = if(p.compost == CompostType.NONE) "This patch has not been treated." else "This patch has been treated with ${p.compost.name.toLowerCase()} compost." else if (p.isGrown()) "The patch is fully grown."
player.sendMessage("$status1 $status2") else "The patch has something growing in it."
player.sendMessage(status3)
} val statusGardener = if (patch == FarmingPatch.TROLL_STRONGHOLD_HERB) "My Arm will look after this patch for you."
else if (p.protectionPaid) "A nearby gardener is looking after this patch for you."
else ""
sendMessage(player, "$statusPatchType $statusCompost $statusStage".trim())
if (statusGardener != "") sendMessage(player, statusGardener)
return true return true
} }

View file

@ -5,7 +5,6 @@ import core.game.node.entity.player.Player
import core.tools.Log import core.tools.Log
import core.tools.RandomFunction import core.tools.RandomFunction
import org.rs09.consts.Items import org.rs09.consts.Items
import core.tools.SystemLogger
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.min import kotlin.math.min
@ -21,8 +20,8 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
fun setNewHarvestAmount() { fun setNewHarvestAmount() {
val compostMod = when(compost) { val compostMod = when(compost) {
CompostType.NONE -> 0 CompostType.NONE -> 0
CompostType.NORMAL -> 1 CompostType.COMPOST -> 1
CompostType.SUPER -> 2 CompostType.SUPERCOMPOST -> 2
} }
harvestAmt = when (plantable) { harvestAmt = when (plantable) {
Plantable.LIMPWURT_SEED, Plantable.WOAD_SEED -> 3 Plantable.LIMPWURT_SEED, Plantable.WOAD_SEED -> 3
@ -30,14 +29,14 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
Plantable.WILLOW_SAPLING -> 0 Plantable.WILLOW_SAPLING -> 0
else -> 1 else -> 1
} }
if(plantable != null && plantable?.applicablePatch != PatchType.FLOWER) { if(plantable != null && plantable?.applicablePatch != PatchType.FLOWER_PATCH) {
harvestAmt += compostMod harvestAmt += compostMod
} }
cropLives = 3 + compostMod cropLives = 3 + compostMod
} }
fun rollLivesDecrement(farmingLevel: Int, magicSecateurs: Boolean){ fun rollLivesDecrement(farmingLevel: Int, magicSecateurs: Boolean){
if(patch.type == PatchType.HERB){ if(patch.type == PatchType.HERB_PATCH){
//authentic formula thanks to released data. //authentic formula thanks to released data.
var herbSaveLow = when(plantable){ var herbSaveLow = when(plantable){
Plantable.GUAM_SEED -> min(24 + farmingLevel, 80) Plantable.GUAM_SEED -> min(24 + farmingLevel, 80)
@ -70,10 +69,10 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
//inauthentic formulae based on reported averages due to lack of formula //inauthentic formulae based on reported averages due to lack of formula
var chance = when(patch.type){ var chance = when(patch.type){
PatchType.ALLOTMENT -> 8 //average of 8 per life times 3 lives = average 24 PatchType.ALLOTMENT -> 8 //average of 8 per life times 3 lives = average 24
PatchType.HOPS -> 6 //average of 6 per life times 3 lives = 18 PatchType.HOPS_PATCH -> 6 //average of 6 per life times 3 lives = 18
PatchType.BELLADONNA -> 2 //average of 2 per life times 3 lives = 6 PatchType.BELLADONNA_PATCH -> 2 //average of 2 per life times 3 lives = 6
PatchType.EVIL_TURNIP -> 2 //average 2 per, same as BELLADONNA PatchType.EVIL_TURNIP_PATCH -> 2 //average 2 per, same as BELLADONNA
PatchType.CACTUS -> 3 //average of 3 per life times 3 lives = 9 PatchType.CACTUS_PATCH -> 3 //average of 3 per life times 3 lives = 9
else -> 0 // nothing should go here, but if it does, do not give extra crops amd decrement cropLives else -> 0 // nothing should go here, but if it does, do not give extra crops amd decrement cropLives
} }
@ -86,7 +85,11 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
} }
fun isWeedy(): Boolean { fun isWeedy(): Boolean {
return getCurrentState() in 0..3 return getCurrentState() in 0..2
}
fun isEmptyAndWeeded(): Boolean {
return getCurrentState() == 3
} }
fun getCurrentState(): Int{ fun getCurrentState(): Int{
@ -138,14 +141,14 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
fun updateBit(){ fun updateBit(){
if(isCheckHealth){ if(isCheckHealth){
when(patch.type){ when(patch.type){
PatchType.FRUIT_TREE -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages + 20) PatchType.FRUIT_TREE_PATCH -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages + 20)
PatchType.BUSH -> setVarbit(player, patch.varbit, 250 + (plantable!!.ordinal - Plantable.REDBERRY_SEED.ordinal)) PatchType.BUSH_PATCH -> setVarbit(player, patch.varbit, 250 + (plantable!!.ordinal - Plantable.REDBERRY_SEED.ordinal))
PatchType.CACTUS -> setVarbit(player, patch.varbit, 31) PatchType.CACTUS_PATCH -> setVarbit(player, patch.varbit, 31)
else -> log(this::class.java, Log.WARN, "Invalid setting of isCheckHealth for patch type: " + patch.type.name) else -> log(this::class.java, Log.WARN, "Invalid setting of isCheckHealth for patch type: " + patch.type.name)
} }
} else { } else {
when(patch.type){ when(patch.type){
PatchType.ALLOTMENT,PatchType.FLOWER,PatchType.HOPS -> { PatchType.ALLOTMENT,PatchType.FLOWER_PATCH,PatchType.HOPS_PATCH -> {
var state = getUnmodifiedValue() var state = getUnmodifiedValue()
if (isWatered || isDead) state = state or 0x40 if (isWatered || isDead) state = state or 0x40
if (isDiseased) state = state or 0x80 if (isDiseased) state = state or 0x80
@ -153,11 +156,11 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
if (state != getVarbit(player, patch.varbit)) if (state != getVarbit(player, patch.varbit))
setVisualState(state) setVisualState(state)
} }
PatchType.BUSH -> { PatchType.BUSH_PATCH -> {
if(isDead) setVisualState(getBushDeathValue()) if(isDead) setVisualState(getBushDeathValue())
else if(isDiseased && !isDead) setVisualState(getBushDiseaseValue()) else if(isDiseased && !isDead) setVisualState(getBushDiseaseValue())
} }
PatchType.TREE -> { PatchType.TREE_PATCH -> {
var state = getVarbit(player, patch.varbit) var state = getVarbit(player, patch.varbit)
if (isDead) state = state or 0x80 if (isDead) state = state or 0x80
@ -166,20 +169,20 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
if (state != getVarbit(player, patch.varbit)) if (state != getVarbit(player, patch.varbit))
setVisualState(state) setVisualState(state)
} }
PatchType.FRUIT_TREE -> { PatchType.FRUIT_TREE_PATCH -> {
if(isDead) setVisualState(getFruitTreeDeathValue()) if(isDead) setVisualState(getFruitTreeDeathValue())
else if(isDiseased && !isDead) setVisualState(getFruitTreeDiseaseValue()) else if(isDiseased && !isDead) setVisualState(getFruitTreeDiseaseValue())
} }
PatchType.BELLADONNA -> { PatchType.BELLADONNA_PATCH -> {
if(isDead) setVisualState(getBelladonnaDeathValue()) if(isDead) setVisualState(getBelladonnaDeathValue())
else if(isDiseased && !isDead) setVisualState(getBelladonnaDiseaseValue()) else if(isDiseased && !isDead) setVisualState(getBelladonnaDiseaseValue())
else setVisualState((plantable?.value ?: 0) + currentGrowthStage) else setVisualState((plantable?.value ?: 0) + currentGrowthStage)
} }
PatchType.CACTUS -> { PatchType.CACTUS_PATCH -> {
if(isDead) setVisualState(getCactusDeathValue()) if(isDead) setVisualState(getCactusDeathValue())
else if(isDiseased && !isDead) setVisualState(getCactusDiseaseValue()) else if(isDiseased && !isDead) setVisualState(getCactusDiseaseValue())
} }
PatchType.HERB -> { PatchType.HERB_PATCH -> {
if(isDead) setVisualState(getHerbDeathValue()) if(isDead) setVisualState(getHerbDeathValue())
else if(isDiseased && !isDead) setVisualState(getHerbDiseaseValue()) else if(isDiseased && !isDead) setVisualState(getHerbDiseaseValue())
else setVisualState((plantable?.value ?: 0) + currentGrowthStage) else setVisualState((plantable?.value ?: 0) + currentGrowthStage)
@ -261,7 +264,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
} }
private fun grow(){ private fun grow(){
if(isWeedy() && getCurrentState() > 0) { if((isWeedy() || isEmptyAndWeeded()) && getCurrentState() > 0) {
nextGrowth = System.currentTimeMillis() + 60000 nextGrowth = System.currentTimeMillis() + 60000
setCurrentState(getCurrentState() - 1) setCurrentState(getCurrentState() - 1)
currentGrowthStage-- currentGrowthStage--
@ -275,28 +278,28 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
diseaseMod = when(compost){ diseaseMod = when(compost){
CompostType.NONE -> 0 CompostType.NONE -> 0
CompostType.NORMAL -> 8 CompostType.COMPOST -> 8
CompostType.SUPER -> 13 CompostType.SUPERCOMPOST -> 13
} }
if(patch != FarmingPatch.TROLL_STRONGHOLD_HERB && RandomFunction.random(128) <= (17 - diseaseMod) && !isWatered && !isGrown() && !protectionPaid && !isFlowerProtected() && patch.type != PatchType.EVIL_TURNIP ){ if(patch != FarmingPatch.TROLL_STRONGHOLD_HERB && RandomFunction.random(128) <= (17 - diseaseMod) && !isWatered && !isGrown() && !protectionPaid && !isFlowerProtected() && patch.type != PatchType.EVIL_TURNIP_PATCH ){
//bush, tree, fruit tree, herb and cactus can not disease on stage 1(0) of growth. //bush, tree, fruit tree, herb and cactus can not disease on stage 1(0) of growth.
if(!((patch.type == PatchType.BUSH || patch.type == PatchType.TREE || patch.type == PatchType.FRUIT_TREE || patch.type == PatchType.CACTUS || patch.type == PatchType.HERB) && currentGrowthStage == 0)) { if(!((patch.type == PatchType.BUSH_PATCH || patch.type == PatchType.TREE_PATCH || patch.type == PatchType.FRUIT_TREE_PATCH || patch.type == PatchType.CACTUS_PATCH || patch.type == PatchType.HERB_PATCH) && currentGrowthStage == 0)) {
isDiseased = true isDiseased = true
return return
} }
} }
if((patch.type == PatchType.FRUIT_TREE || patch.type == PatchType.TREE || patch.type == PatchType.BUSH || patch.type == PatchType.CACTUS) && plantable != null && plantable?.stages == currentGrowthStage + 1){ if((patch.type == PatchType.FRUIT_TREE_PATCH || patch.type == PatchType.TREE_PATCH || patch.type == PatchType.BUSH_PATCH || patch.type == PatchType.CACTUS_PATCH) && plantable != null && plantable?.stages == currentGrowthStage + 1){
isCheckHealth = true isCheckHealth = true
} }
if((patch.type == PatchType.FRUIT_TREE || patch.type == PatchType.BUSH || patch.type == PatchType.CACTUS) && plantable?.stages == currentGrowthStage){ if((patch.type == PatchType.FRUIT_TREE_PATCH || patch.type == PatchType.BUSH_PATCH || patch.type == PatchType.CACTUS_PATCH) && plantable?.stages == currentGrowthStage){
if((patch.type == PatchType.BUSH && getFruitOrBerryCount() < 4) || (patch.type == PatchType.FRUIT_TREE && getFruitOrBerryCount() < 6) || (patch.type == PatchType.CACTUS && getFruitOrBerryCount() < 3)){ if((patch.type == PatchType.BUSH_PATCH && getFruitOrBerryCount() < 4) || (patch.type == PatchType.FRUIT_TREE_PATCH && getFruitOrBerryCount() < 6) || (patch.type == PatchType.CACTUS_PATCH && getFruitOrBerryCount() < 3)){
setCurrentState(getCurrentState() + 1) setCurrentState(getCurrentState() + 1)
} }
} }
if(patch.type == PatchType.TREE) { if(patch.type == PatchType.TREE_PATCH) {
// Willow branches // Willow branches
if(harvestAmt < 6) { if(harvestAmt < 6) {
harvestAmt++ harvestAmt++
@ -313,7 +316,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
} }
fun regrowIfTreeStump() { fun regrowIfTreeStump() {
if(patch.type == PatchType.TREE && plantable != null) { if(patch.type == PatchType.TREE_PATCH && plantable != null) {
// plantable.value + plantable.stages is the check-health stage, so +1 is the choppable tree, and +2 is the stump // plantable.value + plantable.stages is the check-health stage, so +1 is the choppable tree, and +2 is the stump
if(getCurrentState() == plantable!!.value + plantable!!.stages + 2) { if(getCurrentState() == plantable!!.value + plantable!!.stages + 2) {
setCurrentState(getCurrentState() - 1) setCurrentState(getCurrentState() - 1)
@ -356,7 +359,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
fun getStageGrowthMinutes() : Int { fun getStageGrowthMinutes() : Int {
var minutes = patch.type.stageGrowthTime var minutes = patch.type.stageGrowthTime
if(patch.type == PatchType.FRUIT_TREE && isGrown()) { if(patch.type == PatchType.FRUIT_TREE_PATCH && isGrown()) {
// Fruit trees take 160 minutes per stage to grow, but // Fruit trees take 160 minutes per stage to grow, but
// restocking their fruit should take 40 minutes per fruit // restocking their fruit should take 40 minutes per fruit
minutes = 40 minutes = 40

View file

@ -1,33 +1,45 @@
package content.global.skill.farming package content.global.skill.farming
import core.api.playAudio import core.api.*
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import core.game.system.task.Pulse import core.game.system.task.Pulse
import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.Sounds import org.rs09.consts.Sounds
object PatchRaker { object PatchRaker {
val RAKE_ANIM = Animation(2273) val RAKE_ANIM = getAnimation(2273)
@JvmStatic @JvmStatic
fun rake(player: Player, patch: FarmingPatch) { fun rake(player: Player, patch: FarmingPatch) {
player.pulseManager.run(object : Pulse(){ val p = patch.getPatchFor(player)
val patchName = p.patch.type.displayName()
var firstRake = true
if (p.isEmptyAndWeeded()) {
sendMessage(player, "This $patchName doesn't need weeding right now.")
return
}
submitIndividualPulse(player, object : Pulse() {
override fun pulse(): Boolean { override fun pulse(): Boolean {
var patchStage = patch.getPatchFor(player).getCurrentState() var patchStage = patch.getPatchFor(player).getCurrentState()
if(patchStage <= 2){ if (firstRake || patchStage < 2) {
player.animator.animate(RAKE_ANIM) // don't play the animation when on patchStage 2 as it has already
// played three times at this point and the patch will be weed-free
// after the third play
animate(player, RAKE_ANIM)
playAudio(player, Sounds.FARMING_RAKING_2442) playAudio(player, Sounds.FARMING_RAKING_2442)
firstRake = false
} }
if (delay < 5) { if (delay < 5) {
delay = 5 delay = 5
} else { } else {
patch.getPatchFor(player).currentGrowthStage++ patch.getPatchFor(player).currentGrowthStage++
patch.getPatchFor(player).setCurrentState(++patchStage) patch.getPatchFor(player).setCurrentState(++patchStage)
player.inventory.add(Item(Items.WEEDS_6055)) addItem(player, Items.WEEDS_6055)
player.skills.addExperience(Skills.FARMING,4.0) rewardXP(player, Skills.FARMING, 4.0)
}
if (patchStage >= 3) {
resetAnimator(player)
} }
return patchStage >= 3 return patchStage >= 3
} }

View file

@ -2,15 +2,20 @@ package content.global.skill.farming
enum class PatchType(val stageGrowthTime: Int) { enum class PatchType(val stageGrowthTime: Int) {
ALLOTMENT(10), ALLOTMENT(10),
HOPS(10), HOPS_PATCH(10),
TREE(40), TREE_PATCH(40),
FRUIT_TREE(160), FRUIT_TREE_PATCH(160),
BUSH(20), BUSH_PATCH(20),
FLOWER(5), FLOWER_PATCH(5),
HERB(20), HERB_PATCH(20),
SPIRIT_TREE(295), SPIRIT_TREE_PATCH(295),
MUSHROOM(30), MUSHROOM_PATCH(30),
BELLADONNA(80), BELLADONNA_PATCH(80),
CACTUS(60), CACTUS_PATCH(60),
EVIL_TURNIP(5) EVIL_TURNIP_PATCH(5);
/**
* Returns the display name of this PatchType.
*/
fun displayName(): String = name.lowercase().replace("_", " ")
} }

View file

@ -6,15 +6,15 @@ import org.rs09.consts.Items
enum class Plantable(val itemID: Int, val value: Int, val stages: Int, val plantingXP: Double, val harvestXP: Double, val checkHealthXP: Double, val requiredLevel: Int, val applicablePatch: PatchType, val harvestItem: Int, val protectionItem: Item? = null,val protectionFlower: Plantable? = null) { enum class Plantable(val itemID: Int, val value: Int, val stages: Int, val plantingXP: Double, val harvestXP: Double, val checkHealthXP: Double, val requiredLevel: Int, val applicablePatch: PatchType, val harvestItem: Int, val protectionItem: Item? = null,val protectionFlower: Plantable? = null) {
//Flowers //Flowers
MARIGOLD_SEED(5096,8,4,8.5,47.0,0.0,2,PatchType.FLOWER,Items.MARIGOLDS_6010), MARIGOLD_SEED(5096,8,4,8.5,47.0,0.0,2,PatchType.FLOWER_PATCH,Items.MARIGOLDS_6010),
ROSEMARY_SEED(5097,13,4,12.0,66.5,0.0,11,PatchType.FLOWER, Items.ROSEMARY_6014), ROSEMARY_SEED(5097,13,4,12.0,66.5,0.0,11,PatchType.FLOWER_PATCH, Items.ROSEMARY_6014),
NASTURTIUM_SEED(5098,18,4,19.5,111.0,0.0,24,PatchType.FLOWER,Items.NASTURTIUMS_6012), NASTURTIUM_SEED(5098,18,4,19.5,111.0,0.0,24,PatchType.FLOWER_PATCH,Items.NASTURTIUMS_6012),
WOAD_SEED(5099,23,4,20.5,115.5,0.0,25,PatchType.FLOWER,Items.WOAD_LEAF_1793), WOAD_SEED(5099,23,4,20.5,115.5,0.0,25,PatchType.FLOWER_PATCH,Items.WOAD_LEAF_1793),
LIMPWURT_SEED(5100,28,4,21.5,120.0,0.0,26,PatchType.FLOWER,Items.LIMPWURT_ROOT_225), LIMPWURT_SEED(5100,28,4,21.5,120.0,0.0,26,PatchType.FLOWER_PATCH,Items.LIMPWURT_ROOT_225),
WHITE_LILY_SEED(14589,37,4,42.0,250.0,0.0,52,PatchType.FLOWER,Items.WHITE_LILY_14583), WHITE_LILY_SEED(14589,37,4,42.0,250.0,0.0,52,PatchType.FLOWER_PATCH,Items.WHITE_LILY_14583),
//Flower(Technically) //Flower(Technically)
SCARECROW(6059,33,3,0.0,0.0,0.0,23,PatchType.FLOWER,Items.SCARECROW_6059), SCARECROW(6059,33,3,0.0,0.0,0.0,23,PatchType.FLOWER_PATCH,Items.SCARECROW_6059),
//Allotments //Allotments
POTATO_SEED(5318, 6, 4, 8.0, 9.0, 0.0, 1, PatchType.ALLOTMENT, Items.POTATO_1942,Item(Items.COMPOST_6032,2),MARIGOLD_SEED), POTATO_SEED(5318, 6, 4, 8.0, 9.0, 0.0, 1, PatchType.ALLOTMENT, Items.POTATO_1942,Item(Items.COMPOST_6032,2),MARIGOLD_SEED),
@ -26,61 +26,61 @@ enum class Plantable(val itemID: Int, val value: Int, val stages: Int, val plant
WATERMELON_SEED(5321,52,8,48.5,54.5,0.0,47,PatchType.ALLOTMENT,Items.WATERMELON_5982,Item(Items.CURRY_LEAF_5970,10),NASTURTIUM_SEED), WATERMELON_SEED(5321,52,8,48.5,54.5,0.0,47,PatchType.ALLOTMENT,Items.WATERMELON_5982,Item(Items.CURRY_LEAF_5970,10),NASTURTIUM_SEED),
//Hops //Hops
BARLEY_SEED(5305,49,4,8.5,9.5,0.0,3,PatchType.HOPS,Items.BARLEY_6006,Item(Items.COMPOST_6032,3)), BARLEY_SEED(5305,49,4,8.5,9.5,0.0,3,PatchType.HOPS_PATCH,Items.BARLEY_6006,Item(Items.COMPOST_6032,3)),
HAMMERSTONE_SEED(5307,4,4,9.0,10.0,0.0,4,PatchType.HOPS,Items.HAMMERSTONE_HOPS_5994,Item(Items.MARIGOLDS_6010)), HAMMERSTONE_SEED(5307,4,4,9.0,10.0,0.0,4,PatchType.HOPS_PATCH,Items.HAMMERSTONE_HOPS_5994,Item(Items.MARIGOLDS_6010)),
ASGARNIAN_SEED(5308,11,5,10.9,12.0,0.0,8,PatchType.HOPS,Items.ASGARNIAN_HOPS_5996,Item(Items.ONIONS10_5458)), ASGARNIAN_SEED(5308,11,5,10.9,12.0,0.0,8,PatchType.HOPS_PATCH,Items.ASGARNIAN_HOPS_5996,Item(Items.ONIONS10_5458)),
JUTE_SEED(5306,56,5,13.0,14.5,0.0,13,PatchType.HOPS,Items.JUTE_FIBRE_5931,Item(Items.BARLEY_MALT_6008,6)), JUTE_SEED(5306,56,5,13.0,14.5,0.0,13,PatchType.HOPS_PATCH,Items.JUTE_FIBRE_5931,Item(Items.BARLEY_MALT_6008,6)),
YANILLIAN_SEED(5309,19,6,14.5,16.0,0.0,16,PatchType.HOPS,Items.YANILLIAN_HOPS_5998,Item(Items.TOMATOES5_5968)), YANILLIAN_SEED(5309,19,6,14.5,16.0,0.0,16,PatchType.HOPS_PATCH,Items.YANILLIAN_HOPS_5998,Item(Items.TOMATOES5_5968)),
KRANDORIAN_SEED(5310,28,7,17.5,19.5,0.0,21,PatchType.HOPS,Items.KRANDORIAN_HOPS_6000,Item(Items.CABBAGES10_5478,3)), KRANDORIAN_SEED(5310,28,7,17.5,19.5,0.0,21,PatchType.HOPS_PATCH,Items.KRANDORIAN_HOPS_6000,Item(Items.CABBAGES10_5478,3)),
WILDBLOOD_SEED(5311,38,8,23.0,26.0,0.0,28,PatchType.HOPS,Items.WILDBLOOD_HOPS_6002,Item(Items.NASTURTIUMS_6012)), WILDBLOOD_SEED(5311,38,8,23.0,26.0,0.0,28,PatchType.HOPS_PATCH,Items.WILDBLOOD_HOPS_6002,Item(Items.NASTURTIUMS_6012)),
//Trees //Trees
OAK_SAPLING(5370,8,4,14.0,0.0,467.3,15,PatchType.TREE,Items.OAK_ROOTS_6043,Item(Items.TOMATOES5_5968)), OAK_SAPLING(5370,8,4,14.0,0.0,467.3,15,PatchType.TREE_PATCH,Items.OAK_ROOTS_6043,Item(Items.TOMATOES5_5968)),
WILLOW_SAPLING(5371,15,6,25.0,0.0,1456.5,30,PatchType.TREE,Items.WILLOW_ROOTS_6045,Item(Items.APPLES5_5386)), WILLOW_SAPLING(5371,15,6,25.0,0.0,1456.5,30,PatchType.TREE_PATCH,Items.WILLOW_ROOTS_6045,Item(Items.APPLES5_5386)),
MAPLE_SAPLING(5372,24,8,45.0,0.0,3403.4,45,PatchType.TREE,Items.MAPLE_ROOTS_6047,Item(Items.ORANGES5_5396)), MAPLE_SAPLING(5372,24,8,45.0,0.0,3403.4,45,PatchType.TREE_PATCH,Items.MAPLE_ROOTS_6047,Item(Items.ORANGES5_5396)),
YEW_SAPLING(5373,35,10,81.0,0.0,7069.9,60,PatchType.TREE,Items.YEW_ROOTS_6049,Item(Items.CACTUS_SPINE_6016,10)), YEW_SAPLING(5373,35,10,81.0,0.0,7069.9,60,PatchType.TREE_PATCH,Items.YEW_ROOTS_6049,Item(Items.CACTUS_SPINE_6016,10)),
MAGIC_SAPLING(5374,48,12,145.5,0.0,13768.3,75,PatchType.TREE,Items.MAGIC_ROOTS_6051,Item(Items.COCONUT_5974,25)), MAGIC_SAPLING(5374,48,12,145.5,0.0,13768.3,75,PatchType.TREE_PATCH,Items.MAGIC_ROOTS_6051,Item(Items.COCONUT_5974,25)),
//Fruit Trees //Fruit Trees
APPLE_SAPLING(5496,8,6,22.0,8.5,1199.5,27,PatchType.FRUIT_TREE,Items.COOKING_APPLE_1955,Item(Items.SWEETCORN_5986,9)), APPLE_SAPLING(5496,8,6,22.0,8.5,1199.5,27,PatchType.FRUIT_TREE_PATCH,Items.COOKING_APPLE_1955,Item(Items.SWEETCORN_5986,9)),
BANANA_SAPLING(5497,35,6,28.0,10.5,1750.5,33,PatchType.FRUIT_TREE,Items.BANANA_1963,Item(Items.APPLES5_5386,4)), BANANA_SAPLING(5497,35,6,28.0,10.5,1750.5,33,PatchType.FRUIT_TREE_PATCH,Items.BANANA_1963,Item(Items.APPLES5_5386,4)),
ORANGE_SAPLING(5498,72,6,35.5,13.5,2470.2,39,PatchType.FRUIT_TREE,Items.ORANGE_2108,Item(Items.STRAWBERRIES5_5406,3)), ORANGE_SAPLING(5498,72,6,35.5,13.5,2470.2,39,PatchType.FRUIT_TREE_PATCH,Items.ORANGE_2108,Item(Items.STRAWBERRIES5_5406,3)),
CURRY_SAPLING(5499,99,6,40.0,15.0,2906.9,42,PatchType.FRUIT_TREE,Items.CURRY_LEAF_5970,Item(Items.BANANAS5_5416,5)), CURRY_SAPLING(5499,99,6,40.0,15.0,2906.9,42,PatchType.FRUIT_TREE_PATCH,Items.CURRY_LEAF_5970,Item(Items.BANANAS5_5416,5)),
PINEAPPLE_SAPLING(5500,136,6,57.0,21.5,4605.7,51,PatchType.FRUIT_TREE,Items.PINEAPPLE_2114,Item(Items.WATERMELON_5982,10)), PINEAPPLE_SAPLING(5500,136,6,57.0,21.5,4605.7,51,PatchType.FRUIT_TREE_PATCH,Items.PINEAPPLE_2114,Item(Items.WATERMELON_5982,10)),
PAPAYA_SAPLING(5501,163,6,72.0,27.0,6146.4,57,PatchType.FRUIT_TREE,Items.PAPAYA_FRUIT_5972,Item(Items.PINEAPPLE_2114,10)), PAPAYA_SAPLING(5501,163,6,72.0,27.0,6146.4,57,PatchType.FRUIT_TREE_PATCH,Items.PAPAYA_FRUIT_5972,Item(Items.PINEAPPLE_2114,10)),
PALM_SAPLING(5502,200,6,110.5,41.5,10150.1,68,PatchType.FRUIT_TREE,Items.COCONUT_5974,Item(Items.PAPAYA_FRUIT_5972,15)), PALM_SAPLING(5502,200,6,110.5,41.5,10150.1,68,PatchType.FRUIT_TREE_PATCH,Items.COCONUT_5974,Item(Items.PAPAYA_FRUIT_5972,15)),
//Bushes //Bushes
REDBERRY_SEED(5101,5,5,11.5,4.5,64.0,10,PatchType.BUSH,Items.REDBERRIES_1951,Item(Items.CABBAGES10_5478,4)), REDBERRY_SEED(5101,5,5,11.5,4.5,64.0,10,PatchType.BUSH_PATCH,Items.REDBERRIES_1951,Item(Items.CABBAGES10_5478,4)),
CADAVABERRY_SEED(5102,15,6,18.0,7.0,102.5,22,PatchType.BUSH,Items.CADAVA_BERRIES_753,Item(Items.TOMATOES5_5968,3)), CADAVABERRY_SEED(5102,15,6,18.0,7.0,102.5,22,PatchType.BUSH_PATCH,Items.CADAVA_BERRIES_753,Item(Items.TOMATOES5_5968,3)),
DWELLBERRY_SEED(5103,26,27,31.5,12.0,177.5,36,PatchType.BUSH,Items.DWELLBERRIES_2126,Item(Items.STRAWBERRIES5_5406,3)), DWELLBERRY_SEED(5103,26,27,31.5,12.0,177.5,36,PatchType.BUSH_PATCH,Items.DWELLBERRIES_2126,Item(Items.STRAWBERRIES5_5406,3)),
JANGERBERRY_SEED(5104,38,8,50.5,19.0,284.5,48,PatchType.BUSH,Items.JANGERBERRIES_247,Item(Items.WATERMELON_5982,6)), JANGERBERRY_SEED(5104,38,8,50.5,19.0,284.5,48,PatchType.BUSH_PATCH,Items.JANGERBERRIES_247,Item(Items.WATERMELON_5982,6)),
WHITEBERRY_SEED(5105,51,8,78.0,29.0,437.5,59,PatchType.BUSH,Items.WHITE_BERRIES_239,null), WHITEBERRY_SEED(5105,51,8,78.0,29.0,437.5,59,PatchType.BUSH_PATCH,Items.WHITE_BERRIES_239,null),
POISON_IVY_SEED(5106,197,8,120.0,45.0,675.0,70,PatchType.BUSH,Items.POISON_IVY_BERRIES_6018,null), POISON_IVY_SEED(5106,197,8,120.0,45.0,675.0,70,PatchType.BUSH_PATCH,Items.POISON_IVY_BERRIES_6018,null),
//Herbs //Herbs
GUAM_SEED(5291,4,4,11.0,12.5,0.0,9,PatchType.HERB,Items.GRIMY_GUAM_199), GUAM_SEED(5291,4,4,11.0,12.5,0.0,9,PatchType.HERB_PATCH,Items.GRIMY_GUAM_199),
MARRENTILL_SEED(5292,11,4,13.5,15.0,0.0,14,PatchType.HERB,Items.GRIMY_MARRENTILL_201), MARRENTILL_SEED(5292,11,4,13.5,15.0,0.0,14,PatchType.HERB_PATCH,Items.GRIMY_MARRENTILL_201),
TARROMIN_SEED(5293,18,4,16.0,18.0,0.0,19,PatchType.HERB,Items.GRIMY_TARROMIN_203), TARROMIN_SEED(5293,18,4,16.0,18.0,0.0,19,PatchType.HERB_PATCH,Items.GRIMY_TARROMIN_203),
HARRALANDER_SEED(5294,25,4,21.5,24.0,0.0,26,PatchType.HERB,Items.GRIMY_HARRALANDER_205), HARRALANDER_SEED(5294,25,4,21.5,24.0,0.0,26,PatchType.HERB_PATCH,Items.GRIMY_HARRALANDER_205),
RANARR_SEED(5295,32,4,27.0,30.5,0.0,32,PatchType.HERB,Items.GRIMY_RANARR_207), RANARR_SEED(5295,32,4,27.0,30.5,0.0,32,PatchType.HERB_PATCH,Items.GRIMY_RANARR_207),
AVANTOE_SEED(5298,39,4,54.5,61.5,0.0,50,PatchType.HERB,Items.GRIMY_AVANTOE_211), AVANTOE_SEED(5298,39,4,54.5,61.5,0.0,50,PatchType.HERB_PATCH,Items.GRIMY_AVANTOE_211),
TOADFLAX_SEED(5296,46,4,34.0,38.5,0.0,38,PatchType.HERB,Items.GRIMY_TOADFLAX_3049), TOADFLAX_SEED(5296,46,4,34.0,38.5,0.0,38,PatchType.HERB_PATCH,Items.GRIMY_TOADFLAX_3049),
IRIT_SEED(5297,53,4,43.0,48.5,0.0,44,PatchType.HERB,Items.GRIMY_IRIT_209), IRIT_SEED(5297,53,4,43.0,48.5,0.0,44,PatchType.HERB_PATCH,Items.GRIMY_IRIT_209),
KWUARM_SEED(5299,68,4,69.0,78.0,0.0,56,PatchType.HERB,Items.GRIMY_KWUARM_213), KWUARM_SEED(5299,68,4,69.0,78.0,0.0,56,PatchType.HERB_PATCH,Items.GRIMY_KWUARM_213),
SNAPDRAGON_SEED(5300,75,4,87.5,98.5,0.0,62,PatchType.HERB,Items.GRIMY_SNAPDRAGON_3051), SNAPDRAGON_SEED(5300,75,4,87.5,98.5,0.0,62,PatchType.HERB_PATCH,Items.GRIMY_SNAPDRAGON_3051),
CADANTINE_SEED(5301,82,4,106.5,120.0,0.0,67,PatchType.HERB,Items.GRIMY_CADANTINE_215), CADANTINE_SEED(5301,82,4,106.5,120.0,0.0,67,PatchType.HERB_PATCH,Items.GRIMY_CADANTINE_215),
LANTADYME_SEED(5302,89,4,134.5,151.5,0.0,73,PatchType.HERB,Items.GRIMY_LANTADYME_2485), LANTADYME_SEED(5302,89,4,134.5,151.5,0.0,73,PatchType.HERB_PATCH,Items.GRIMY_LANTADYME_2485),
DWARF_WEED_SEED(5303,96,4,170.5,192.0,0.0,79,PatchType.HERB,Items.GRIMY_DWARF_WEED_217), DWARF_WEED_SEED(5303,96,4,170.5,192.0,0.0,79,PatchType.HERB_PATCH,Items.GRIMY_DWARF_WEED_217),
TORSTOL_SEED(5304,103,4,199.5,224.5,0.0,85,PatchType.HERB,Items.GRIMY_TORSTOL_219), TORSTOL_SEED(5304,103,4,199.5,224.5,0.0,85,PatchType.HERB_PATCH,Items.GRIMY_TORSTOL_219),
GOUT_TUBER(6311,192,4,105.0,45.0,0.0,29,PatchType.HERB,Items.GOUTWEED_3261), GOUT_TUBER(6311,192,4,105.0,45.0,0.0,29,PatchType.HERB_PATCH,Items.GOUTWEED_3261),
SPIRIT_WEED_SEED(12176, 204, 4, 32.0, 36.0, 0.0, 36, PatchType.HERB, Items.GRIMY_SPIRIT_WEED_12174), SPIRIT_WEED_SEED(12176, 204, 4, 32.0, 36.0, 0.0, 36, PatchType.HERB_PATCH, Items.GRIMY_SPIRIT_WEED_12174),
//Other //Other
BELLADONNA_SEED(5281, 4, 4, 91.0, 128.0, 0.0, 63, PatchType.BELLADONNA, Items.CAVE_NIGHTSHADE_2398), BELLADONNA_SEED(5281, 4, 4, 91.0, 128.0, 0.0, 63, PatchType.BELLADONNA_PATCH, Items.CAVE_NIGHTSHADE_2398),
MUSHROOM_SPORE(Items.MUSHROOM_SPORE_5282, 6, 7, 61.5, 57.7, 0.0, 53, PatchType.MUSHROOM, Items.MUSHROOM_6004), MUSHROOM_SPORE(Items.MUSHROOM_SPORE_5282, 6, 7, 61.5, 57.7, 0.0, 53, PatchType.MUSHROOM_PATCH, Items.MUSHROOM_6004),
CACTUS_SEED(Items.CACTUS_SEED_5280, 8, 7, 66.5, 25.0, 374.0, 55, PatchType.CACTUS, Items.CACTUS_SPINE_6016), CACTUS_SEED(Items.CACTUS_SEED_5280, 8, 7, 66.5, 25.0, 374.0, 55, PatchType.CACTUS_PATCH, Items.CACTUS_SPINE_6016),
EVIL_TURNIP_SEED(Items.EVIL_TURNIP_SEED_12148, 4, 1, 41.0, 46.0, 0.0, 42, PatchType.EVIL_TURNIP, Items.EVIL_TURNIP_12134) EVIL_TURNIP_SEED(Items.EVIL_TURNIP_SEED_12148, 4, 1, 41.0, 46.0, 0.0, 42, PatchType.EVIL_TURNIP_PATCH, Items.EVIL_TURNIP_12134)
; ;
constructor(itemID: Int, value: Int, stages: Int, plantingXP: Double, harvestXP: Double, checkHealthXP: Double, requiredLevel: Int, applicablePatch: PatchType, harvestItem: Int, protectionFlower: Plantable) constructor(itemID: Int, value: Int, stages: Int, plantingXP: Double, harvestXP: Double, checkHealthXP: Double, requiredLevel: Int, applicablePatch: PatchType, harvestItem: Int, protectionFlower: Plantable)

View file

@ -7,6 +7,7 @@ import core.game.interaction.IntType
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import content.global.skill.farming.timers.* import content.global.skill.farming.timers.*
import core.game.interaction.InteractionListener import core.game.interaction.InteractionListener
import core.tools.prependArticle
class SeedlingListener : InteractionListener { class SeedlingListener : InteractionListener {
override fun defineListeners() { override fun defineListeners() {
@ -27,6 +28,8 @@ class SeedlingListener : InteractionListener {
if (seedling == -1) return false if (seedling == -1) return false
if (!removeItem(player, seed.id) || !removeItem(player, pot)) return true if (!removeItem(player, seed.id) || !removeItem(player, pot)) return true
addItem(player, seedling) addItem(player, seedling)
sendMessage(player, "You sow ${prependArticle(seed.name.lowercase())} in the plantpot.")
sendMessage(player, "It needs watering before it will grow.")
return true return true
} }
@ -51,6 +54,7 @@ class SeedlingListener : InteractionListener {
if (index == -1) return Items.WATERING_CAN_5331 if (index == -1) return Items.WATERING_CAN_5331
return if (index != WATERING_CANS.size -1) WATERING_CANS[index + 1] else Items.WATERING_CAN_5331 return if (index != WATERING_CANS.size -1) WATERING_CANS[index + 1] else Items.WATERING_CAN_5331
} }
fun getSeedling(id: Int) : Int { fun getSeedling(id: Int) : Int {
return when (id) { return when (id) {
Items.ACORN_5312 -> Items.OAK_SEEDLING_5358 Items.ACORN_5312 -> Items.OAK_SEEDLING_5358

View file

@ -1,7 +1,6 @@
package content.global.skill.farming package content.global.skill.farming
import core.api.* import core.api.*
import core.game.component.Component
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.item.Item import core.game.node.item.Item
import org.rs09.consts.Components import org.rs09.consts.Components
@ -15,12 +14,13 @@ class ToolLeprechaunInterface : InterfaceListener {
override fun defineInterfaceListeners() { override fun defineInterfaceListeners() {
onOpen(FARMING_TOOLS) { player, component -> onOpen(FARMING_TOOLS) { player, component ->
player.interfaceManager.openSingleTab(Component(TOOLS_SIDE)) openSingleTab(player, TOOLS_SIDE)
return@onOpen true return@onOpen true
} }
onClose(FARMING_TOOLS) { player, _ -> onClose(FARMING_TOOLS) { player, _ ->
player.interfaceManager.closeSingleTab() closeTabInterface(player)
return@onClose true
} }
on(FARMING_TOOLS) { player, _, opcode, buttonID, _, _ -> on(FARMING_TOOLS) { player, _, opcode, buttonID, _, _ ->
@ -34,10 +34,12 @@ class ToolLeprechaunInterface : InterfaceListener {
} }
37 -> { 37 -> {
if (!hasWateringCan(player)) { if (!hasWateringCan(player)) {
player.dialogueInterpreter.sendDialogue("You don't have any of those stored.") sendMessage(player, "You haven't got a watering can stored in here!")
} else { } else {
player.inventory.add(Item(getWateringCan(player))) if (freeSlots(player) == 0) {
setNoWateringCan(player) sendMessage(player, "You don't have enough space for that.")
}
if (addItem(player, getWateringCan(player))) setNoWateringCan(player)
} }
} }
38 -> doWithdrawal(player, Items.GARDENING_TROWEL_5325, ::setHasGardeningTrowel, ::hasGardeningTrowel) 38 -> doWithdrawal(player, Items.GARDENING_TROWEL_5325, ::setHasGardeningTrowel, ::hasGardeningTrowel)
@ -54,31 +56,31 @@ class ToolLeprechaunInterface : InterfaceListener {
19 -> doDeposit(player, Items.SEED_DIBBER_5343, ::setHasDibber, ::hasDibber) 19 -> doDeposit(player, Items.SEED_DIBBER_5343, ::setHasDibber, ::hasDibber)
20 -> doDeposit(player, Items.SPADE_952, ::setHasSpade, ::hasSpade) 20 -> doDeposit(player, Items.SPADE_952, ::setHasSpade, ::hasSpade)
21 -> { 21 -> {
if(!player.inventory.contains(Items.SECATEURS_5329,1) && !player.inventory.contains(Items.MAGIC_SECATEURS_7409,1)){ if (!inInventory(player, Items.SECATEURS_5329) && !inInventory(player, Items.MAGIC_SECATEURS_7409)) {
player.dialogueInterpreter.sendDialogue("You don't have any of those to store.") sendMessage(player, "You haven't got any secateurs to store.")
} else if (!hasSecateurs(player)) { } else if (!hasSecateurs(player)) {
if(player.inventory.contains(Items.MAGIC_SECATEURS_7409,1)){ if (inInventory(player, Items.MAGIC_SECATEURS_7409)) {
player.inventory.remove(Item(Items.MAGIC_SECATEURS_7409)) removeItem(player, Items.MAGIC_SECATEURS_7409)
setHasSecateurs(player,true) setHasSecateurs(player,true)
setHasMagicSecateurs(player,true) setHasMagicSecateurs(player,true)
} else { } else {
player.inventory.remove(Item(Items.SECATEURS_5329)) removeItem(player, Items.SECATEURS_5329)
setHasSecateurs(player,true) setHasSecateurs(player,true)
setHasMagicSecateurs(player,false) setHasMagicSecateurs(player,false)
} }
} else { } else {
player.dialogueInterpreter.sendDialogue("You already have one of those stored.") sendMessage(player, "You already have one of those stored.")
} }
} }
22 -> { 22 -> {
val can = getHighestCan(player) val can = getHighestCan(player)
if (can == null) { if (can == null) {
player.dialogueInterpreter.sendDialogue("You don't have any of those to store.") sendMessage(player, "You haven't got a watering can to store.")
} else if (!hasWateringCan(player)) { } else if (!hasWateringCan(player)) {
player.inventory.remove(can) removeItem(player, can)
setWateringCan(player,can) setWateringCan(player,can)
} else { } else {
player.dialogueInterpreter.sendDialogue("You already have one of those stored.") sendMessage(player, "You already have one of those stored.")
} }
} }
23 -> doDeposit(player, Items.GARDENING_TROWEL_5325, ::setHasGardeningTrowel, ::hasGardeningTrowel) 23 -> doDeposit(player, Items.GARDENING_TROWEL_5325, ::setHasGardeningTrowel, ::hasGardeningTrowel)
@ -94,38 +96,42 @@ class ToolLeprechaunInterface : InterfaceListener {
private fun doWithdrawal(player: Player?, item: Int, withdrawMethod: (Player?, Boolean) -> Unit, checkMethod: (Player?) -> Boolean) { private fun doWithdrawal(player: Player?, item: Int, withdrawMethod: (Player?, Boolean) -> Unit, checkMethod: (Player?) -> Boolean) {
player ?: return player ?: return
if (!checkMethod.invoke(player)) { if (!checkMethod.invoke(player)) {
player.dialogueInterpreter.sendDialogue("You don't have any of those stored.") val determiner = if (getItemName(item).lowercase().endsWith("s")) "any" else "a"
sendMessage(player, "You haven't got $determiner ${getItemName(item).lowercase()} stored in here!")
} else { } else {
if(!player.inventory.hasSpaceFor(Item(item))){ if (!hasSpaceFor(player, Item(item))) {
player.dialogueInterpreter.sendDialogue("You don't have enough space for that.") sendMessage(player, "You don't have enough space for that.")
return return
} }
withdrawMethod.invoke(player, false) withdrawMethod.invoke(player, false)
player.inventory.add(Item(item)) addItem(player, item)
} }
} }
private fun doDeposit(player: Player?, item: Int, depositMethod: (Player?, Boolean) -> Unit, checkMethod: (Player?) -> Boolean) { private fun doDeposit(player: Player?, item: Int, depositMethod: (Player?, Boolean) -> Unit, checkMethod: (Player?) -> Boolean) {
player ?: return player ?: return
if(!player.inventory.contains(item,1)){ if (!inInventory(player, item)) {
player.dialogueInterpreter.sendDialogue("You don't have any of those to store.") val determiner = if (getItemName(item).lowercase().endsWith("s")) "any" else "a"
sendMessage(player, "You haven't got $determiner ${getItemName(item).lowercase()} to store.")
return
} }
if (!checkMethod.invoke(player)) { if (!checkMethod.invoke(player)) {
depositMethod.invoke(player, true) depositMethod.invoke(player, true)
player.inventory.remove(Item(item)) removeItem(player, item)
} else { } else {
player.dialogueInterpreter.sendDialogue("You already have one of those stored.") sendMessage(player, "You already have one of those stored.")
} }
} }
private fun doStackedDeposit(player: Player?, item: Int, amount: Int, updateQuantityMethod: (Player?, Int) -> Unit, quantityCheckMethod: (Player?) -> Int) { private fun doStackedDeposit(player: Player?, item: Int, amount: Int, updateQuantityMethod: (Player?, Int) -> Unit, quantityCheckMethod: (Player?) -> Int) {
player ?: return player ?: return
val hasAmount = player.inventory.getAmount(item) val hasAmount = amountInInventory(player, item)
var finalAmount = amount var finalAmount = amount
val spaceLeft = (if (item == Items.BUCKET_1925) 31 else 255) - quantityCheckMethod.invoke(player) val spaceLeft = (if (item == Items.BUCKET_1925) 31 else 255) - quantityCheckMethod.invoke(player)
if (hasAmount == 0) { if (hasAmount == 0) {
player.dialogueInterpreter.sendDialogue("You don't have any of those to store.") val itemName = if (item == Items.BUCKET_1925) "buckets" else getItemName(item).lowercase()
sendMessage(player, "You haven't got any $itemName to store.")
return return
} }
@ -138,15 +144,11 @@ class ToolLeprechaunInterface : InterfaceListener {
if (amt > spaceLeft) { if (amt > spaceLeft) {
amt = spaceLeft amt = spaceLeft
} }
if(amt == 0){ if (removeItem(player, Item(item, amt))) updateQuantityMethod.invoke(player, amt)
player.dialogueInterpreter.sendDialogue("You don't have any of those to store.")
return@sendInputDialogue
}
player.inventory.remove(Item(item,amt))
updateQuantityMethod.invoke(player,amt)
} }
return return
} }
if (amount == -1) { if (amount == -1) {
finalAmount = hasAmount finalAmount = hasAmount
if (finalAmount > spaceLeft) { if (finalAmount > spaceLeft) {
@ -157,19 +159,17 @@ class ToolLeprechaunInterface : InterfaceListener {
if (finalAmount > hasAmount) { if (finalAmount > hasAmount) {
finalAmount = hasAmount finalAmount = hasAmount
} }
if(finalAmount > spaceLeft) finalAmount = spaceLeft
if(!player.inventory.contains(item,finalAmount)){
player.dialogueInterpreter.sendDialogue("You don't have any of those to store.")
return
}
if (finalAmount > spaceLeft) { if (finalAmount > spaceLeft) {
player.dialogueInterpreter.sendDialogue("You can't store any more of those.") if (item == Items.BUCKET_1925) {
sendMessage(player, "You can't store that many buckets in here.")
} else {
sendMessage(player, "You can't store that much ${getItemName(item).lowercase()} in here.")
}
return return
} }
player.inventory.remove(Item(item,finalAmount)) removeItem(player, Item(item, finalAmount))
updateQuantityMethod.invoke(player,finalAmount) updateQuantityMethod.invoke(player,finalAmount)
} }
@ -177,41 +177,44 @@ class ToolLeprechaunInterface : InterfaceListener {
player ?: return player ?: return
val hasAmount = quantityCheckMethod.invoke(player) val hasAmount = quantityCheckMethod.invoke(player)
var finalAmount = amount var finalAmount = amount
if (hasAmount == 0) { if (hasAmount == 0) {
player.dialogueInterpreter.sendDialogue("You don't have any of those stored.") val itemName = if (item == Items.BUCKET_1925) "buckets" else getItemName(item).lowercase()
} else { sendMessage(player, "You haven't got any $itemName stored in here!")
return
}
if (amount == -2) { if (amount == -2) {
sendInputDialogue(player, InputType.AMOUNT, "Enter the amount:") { value -> sendInputDialogue(player, InputType.AMOUNT, "Enter the amount:") { value ->
var amt = value as Int var amt = value as Int
if (amt > hasAmount) { if (amt > hasAmount) {
amt = hasAmount amt = hasAmount
} }
if(amt > player.inventory.freeSlots()){ if (amt > freeSlots(player)) {
amt = player.inventory.freeSlots() amt = freeSlots(player)
} }
if (amt <= 0) { if (amt <= 0) {
player.dialogueInterpreter.sendDialogue("You don't have enough inventory space for that.") sendMessage(player, "You don't have enough inventory space for that.")
} else { } else {
player.inventory.add(Item(item, amt)) addItem(player, item, amt)
updateQuantityMethod.invoke(player, -amt) updateQuantityMethod.invoke(player, -amt)
} }
} }
return return
} }
if (amount == -1) { if (amount == -1) {
finalAmount = player.inventory.freeSlots() finalAmount = freeSlots(player)
} }
if (finalAmount > hasAmount) { if (finalAmount > hasAmount) {
finalAmount = hasAmount finalAmount = hasAmount
} }
if(!player.inventory.hasSpaceFor(Item(item,finalAmount)) || finalAmount == 0){ if (!hasSpaceFor(player, Item(item, finalAmount)) || finalAmount == 0) {
player.dialogueInterpreter.sendDialogue("You don't have enough inventory space for that.") sendMessage(player, "You don't have enough inventory space for that.")
return return
} }
player.inventory.add(Item(item,finalAmount)) addItem(player, item, finalAmount)
updateQuantityMethod.invoke(player, -finalAmount) updateQuantityMethod.invoke(player, -finalAmount)
} }
}
fun getAmount(opcode: Int): Int { fun getAmount(opcode: Int): Int {
return when (opcode) { return when (opcode) {

View file

@ -1,7 +1,6 @@
package content.global.skill.farming package content.global.skill.farming
import core.api.* import core.api.*
import core.game.node.item.Item
import core.game.system.task.Pulse import core.game.system.task.Pulse
import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items import org.rs09.consts.Items
@ -28,55 +27,55 @@ class UseWithBinHandler : InteractionListener {
when (used) { when (used) {
Items.COMPOST_POTION1_6476, Items.COMPOST_POTION2_6474, Items.COMPOST_POTION3_6472, Items.COMPOST_POTION4_6470 -> { Items.COMPOST_POTION1_6476, Items.COMPOST_POTION2_6474, Items.COMPOST_POTION3_6472, Items.COMPOST_POTION4_6470 -> {
if (!bin.isSuperCompost && bin.isFinished && !bin.isClosed) { if (!bin.isSuperCompost && bin.isFinished && !bin.isClosed) {
player.animator.animate(compostPotionAnimation) animate(player, compostPotionAnimation)
player.pulseManager.run(object : Pulse(compostPotionAnimation.duration){ submitIndividualPulse(player, object : Pulse(compostPotionAnimation.duration) {
override fun pulse(): Boolean { override fun pulse(): Boolean {
if(player.inventory.remove(usedNode.asItem())) { if (removeItem(player, usedNode.asItem())) {
bin.convert() bin.convert()
player.inventory.add(Item(used.getNext())) addItem(player, used.getNext())
} }
return true return true
} }
}) })
} else { } else {
player.dialogueInterpreter.sendDialogue("You can only do this with an open bin of","finished regular compost.") sendDialogue(player, "You can only do this with an open bin of finished regular compost.")
} }
} }
Items.BUCKET_1925 -> { Items.BUCKET_1925 -> {
if (bin.isFinished && !bin.isClosed) { if (bin.isFinished && !bin.isClosed) {
player.pulseManager.run(object : Pulse(scoopAnimation.duration){ submitIndividualPulse(player, object : Pulse(scoopAnimation.duration) {
override fun pulse(): Boolean { override fun pulse(): Boolean {
if (!player.inventory.containsItem(usedNode.asItem())) return true if (!player.inventory.containsItem(usedNode.asItem())) return true
player.animator.animate(scoopAnimation) animate(player, scoopAnimation)
val item = bin.takeItem() val item = bin.takeItem()
if(item != null && player.inventory.remove(usedNode.asItem())){ if (item != null && removeItem(player, usedNode.asItem())) {
player.inventory.add(item) player.inventory.add(item)
} }
return item == null || !player.inventory.containsItem(usedNode.asItem()) return item == null || !player.inventory.containsItem(usedNode.asItem())
} }
}) })
} else { } else {
player.dialogueInterpreter.sendDialogue("You can only scoop an opened bin of finished compost.") sendDialogue(player, "You can only scoop an opened bin of finished compost.")
} }
} }
else -> else ->
if (bin.isFull()) { if (bin.isFull()) {
player.sendMessage("This compost bin is already full.") sendMessage(player, "This compost bin is already full.")
return@onUseWith true return@onUseWith true
} else if (!bin.isFinished) { } else if (!bin.isFinished) {
player.pulseManager.run(object : Pulse(fillAnim.duration){ submitIndividualPulse(player, object : Pulse(fillAnim.duration) {
override fun pulse(): Boolean { override fun pulse(): Boolean {
player.animator.animate(fillAnim) animate(player, fillAnim)
if(player.inventory.remove(usedNode.asItem())){ if (removeItem(player, usedNode.asItem())) {
bin.addItem(usedNode.asItem()) bin.addItem(usedNode.asItem())
} }
return bin.isFull() || player.inventory.getAmount(usedNode.asItem()) == 0 return bin.isFull() || player.inventory.getAmount(usedNode.asItem()) == 0
} }
}) })
} else { } else {
sendMessage (player, "You should empty the remaining compost first.") sendDialogue(player, "The compost bin must be empty of compost before you can put new items in it.")
} }
} }
return@onUseWith true return@onUseWith true

View file

@ -9,6 +9,9 @@ import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items import org.rs09.consts.Items
import core.game.interaction.IntType import core.game.interaction.IntType
import core.game.interaction.InteractionListener import core.game.interaction.InteractionListener
import core.game.interaction.QueueStrength
import core.tools.StringUtils
import core.tools.prependArticle
import org.rs09.consts.Sounds import org.rs09.consts.Sounds
class UseWithPatchHandler : InteractionListener { class UseWithPatchHandler : InteractionListener {
@ -41,53 +44,81 @@ class UseWithPatchHandler : InteractionListener {
player.faceLocation(with.location) player.faceLocation(with.location)
when (usedItem.id) { when (usedItem.id) {
RAKE -> PatchRaker.rake(player,patch) RAKE -> PatchRaker.rake(player,patch)
SEED_DIBBER -> player.sendMessage("I should plant a seed, not the seed dibber.") SEED_DIBBER -> sendMessage(player, "I should plant a seed, not the seed dibber.")
SPADE -> player.dialogueInterpreter.open(67984003,patch.getPatchFor(player)) //DigUpPatchDialogue.kt SPADE -> {
val anim = getAnimation(830)
val p = patch.getPatchFor(player)
if (p.isDead) {
sendMessage(player, "You start digging the farming patch...")
queueScript(player, 0, QueueStrength.WEAK) { stage: Int ->
when (stage) {
0 -> {
animate(player, anim)
playAudio(player, Sounds.DIGSPADE_1470)
return@queueScript delayScript(player,anim.duration + 2)
}
1 -> {
animate(player, anim)
playAudio(player, Sounds.DIGSPADE_1470)
return@queueScript delayScript(player, anim.duration + 1)
}
2 -> {
p.clear()
sendMessage(player, "You have successfully cleared this patch for new crops.")
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
}
} else {
openDialogue(player, 67984003, patch.getPatchFor(player)) // DigUpPatchDialogue.kt
}
}
SECATEURS, MAGIC_SECATEURS -> { SECATEURS, MAGIC_SECATEURS -> {
val p = patch.getPatchFor(player) val p = patch.getPatchFor(player)
if(patch.type == PatchType.TREE) { if (patch.type == PatchType.TREE_PATCH) {
if (p.isDiseased && !p.isDead) { if (p.isDiseased && !p.isDead) {
player.pulseManager.run(object: Pulse(){ submitIndividualPulse(player, object: Pulse() {
override fun pulse(): Boolean { override fun pulse(): Boolean {
if (usedItem.id == SECATEURS ) player.animator.animate(secateursTreeAnim) else player.animator.animate(magicSecateursTreeAnim) if (usedItem.id == SECATEURS) animate(player, secateursTreeAnim) else animate(player, magicSecateursTreeAnim)
p.cureDisease() p.cureDisease()
return true return true
} }
}) })
} else if (p.plantable == Plantable.WILLOW_SAPLING && p.harvestAmt > 0) { } else if (p.plantable == Plantable.WILLOW_SAPLING && p.harvestAmt > 0) {
val pulse = CropHarvester.harvestPulse(player, with, Items.WILLOW_BRANCH_5933) ?: return@onUseWith false val pulse = CropHarvester.harvestPulse(player, with, Items.WILLOW_BRANCH_5933) ?: return@onUseWith false
player.pulseManager.run(pulse) submitIndividualPulse(player, pulse)
} }
} }
} }
TROWEL, Items.PLANT_POT_5350 -> { TROWEL, Items.PLANT_POT_5350 -> {
if(!player.inventory.containsAtLeastOneItem(TROWEL)) { if (!inInventory(player, TROWEL)) {
player.sendMessage("You need a trowel to fill plant pots with dirt.") sendMessage(player, "You need a trowel to fill plant pots with dirt.")
return@onUseWith true return@onUseWith true
} }
val p = patch.getPatchFor(player) val p = patch.getPatchFor(player)
if(!p.isWeedy()){ if (!p.isWeedy() && !p.isEmptyAndWeeded()) {
player.sendMessage("This patch has something growing in it.") sendMessage(player, "This patch has something growing in it.")
return@onUseWith true return@onUseWith true
} else if (p.currentGrowthStage != 3) { } else if (p.currentGrowthStage != 3) {
player.sendMessage("I should clear this of weeds before trying to take some dirt.") sendMessage(player, "I should clear this of weeds before trying to take some dirt.")
return@onUseWith true return@onUseWith true
} }
val potAmount = player.inventory.getAmount(Items.PLANT_POT_5350) val potAmount = amountInInventory(player, Items.PLANT_POT_5350)
if (potAmount == 0) { if (potAmount == 0) {
player.sendMessage("You have no plant pots to fill.") sendMessage(player, "You have no plant pots to fill.")
return@onUseWith true return@onUseWith true
} }
val anim = Animation(2272) val anim = Animation(2272)
player.pulseManager.run(object : Pulse(anim.duration){ submitIndividualPulse(player, object : Pulse(anim.duration) {
override fun pulse(): Boolean { override fun pulse(): Boolean {
if(player.inventory.remove(Item(Items.PLANT_POT_5350))){ if (removeItem(player, Items.PLANT_POT_5350)) {
player.animator.animate(anim) animate(player, anim)
player.inventory.add(Item(Items.PLANT_POT_5354)) addItem(player, Items.PLANT_POT_5354)
} else return true } else return true
return false return false
} }
@ -96,37 +127,52 @@ class UseWithPatchHandler : InteractionListener {
Items.PLANT_CURE_6036 -> { Items.PLANT_CURE_6036 -> {
val p = patch.getPatchFor(player) val p = patch.getPatchFor(player)
val patchName = p.patch.type.displayName()
if (p.isDiseased && !p.isDead) { if (p.isDiseased && !p.isDead) {
player.pulseManager.run(object: Pulse(){ sendMessage(player, "You treat the $patchName with the plant cure.")
override fun pulse(): Boolean { queueScript(player, 0, QueueStrength.WEAK) { stage: Int ->
player.animator.animate(plantCureAnim) when (stage) {
0 -> {
animate(player, plantCureAnim)
playAudio(player, Sounds.FARMING_PLANTCURE_2438) playAudio(player, Sounds.FARMING_PLANTCURE_2438)
if(player.inventory.remove(usedItem)){ return@queueScript delayScript(player, plantCureAnim.duration / 2)
player.inventory.add(Item(Items.VIAL_229)) }
1 -> {
if (removeItem(player, usedItem)) {
addItem(player, Items.VIAL_229)
p.cureDisease() p.cureDisease()
sendMessage(player, "It is restored to health.")
}
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
} }
return true
} }
})
} else { } else {
player.sendMessage("I have no reason to do this right now.") sendMessage(player, "I have no reason to do this right now.")
} }
} }
Items.WATERING_CAN1_5333,Items.WATERING_CAN2_5334,Items.WATERING_CAN3_5335,Items.WATERING_CAN4_5336,Items.WATERING_CAN5_5337,Items.WATERING_CAN6_5338,Items.WATERING_CAN7_5339,Items.WATERING_CAN8_5340 -> { Items.WATERING_CAN_5331,Items.WATERING_CAN1_5333,Items.WATERING_CAN2_5334,Items.WATERING_CAN3_5335,Items.WATERING_CAN4_5336,Items.WATERING_CAN5_5337,Items.WATERING_CAN6_5338,Items.WATERING_CAN7_5339,Items.WATERING_CAN8_5340 -> {
val p = patch.getPatchFor(player) val p = patch.getPatchFor(player)
val t = p.patch.type val t = p.patch.type
if(!p.isWatered && p.plantable != Plantable.SCARECROW && (t == PatchType.ALLOTMENT || t == PatchType.FLOWER || t == PatchType.HOPS) && !p.isGrown()){ if (p.isWatered || p.isEmptyAndWeeded() || p.isGrown() || p.plantable == Plantable.SCARECROW) {
player.pulseManager.run(object : Pulse(){ sendMessage(player, "This patch doesn't need watering.")
} else if (t == PatchType.ALLOTMENT || t == PatchType.FLOWER_PATCH || t == PatchType.HOPS_PATCH) {
submitIndividualPulse(player, object : Pulse() {
override fun pulse(): Boolean { override fun pulse(): Boolean {
if (p.isWeedy()) { if (p.isWeedy()) {
player.sendMessage("You should grow something first.") sendMessage(player, "You should grow something first.")
return true return true
} }
player.animator.animate(wateringCanAnim) if (usedItem.id == Items.WATERING_CAN_5331) {
sendMessage(player, "You need to fill the watering can first.")
return true
}
animate(player, wateringCanAnim)
playAudio(player, Sounds.FARMING_WATERING_2446) playAudio(player, Sounds.FARMING_WATERING_2446)
if(player.inventory.remove(usedItem)){ if (removeItem(player, usedItem)) {
player.inventory.add(Item(usedItem.id.getNext())) addItem(player, usedItem.id.getNext())
p.water() p.water()
} }
return true return true
@ -137,25 +183,28 @@ class UseWithPatchHandler : InteractionListener {
Items.SUPERCOMPOST_6034, Items.COMPOST_6032 -> { Items.SUPERCOMPOST_6034, Items.COMPOST_6032 -> {
val p = patch.getPatchFor(player) val p = patch.getPatchFor(player)
if(p.compost == CompostType.NONE) { val patchName = p.patch.type.displayName()
player.animator.animate(pourBucketAnim)
if (!p.isEmptyAndWeeded()) {
sendMessage(player, "This patch needs to be empty and weeded to do that.")
} else if (p.compost == CompostType.NONE) {
animate(player, pourBucketAnim)
playAudio(player, Sounds.FARMING_COMPOST_2427) playAudio(player, Sounds.FARMING_COMPOST_2427)
player.pulseManager.run(object : Pulse(){ runTask(player) {
override fun pulse(): Boolean {
if (player.inventory.remove(usedItem,false)) { if (player.inventory.remove(usedItem,false)) {
p.compost = if(usedItem.id == Items.SUPERCOMPOST_6034) CompostType.SUPER else CompostType.NORMAL sendMessage(player, "You treat the $patchName with ${usedItem.name.lowercase()}.")
if(p.compost == CompostType.SUPER) rewardXP(player, Skills.FARMING, 26.0) else rewardXP(player, Skills.FARMING, 18.5) p.compost = if (usedItem.id == Items.SUPERCOMPOST_6034) CompostType.SUPERCOMPOST else CompostType.COMPOST
if(p.plantable != null && p.plantable?.applicablePatch != PatchType.FLOWER) { if (p.compost == CompostType.SUPERCOMPOST) rewardXP(player, Skills.FARMING, 26.0) else rewardXP(player, Skills.FARMING, 18.5)
p.harvestAmt += if(p.compost == CompostType.NORMAL) 1 else if(p.compost == CompostType.SUPER) 2 else 0 if (p.plantable != null && p.plantable?.applicablePatch != PatchType.FLOWER_PATCH) {
p.harvestAmt += if (p.compost == CompostType.COMPOST) 1 else if (p.compost == CompostType.SUPERCOMPOST) 2 else 0
} }
p.cropLives += if(p.compost == CompostType.SUPER) 2 else 1 p.cropLives += if (p.compost == CompostType.SUPERCOMPOST) 2 else 1
player.inventory.add(Item(Items.BUCKET_1925)) addItem(player, Items.BUCKET_1925)
} }
return true return@runTask
} }
})
} else { } else {
player.sendMessage("This patch has already been treated with compost.") sendMessage(player, "This $patchName has already been treated with ${p.compost.name.lowercase()}.")
} }
} }
@ -163,26 +212,28 @@ class UseWithPatchHandler : InteractionListener {
val plantable = Plantable.forItemID(usedItem.id) ?: return@onUseWith false val plantable = Plantable.forItemID(usedItem.id) ?: return@onUseWith false
if (plantable.applicablePatch != patch.type) { if (plantable.applicablePatch != patch.type) {
player.sendMessage("You can't plant that seed in this patch.") val seedNamePlural = StringUtils.plusS(plantable.name.replace("_", " ").lowercase())
val patchType = if (plantable.applicablePatch == PatchType.ALLOTMENT) "a vegetable patch" else prependArticle(plantable.applicablePatch.displayName())
sendMessage(player, "You can only plant $seedNamePlural in $patchType.")
return@onUseWith true return@onUseWith true
} }
if(plantable.requiredLevel > player.skills.getLevel(Skills.FARMING)){ if (!hasLevelDyn(player, Skills.FARMING, plantable.requiredLevel)) {
player.sendMessage("You need a Farming level of ${plantable.requiredLevel} to plant this.") sendMessage(player, "You need a Farming level of ${plantable.requiredLevel} to plant this.")
return@onUseWith true return@onUseWith true
} }
val p = patch.getPatchFor(player) val p = patch.getPatchFor(player)
if(p.getCurrentState() < 3 && p.isWeedy()){ if (p.getCurrentState() < 3 && p.isWeedy() && plantable != Plantable.SCARECROW) {
player.sendMessage("You must weed your patch before you can plant a seed in it.") sendMessage(player, "This patch needs weeding first.")
return@onUseWith true return@onUseWith true
} else if (p.getCurrentState() > 3) { } else if (p.getCurrentState() > 3) {
player.sendMessage("There is already something growing in this patch.") sendMessage(player, "There is already something growing in this patch.")
return@onUseWith true return@onUseWith true
} }
val plantItem = val plantItem =
if(patch.type == PatchType.ALLOTMENT) Item(plantable.itemID,3) else if(patch.type == PatchType.HOPS){ if (patch.type == PatchType.ALLOTMENT) Item(plantable.itemID,3) else if (patch.type == PatchType.HOPS_PATCH) {
if (plantable == Plantable.JUTE_SEED) Item(plantable.itemID,3) else Item(plantable.itemID,4) if (plantable == Plantable.JUTE_SEED) Item(plantable.itemID,3) else Item(plantable.itemID,4)
} else { } else {
Item(plantable.itemID,1) Item(plantable.itemID,1)
@ -190,36 +241,49 @@ class UseWithPatchHandler : InteractionListener {
if (patch.type == PatchType.ALLOTMENT) { if (patch.type == PatchType.ALLOTMENT) {
if (!player.inventory.containsItem(plantItem)) { if (!player.inventory.containsItem(plantItem)) {
player.sendMessage("You need 3 seeds to plant an allotment patch.") sendMessage(player, "You need 3 seeds to plant an allotment patch.")
return@onUseWith true return@onUseWith true
} }
} }
if(patch.type != PatchType.FRUIT_TREE && patch.type != PatchType.TREE){ if (patch.type != PatchType.FRUIT_TREE_PATCH && patch.type != PatchType.TREE_PATCH) {
if(!player.inventory.contains(Items.SEED_DIBBER_5343,1)){ if (!inInventory(player, Items.SEED_DIBBER_5343)) {
player.sendMessage("You need a seed dibber to plant that.") sendMessage(player, "You need a seed dibber to plant that.")
return@onUseWith true return@onUseWith true
} }
} else { } else {
if(!player.inventory.contains(Items.SPADE_952,1)){ if (!inInventory(player, Items.SPADE_952) && plantable != Plantable.SCARECROW) {
player.sendMessage("You need a spade to plant that.") sendMessage(player, "You need a spade to plant that.")
return@onUseWith true return@onUseWith true
} }
} }
player.lock() player.lock()
if(player.inventory.remove(plantItem)) { if (removeItem(player, plantItem)) {
player.animator.animate(Animation(2291)) if (plantable != Plantable.SCARECROW) {
animate(player, 2291)
playAudio(player, Sounds.FARMING_DIBBING_2432) playAudio(player, Sounds.FARMING_DIBBING_2432)
player.pulseManager.run(object : Pulse(3) { }
val delay = if (plantable == Plantable.SCARECROW) 0 else 3
submitIndividualPulse(player, object : Pulse(delay) {
override fun pulse(): Boolean { override fun pulse(): Boolean {
if (plantable == Plantable.JUTE_SEED && patch == FarmingPatch.MCGRUBOR_HOPS && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 0, 7)) { if (plantable == Plantable.JUTE_SEED && patch == FarmingPatch.MCGRUBOR_HOPS && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 0, 7)) {
player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 0, 7) player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 0, 7)
} }
p.plant(plantable) p.plant(plantable)
player.skills.addExperience(Skills.FARMING, plantable.plantingXP) rewardXP(player, Skills.FARMING, plantable.plantingXP)
p.setNewHarvestAmount() p.setNewHarvestAmount()
if(p.patch.type == PatchType.TREE || p.patch.type == PatchType.FRUIT_TREE){ if (p.patch.type == PatchType.TREE_PATCH || p.patch.type == PatchType.FRUIT_TREE_PATCH) {
player.inventory.add(Item(Items.PLANT_POT_5350)) addItem(player, Items.PLANT_POT_5350)
} }
val itemAmount = if (plantItem.amount == 1) "a" else plantItem.amount
val itemName = if (plantItem.amount == 1) getItemName(plantItem.id).lowercase() else StringUtils.plusS(getItemName(plantItem.id).lowercase())
val patchName = p.patch.type.displayName()
if (plantable == Plantable.SCARECROW) {
sendMessage(player, "You place the scarecrow in the $patchName.")
} else {
sendMessage(player, "You plant $itemAmount $itemName in the $patchName.")
}
player.unlock() player.unlock()
return true return true
} }
@ -236,7 +300,29 @@ class UseWithPatchHandler : InteractionListener {
for (p in Plantable.values()) { for (p in Plantable.values()) {
allowedNodes.add(p.itemID) allowedNodes.add(p.itemID)
} }
allowedNodes.addAll(arrayListOf(RAKE,SEED_DIBBER,SPADE,SECATEURS,MAGIC_SECATEURS,TROWEL,Items.SUPERCOMPOST_6034,Items.COMPOST_6032,Items.PLANT_CURE_6036,Items.WATERING_CAN1_5333,Items.WATERING_CAN2_5334,Items.WATERING_CAN3_5335,Items.WATERING_CAN4_5336,Items.WATERING_CAN5_5337,Items.WATERING_CAN6_5338,Items.WATERING_CAN7_5339,Items.WATERING_CAN8_5340, Items.PLANT_POT_5350)) allowedNodes.addAll(
arrayListOf(
RAKE,
SEED_DIBBER,
SPADE,
SECATEURS,
MAGIC_SECATEURS,
TROWEL,
Items.SUPERCOMPOST_6034,
Items.COMPOST_6032,
Items.PLANT_CURE_6036,
Items.WATERING_CAN_5331,
Items.WATERING_CAN1_5333,
Items.WATERING_CAN2_5334,
Items.WATERING_CAN3_5335,
Items.WATERING_CAN4_5336,
Items.WATERING_CAN5_5337,
Items.WATERING_CAN6_5338,
Items.WATERING_CAN7_5339,
Items.WATERING_CAN8_5340,
Items.PLANT_POT_5350
)
)
} }
private fun Int.getNext(): Int { private fun Int.getNext(): Int {

View file

@ -1,6 +1,5 @@
package content.global.skill.farming.timers package content.global.skill.farming.timers
import core.api.*
import core.tools.* import core.tools.*
import core.game.system.timer.* import core.game.system.timer.*
import org.json.simple.* import org.json.simple.*
@ -58,12 +57,12 @@ class CropGrowth : PersistTimer (500, "farming:crops", isSoft = true) {
private fun runOfflineCatchupLogic() { private fun runOfflineCatchupLogic() {
for ((_, patch) in patchMap) { for ((_, patch) in patchMap) {
val type = patch.patch.type val type = patch.patch.type
val shouldPlayCatchup = !patch.isGrown() || (type == PatchType.BUSH && patch.getFruitOrBerryCount() < 4) || (type == PatchType.FRUIT_TREE && patch.getFruitOrBerryCount() < 6) val shouldPlayCatchup = !patch.isGrown() || (type == PatchType.BUSH_PATCH && patch.getFruitOrBerryCount() < 4) || (type == PatchType.FRUIT_TREE_PATCH && patch.getFruitOrBerryCount() < 6)
if(shouldPlayCatchup && patch.plantable != null && !patch.isDead){ if(shouldPlayCatchup && patch.plantable != null && !patch.isDead){
var stagesToSimulate = if (!patch.isGrown()) patch.plantable!!.stages - patch.currentGrowthStage else 0 var stagesToSimulate = if (!patch.isGrown()) patch.plantable!!.stages - patch.currentGrowthStage else 0
if (type == PatchType.BUSH) if (type == PatchType.BUSH_PATCH)
stagesToSimulate += Math.min(4, 4 - patch.getFruitOrBerryCount()) stagesToSimulate += Math.min(4, 4 - patch.getFruitOrBerryCount())
if (type == PatchType.FRUIT_TREE) if (type == PatchType.FRUIT_TREE_PATCH)
stagesToSimulate += Math.min(6, 6 - patch.getFruitOrBerryCount()) stagesToSimulate += Math.min(6, 6 - patch.getFruitOrBerryCount())
val nowTime = System.currentTimeMillis() val nowTime = System.currentTimeMillis()

View file

@ -328,7 +328,7 @@ class LunarListeners : SpellListener("lunar"), Commands {
return return
} }
val patch = fPatch.getPatchFor(player) val patch = fPatch.getPatchFor(player)
if(!patch.isDiseased && !patch.isWeedy()){ if(!patch.isDiseased && !patch.isWeedy() && !patch.isEmptyAndWeeded()){
sendMessage(player, "It is growing just fine.") sendMessage(player, "It is growing just fine.")
return return
} }
@ -642,7 +642,7 @@ class LunarListeners : SpellListener("lunar"), Commands {
animate(player, Animations.LUNAR_SPELLBOOK_FERTILE_SOIL_4413) animate(player, Animations.LUNAR_SPELLBOOK_FERTILE_SOIL_4413)
sendGraphics(Graphics.LUNAR_SPELLBOOK_FERTILE_SOIL_724, target.location) sendGraphics(Graphics.LUNAR_SPELLBOOK_FERTILE_SOIL_724, target.location)
playGlobalAudio(target.location, Sounds.LUNAR_FERTILIZE_2891) playGlobalAudio(target.location, Sounds.LUNAR_FERTILIZE_2891)
patch.compost = CompostType.SUPER patch.compost = CompostType.SUPERCOMPOST
sendMessage(player, "You fertilize the soil.") sendMessage(player, "You fertilize the soil.")
addXP(player, 87.0) addXP(player, 87.0)
} }

View file

@ -16,7 +16,6 @@ import core.ServerStore.Companion.getInt
import core.api.* import core.api.*
import core.cache.def.impl.ItemDefinition import core.cache.def.impl.ItemDefinition
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.Sounds
enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? = null) { enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? = null) {
BAREFISTED_SMITHING("cape_perks:barefisted-smithing"), BAREFISTED_SMITHING("cape_perks:barefisted-smithing"),
@ -59,7 +58,7 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)?
val possibleSeeds = Plantable.values() val possibleSeeds = Plantable.values()
for(i in 0 until 10){ for(i in 0 until 10){
var seed = possibleSeeds.random() var seed = possibleSeeds.random()
while(seed == Plantable.SCARECROW || seed.applicablePatch == PatchType.FRUIT_TREE || seed.applicablePatch == PatchType.TREE || seed.applicablePatch == PatchType.SPIRIT_TREE){ while(seed == Plantable.SCARECROW || seed.applicablePatch == PatchType.FRUIT_TREE_PATCH || seed.applicablePatch == PatchType.TREE_PATCH || seed.applicablePatch == PatchType.SPIRIT_TREE_PATCH){
seed = possibleSeeds.random() seed = possibleSeeds.random()
} }
val reward = core.game.node.item.Item(seed.itemID) val reward = core.game.node.item.Item(seed.itemID)

View file

@ -77,10 +77,10 @@ public class GiantEntNPC extends Forager {
public void modifyFarmingReward(FarmingPatch fPatch, Item reward) { public void modifyFarmingReward(FarmingPatch fPatch, Item reward) {
PatchType patchType = fPatch.getType(); PatchType patchType = fPatch.getType();
if(patchType == PatchType.FRUIT_TREE || if(patchType == PatchType.FRUIT_TREE_PATCH ||
patchType == PatchType.BUSH || patchType == PatchType.BUSH_PATCH ||
patchType == PatchType.BELLADONNA || patchType == PatchType.BELLADONNA_PATCH ||
patchType == PatchType.CACTUS) { patchType == PatchType.CACTUS_PATCH) {
if(RandomFunction.roll(2)) { if(RandomFunction.roll(2)) {
reward.setAmount(2 * reward.getAmount()); reward.setAmount(2 * reward.getAmount());
} }

View file

@ -767,10 +767,15 @@ fun emote(entity: Entity, emote: Emotes) {
/** /**
* Sends a message to the given player. * Sends a message to the given player.
*
* The message will be split on word boundaries into multiple lines so
* that none of the lines will overflow the player's message box.
*
* @param player the player to send the message to. * @param player the player to send the message to.
* @param message the message to send.
*/ */
fun sendMessage(player: Player, message: String) { fun sendMessage(player: Player, message: String) {
player.sendMessage(message) player.sendMessages(*splitLines(message, 86))
} }
/** /**

View file

@ -2,12 +2,20 @@ package core.api
import java.util.* import java.util.*
import kotlin.math.ceil import kotlin.math.ceil
object DialUtils {
val tagRegex = "<([A-Za-z0-9=/]+)>".toRegex()
fun removeMatches(message: String, regex: Regex): String {
return regex.replace(message, "")
}
}
/** /**
* Automatically split a single continuous string into multiple comma-separated lines. * Automatically split a single continuous string into multiple comma-separated lines.
* Should this not work out for any reason, you should fallback to standard npc and player methods for dialogue. * Should this not work out for any reason, you should fallback to standard npc and player methods for dialogue.
*/ */
fun splitLines(message: String, perLineLimit: Int = 54): Array<String> { fun splitLines(message: String, perLineLimit: Int = 54): Array<String> {
var lines = Array(ceil(message.length / perLineLimit.toFloat()).toInt()) {""} var lines = Array(ceil(DialUtils.removeMatches(message, DialUtils.tagRegex).length / perLineLimit.toFloat()).toInt()) { "" }
//short circuit when possible because it's cheaper. //short circuit when possible because it's cheaper.
if (lines.size == 1) { if (lines.size == 1) {
@ -20,8 +28,28 @@ fun splitLines(message: String, perLineLimit: Int = 54) : Array<String> {
val line = StringBuilder() val line = StringBuilder()
var accumulator = 0 var accumulator = 0
val openTags = LinkedList<String>()
fun pushLine() { fun pushLine() {
if (line.isEmpty()) return if (line.isEmpty()) return
// find all tags that were opened or closed in the line
for (lineTag in DialUtils.tagRegex.findAll(line)) {
if (lineTag.value.get(1) == '/') {
// closing tag encountered; remove it from the list of open tags
for (openTag in openTags.descendingIterator()) {
val lineTagName = lineTag.value.substring(2, lineTag.value.length - 1)
val openTagName = openTag.substring(1, lineTag.value.length - 2)
if (lineTagName == openTagName) {
openTags.remove(openTag)
break
}
}
} else {
openTags.add(lineTag.value)
}
}
//allow array to be resized - there are specific edgecases where it becomes necessary. (Check the unit test for example) //allow array to be resized - there are specific edgecases where it becomes necessary. (Check the unit test for example)
if (lines.size == index) if (lines.size == index)
lines = lines.plus(line.toString()) lines = lines.plus(line.toString())
@ -30,11 +58,15 @@ fun splitLines(message: String, perLineLimit: Int = 54) : Array<String> {
index++ index++
line.clear() line.clear()
accumulator = 0 accumulator = 0
// if any unclosed tags remain, add them to the beginning of the new line
for (tag in openTags) line.append(tag)
openTags.clear()
} }
while (!tokenQueue.isEmpty()) { while (!tokenQueue.isEmpty()) {
val shouldSpace = line.isNotEmpty() val shouldSpace = DialUtils.removeMatches(line.toString(), DialUtils.tagRegex).isNotEmpty()
accumulator += tokenQueue.peek().length accumulator += DialUtils.removeMatches(tokenQueue.peek(), DialUtils.tagRegex).length
if (shouldSpace) accumulator += 1 if (shouldSpace) accumulator += 1
if (accumulator > perLineLimit) { if (accumulator > perLineLimit) {