Repository reorganisation

Unified kotlin and java into just src/main
Unified the rs09 and core packages
Took all content out of the core package, and placed it into the new content package
Reorganised all source code relating to content to be easier to find and explore
This commit is contained in:
Ceikry
2023-01-26 13:59:28 +00:00
committed by Ryan
parent e4b799c44a
commit a100affda2
3156 changed files with 24205 additions and 30585 deletions
@@ -0,0 +1,51 @@
package content.global.skill.farming
import org.rs09.consts.Items
/**
* I don't want to do it this way but jagex has forced my hand
*/
enum class BasketsAndSacks(val produceID: Int, val baseContainer: Int, val capacity : Int) {
POTATO(Items.POTATO_1942, Items.POTATOES1_5420, 10),
ONION(Items.ONION_1957, Items.ONIONS1_5440, 10),
CABBAGE(Items.CABBAGE_1965, Items.CABBAGES1_5460, 10),
APPLE(Items.COOKING_APPLE_1955, Items.APPLES1_5378, 5),
BANANA(Items.BANANA_1963, Items.BANANAS1_5408, 5),
ORANGE(Items.ORANGE_2108, Items.ORANGES1_5388, 5),
STRAWBERRY(Items.STRAWBERRY_5504, Items.STRAWBERRIES1_5398, 5),
TOMATO(Items.TOMATO_1982, Items.TOMATOES1_5960, 5);
val containers = ArrayList<Int>()
companion object{
private val map = HashMap<Int,BasketsAndSacks>()
init {
values().map { it.produceID to it }.toMap(map)
for(b in values()){
b.containers.add(b.baseContainer)
for(i in 1 until b.capacity){
map[b.baseContainer + (i * 2)] = b
b.containers.add(b.baseContainer + (i * 2))
}
}
}
@JvmStatic
fun forId(itemId: Int): BasketsAndSacks?{
return map[itemId]
}
}
fun checkIsLast(containerID: Int): Boolean {
return containerID == containers.last()
}
fun checkIsFirst(containerID: Int): Boolean {
return containerID == containers.first()
}
fun checkWhich(containerID: Int): Int{
return containers.indexOf(containerID)
}
}
@@ -0,0 +1,179 @@
package content.global.skill.farming
import core.api.*
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.item.Item
import core.tools.RandomFunction
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import org.rs09.consts.Items
import java.util.concurrent.TimeUnit
class CompostBin(val player: Player, val bin: CompostBins) {
private var items = ArrayList<Int>()
var isSuperCompost = true
var isTomatoes = true
var isClosed = false
var finishedTime = 0L
var isFinished = false
fun isFull() : Boolean {
return items.size == 15
}
fun close() {
isClosed = true
player.audioManager.send(2428)
finishedTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(RandomFunction.random(35,50).toLong())
updateBit()
}
fun open(){
isClosed = false
player.audioManager.send(2429)
updateBit()
}
fun takeItem(): Item?{
if(items.isEmpty()) return null
val item = items[0]
items.remove(item)
if(items.isEmpty()){
isFinished = false
finishedTime = 0L
isTomatoes = true
isSuperCompost = true
isClosed = false
}
player.audioManager.send(Audio (2443, 1, 1))
updateBit()
if(isSuperCompost) rewardXP(player, Skills.FARMING, 8.5)
else rewardXP(player, Skills.FARMING, 4.5)
return Item(item)
}
fun isReady(): Boolean {
return System.currentTimeMillis() > finishedTime && finishedTime != 0L
}
fun checkSuperCompostItem(id: Int): Boolean{
return when (id){
Items.WATERMELON_5982,
Items.PINEAPPLE_2114,
Items.CALQUAT_FRUIT_5980,
Items.OAK_ROOTS_6043,
Items.WILLOW_ROOTS_6045,
Items.MAPLE_ROOTS_6047,
Items.YEW_ROOTS_6049,
Items.MAGIC_ROOTS_6051,
Items.COCONUT_5974,
Items.COCONUT_SHELL_5978,
Items.PAPAYA_FRUIT_5972,
Items.JANGERBERRIES_247,
Items.WHITE_BERRIES_239,
Items.POISON_IVY_BERRIES_6018,
Items.CLEAN_TOADFLAX_2998,
Items.CLEAN_AVANTOE_261,
Items.CLEAN_KWUARM_263,
Items.CLEAN_CADANTINE_265,
Items.CLEAN_DWARF_WEED_267,
Items.CLEAN_TORSTOL_269,
Items.CLEAN_LANTADYME_2481,
Items.CLEAN_SNAPDRAGON_3000,
Items.GRIMY_TOADFLAX_3049,
Items.GRIMY_KWUARM_213,
Items.GRIMY_AVANTOE_211,
Items.GRIMY_TORSTOL_219,
Items.GRIMY_DWARF_WEED_217,
Items.GRIMY_LANTADYME_2485,
Items.GRIMY_SNAPDRAGON_3051,
Items.GRIMY_CADANTINE_215 -> true
else -> false
}
}
fun addItem(item: Int){
if(!isFull()) {
items.add(item)
if (!checkSuperCompostItem(item)) {
isSuperCompost = false
}
if(item != Items.TOMATO_1982) isTomatoes = false
}
updateBit()
}
fun addItem(item: Item){
val remaining = 15 - items.size
val amount = if(item.amount > remaining){
remaining
} else item.amount
for(i in 0 until amount) {
player.audioManager.send(2441)
addItem(item.id)
}
}
fun updateBit(){
player.varpManager.get(bin.varpIndex).clearBitRange(bin.varpOffest,bin.varpOffest + 7)
if(items.isNotEmpty()) {
if (isFinished) {
player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest + 1, if(items.size == 15) 15 else 14)
if (isTomatoes) player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest + 7, 1)
else if (isSuperCompost) player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest + 5, 1)
} else {
player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest, items.size)
if (isTomatoes) player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest + 7, 1)
}
if (isClosed) player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest + 6, 1)
}
player.varpManager.get(bin.varpIndex).send(player)
}
fun save(root: JSONObject){
val binObject = JSONObject()
binObject.put("isSuper",this.isSuperCompost)
val items = JSONArray()
for(id in this.items){
items.add(id)
}
binObject.put("items",items)
binObject.put("finishTime",finishedTime)
binObject.put("isTomato",isTomatoes)
binObject.put("isClosed",isClosed)
binObject.put("isFinished",isFinished)
root.put("binData",binObject)
}
fun parse(_data: JSONObject){
val isSuper = if(_data.containsKey("isSuper")) (_data["isSuper"] as Boolean) else true
if(_data.containsKey("items")){
(_data["items"] as JSONArray).forEach {
addItem(it.toString().toInt())
}
}
if(_data.containsKey("finishTime")) finishedTime = _data["finishTime"].toString().toLong()
if(_data.containsKey("isTomato")) isTomatoes = _data["isTomato"] as Boolean
if(_data.containsKey("isClosed")) isClosed = _data["isClosed"] as Boolean
if(_data.containsKey("isFinished")) isFinished = _data["isFinished"] as Boolean
updateBit()
}
fun finish(){
if(isTomatoes) items = items.map { Items.ROTTEN_TOMATO_2518 } as ArrayList<Int>
else if(isSuperCompost) items = items.map { Items.SUPERCOMPOST_6034 } as ArrayList<Int>
else items = items.map { Items.COMPOST_6032 } as ArrayList<Int>
isFinished = true
}
fun convert(){
if(!isSuperCompost){
items = items.map { Items.SUPERCOMPOST_6034 } as ArrayList<Int>
isSuperCompost = true
updateBit()
}
}
}
@@ -0,0 +1,49 @@
package content.global.skill.farming
import core.cache.def.impl.SceneryDefinition
import core.game.interaction.OptionHandler
import core.game.node.Node
import core.game.node.entity.player.Player
import core.plugin.Initializable
import core.plugin.Plugin
@Initializable
class CompostBinOptionHandler : OptionHandler() {
override fun newInstance(arg: Any?): Plugin<Any> {
for(i in 7836..7839)
SceneryDefinition.forId(i).childrenIds.forEach {
val def = SceneryDefinition.forId(it)
def.handlers["option:open"] = this
def.handlers["option:close"] = this
def.handlers["option:take-tomato"] = this
}
return this
}
override fun handle(player: Player?, node: Node?, option: String?): Boolean {
player ?: return false
node ?: return false
val cBin = CompostBins.forObject(node.asScenery()) ?: return false
val bin = cBin.getBinForPlayer(player)
when(option){
"close" -> if(!bin.isFull()) player.sendMessage("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()
"take-tomato" -> {
if (!bin.isTomatoes || !bin.isFinished) {
player.sendMessage("This shouldn't be happening. Report this.")
} else {
if(player.inventory.isFull){
player.sendMessage("You don't have enough inventory space to do this.")
} else {
val reward = bin.takeItem()
if (reward != null)
player.inventory.add(reward)
}
}
}
}
return true
}
}
@@ -0,0 +1,39 @@
package content.global.skill.farming
import core.cache.def.impl.SceneryDefinition
import core.cache.def.impl.VarbitDefinition
import core.game.node.scenery.Scenery
import core.game.node.entity.player.Player
enum class CompostBins(val varpIndex: Int, val varpOffest: Int) {
FALADOR_COMPOST(511,0),
CATHERBY_COMPOST(511,8),
PORT_PHAS_COMPOST(511,16),
ARDOUGNE_COMPOST(511,24);
companion object {
@JvmField
val bins = values().map { (it.varpIndex shl it.varpOffest) to it }.toMap()
@JvmStatic
fun forObject(obj: Scenery): CompostBins?{
return forObjectID(obj.id)
}
@JvmStatic
fun forObjectID(id: Int): CompostBins?{
val objDef = SceneryDefinition.forId(id)
val def = VarbitDefinition.forObjectID(objDef.varbitID)
return bins[def.varpId shl def.startBit]
}
}
fun getBinForPlayer(player: Player) : CompostBin {
var state: FarmingState? = player.states.get("farming") as FarmingState?
return if(state == null){
state = player.registerState("farming") as FarmingState
state.getBin(this).also { state.init() }
} else
state.getBin(this)
}
}
@@ -0,0 +1,7 @@
package content.global.skill.farming
enum class CompostType {
NONE,
NORMAL,
SUPER
}
@@ -0,0 +1,121 @@
package content.global.skill.farming
import core.api.*
import core.cache.def.impl.SceneryDefinition
import core.game.interaction.OptionHandler
import core.game.node.Node
import content.global.skill.summoning.familiar.GiantEntNPC
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.item.Item
import core.game.system.task.Pulse
import core.game.world.update.flag.context.Animation
import core.plugin.Initializable
import core.plugin.Plugin
import org.rs09.consts.Items
val livesBased = arrayOf(PatchType.HERB, PatchType.CACTUS, PatchType.BELLADONNA, PatchType.HOPS, PatchType.ALLOTMENT,PatchType.EVIL_TURNIP)
val spadeAnim = Animation(830)
@Initializable
class CropHarvester : OptionHandler() {
override fun newInstance(arg: Any?): Plugin<Any> {
SceneryDefinition.setOptionHandler("harvest",this)
SceneryDefinition.setOptionHandler("pick",this)
return this
}
companion object {
@JvmStatic
fun harvestPulse(player: Player?, node: Node?, crop: Int): Pulse? {
player ?: return null
node ?: return null
val fPatch = FarmingPatch.forObject(node.asScenery())
fPatch ?: return null
val patch = fPatch.getPatchFor(player)
val plantable = patch.plantable
plantable ?: return null
return object : Pulse(0) {
override fun pulse(): Boolean {
var reward = Item(crop, 1)
val familiar = player.familiarManager.familiar
if(familiar != null && familiar is GiantEntNPC) {
familiar.modifyFarmingReward(fPatch, reward)
}
if(!player.inventory.hasSpaceFor(reward)){
player.sendMessage("You don't have enough inventory space for that.")
return true
}
var requiredItem = when(fPatch.type){
PatchType.HERB -> Items.SECATEURS_5329
else -> Items.SPADE_952
}
if(requiredItem == Items.SECATEURS_5329){
if(player.inventory.contains(Items.MAGIC_SECATEURS_7409,1)){
requiredItem = Items.MAGIC_SECATEURS_7409
}
}
val anim = when(requiredItem){
Items.SPADE_952 -> Animation(830)
Items.SECATEURS_5329 -> Animation(7227)
Items.MAGIC_SECATEURS_7409 -> Animation(7228)
else -> Animation(0)
}
val sound = when(requiredItem){
Items.SPADE_952 -> Audio(1470, 1, 1)
Items.SECATEURS_5329 -> Audio(2437, 1, 1)
Items.MAGIC_SECATEURS_7409 -> Audio(2437, 1, 1)
else -> Audio(0)
}
if(!player.inventory.containsItem(Item(requiredItem))){
player.sendMessage("You lack the needed tool to harvest these crops.")
return true
}
player.animator.animate(anim)
player.audioManager.send(sound)
delay = 2
player.inventory.add(reward)
player.skills.addExperience(Skills.FARMING,plantable.harvestXP)
if(patch.patch.type in livesBased){
patch.rollLivesDecrement(
getDynLevel(player, Skills.FARMING),
requiredItem == Items.MAGIC_SECATEURS_7409
)
} else {
patch.harvestAmt--
if(patch.harvestAmt <= 0 && crop == plantable.harvestItem){
patch.clear()
}
}
return patch.cropLives <= 0 || patch.harvestAmt <= 0
}
}
}
}
override fun handle(player: Player?, node: Node?, option: String?): Boolean {
player ?: return false
node ?: return false
val fPatch = FarmingPatch.forObject(node.asScenery())
fPatch ?: return false
val patch = fPatch.getPatchFor(player)
val plantable = patch.plantable
plantable ?: return false
if(patch.isWeedy()){
player.sendMessage("Something seems to have gone wrong here. Report this.")
return true
}
val pulse = harvestPulse(player, node, plantable.harvestItem) ?: return false
player.pulseManager.run(pulse)
return true
}
}
@@ -0,0 +1,65 @@
package content.global.skill.farming
import core.game.dialogue.DialoguePlugin
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
@Initializable
class DigUpPatchDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) {
var patch: Patch? = null
override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin {
return DigUpPatchDialogue(player)
}
override fun open(vararg args: Any?): Boolean {
patch = args[0] as Patch
if(patch?.isWeedy() == true){
player.dialogueInterpreter.sendDialogue("Use a rake to get rid of weeds.")
stage = 1000
return true
}
player.dialogueInterpreter.sendOptions("Dig up this patch?","Yes","No")
stage = 0
return true
}
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
when(stage){
0 -> when(buttonId){
1 -> {
end()
player.animator.animate(Animation(830))
player.audioManager.send(Audio(1470, 1, 1))
player.pulseManager.run(object : Pulse(){
override fun pulse(): Boolean {
if(patch?.patch?.type == PatchType.TREE){
if(patch?.getCurrentState() == (patch?.plantable?.value ?: 0) + (patch?.plantable?.stages ?: 0) + 2 && patch?.isWeedy() != true){
if(!player.inventory.add(Item(patch?.plantable?.harvestItem ?: 0))){
GroundItemManager.create(Item(patch?.plantable?.harvestItem ?: 0),player)
}
}
}
patch?.clear()
return true
}
})
}
else -> end()
}
1000 -> end()
}
return true
}
override fun getIds(): IntArray {
return intArrayOf(67984003)
}
}
@@ -0,0 +1,68 @@
package content.global.skill.farming
import core.game.node.item.Item
import org.rs09.consts.Items
import core.game.dialogue.DialogueFile
import core.tools.END_DIALOGUE
import core.tools.START_DIALOGUE
class FarmerPayOptionDialogue(val patch: Patch): DialogueFile() {
var item: Item? = null
override fun handle(componentID: Int, buttonID: Int) {
when(stage){
START_DIALOGUE -> {
item = patch.plantable?.protectionItem
val protectionText = when(item?.id){
Items.COMPOST_6032 -> if(item?.amount == 1) "bucket of compost" else "buckets of compost"
Items.POTATOES10_5438 -> if(item?.amount == 1) "sack of potatoes" else "sacks of potatoes"
Items.ONIONS10_5458 -> if(item?.amount == 1) "sack of onions" else "sacks of onions"
Items.CABBAGES10_5478 -> if(item?.amount == 1) "sack of cabbages" else "sacks of cabbages"
Items.JUTE_FIBRE_5931 -> "jute fibres"
Items.APPLES5_5386 -> if(item?.amount == 1) "basket of apples" else "baskets of apples"
Items.MARIGOLDS_6010 -> "harvest of marigold"
Items.TOMATOES5_5968 -> if(item?.amount == 1) "basket of tomatoes" else "baskets of tomatoes"
Items.ORANGES5_5396 -> if(item?.amount == 1) "basket of oranges" else "baskets of oranges"
Items.COCONUT_5974 -> "coconuts"
Items.CACTUS_SPINE_6016 -> "cactus spines"
Items.STRAWBERRIES5_5406 -> if(item?.amount == 1) "basket of strawberries" else "baskets of strawberries"
Items.BANANAS5_5416 -> if(item?.amount == 1) "basket of bananas" else "baskets of bananas"
else -> item?.name?.toLowerCase()
}
if(item == null) npc("Sorry, I won't protect that.").also { stage = END_DIALOGUE }
else{
npc("I would like ${item?.amount} $protectionText","to protect that patch.")
stage++
}
}
1 -> options("Sure!","No, thanks.").also { stage++ }
2 -> {
if(player!!.inventory.containsItem(item)){
player("Here you go.").also { stage = 10 }
} else {
item = Item(item!!.noteChange,item!!.amount)
if(player!!.inventory.containsItem(item)){
player("Here you go.").also { stage = 10 }
} else {
player("I don't have that to give.").also { stage = 20 }
}
}
}
10 -> {
if(player!!.inventory.remove(item)){
npc("Thank you! I'll keep an eye on this patch.").also { stage = END_DIALOGUE }
patch?.protectionPaid = true
} else {
npc("That stuff just... vanished....").also { stage = END_DIALOGUE }
}
}
20 -> {
npc("Come back when you do.")
stage = END_DIALOGUE
}
}
}
}
@@ -0,0 +1,42 @@
package content.global.skill.farming
import core.game.node.Node
import core.game.node.entity.player.Player
import core.game.interaction.InteractionListener
import core.game.interaction.IntType
class FarmerPayOptionHandler : InteractionListener {
override fun defineListeners() {
on(IntType.NPC,"pay","pay (north)","pay (north-west)"){ player, node ->
return@on attemptPay(player,node,0)
}
on(IntType.NPC,"pay (south)","pay (south-east)"){ player, node ->
return@on attemptPay(player,node,1)
}
}
fun attemptPay(player: Player, node: Node, index: Int): Boolean{
val farmer = Farmers.forId(node.id) ?: return false
val patch = farmer.patches[index].getPatchFor(player)
if(patch.plantable == null){
player.dialogueInterpreter.sendDialogue("I have nothing to protect in that patch.")
return true
}
if(patch.protectionPaid){
player.dialogueInterpreter.sendDialogue("I have already paid to protect that patch.")
return true
}
if(patch.isGrown()){
player.dialogueInterpreter.sendDialogue("This patch is already fully grown!")
return true
}
player.dialogueInterpreter.open(FarmerPayOptionDialogue(patch),node.asNpc())
return true
}
}
@@ -0,0 +1,35 @@
package content.global.skill.farming
enum class Farmers(val id: Int, val patches: Array<FarmingPatch>) {
LYRA(2326, arrayOf(FarmingPatch.PORT_PHAS_ALLOTMENT_NW,FarmingPatch.PORT_PHAS_ALLOTMENT_SE)),
ELSTAN(2323, arrayOf(FarmingPatch.S_FALADOR_ALLOTMENT_NW,FarmingPatch.S_FALADOR_ALLOTMENT_SE)),
HESKEL(2340, arrayOf(FarmingPatch.N_FALADOR_TREE)),
ALAIN(2339, arrayOf(FarmingPatch.TAVERLY_TREE)),
DANTAERA(2324, arrayOf(FarmingPatch.CATHERBY_ALLOTMENT_N,FarmingPatch.CATHERBY_ALLOTMENT_S)),
ELLENA(2331, arrayOf(FarmingPatch.CATHERBY_FRUIT_TREE)),
GARTH(2330,arrayOf(FarmingPatch.BRIMHAVEN_FRUIT_TREE)),
GILETH(2344,arrayOf(FarmingPatch.TREE_GNOME_VILLAGE_FRUIT_TREE)),
AMAETHWR(2860,arrayOf(FarmingPatch.LLETYA_FRUIT_TREE)),
SELENA(2332, arrayOf(FarmingPatch.YANILLE_HOPS)),
KRAGEN(2325, arrayOf(FarmingPatch.ARDOUGNE_ALLOTMENT_N,FarmingPatch.ARDOUGNE_ALLOTMENT_S)),
BOLONGO(2343, arrayOf(FarmingPatch.GNOME_STRONGHOLD_FRUIT_TREE)),
PRISSY_SCILLA(1037, arrayOf(FarmingPatch.GNOME_STRONGHOLD_TREE)),
FAYETH(2342, arrayOf(FarmingPatch.LUMBRIDGE_TREE)),
TREZNOR(2341, arrayOf(FarmingPatch.VARROCK_TREE)),
VASQUEN(2333, arrayOf(FarmingPatch.LUMBRIDGE_HOPS)),
RHONEN(2334, arrayOf(FarmingPatch.MCGRUBOR_HOPS)),
FRANCIS(2327, arrayOf(FarmingPatch.ENTRANA_HOPS)),
DREVEN(2335, arrayOf(FarmingPatch.CHAMPIONS_GUILD_BUSH)),
TARIA(2336, arrayOf(FarmingPatch.RIMMINGTON_BUSH)),
TORRELL(2338, arrayOf(FarmingPatch.ARDOUGNE_BUSH));
companion object{
@JvmField
val farmers = values().map { it.id to it }.toMap()
@JvmStatic
fun forId(id: Int): Farmers?{
return farmers[id]
}
}
}
@@ -0,0 +1,97 @@
package content.global.skill.farming
import core.cache.def.impl.SceneryDefinition
import core.cache.def.impl.VarbitDefinition
import core.game.node.scenery.Scenery
import core.game.node.entity.player.Player
enum class FarmingPatch(val varpIndex: Int, val varpOffset: Int, val type: PatchType) {
//Allotments
S_FALADOR_ALLOTMENT_NW(504,0,PatchType.ALLOTMENT),
S_FALADOR_ALLOTMENT_SE(504,8,PatchType.ALLOTMENT),
CATHERBY_ALLOTMENT_N(504,16,PatchType.ALLOTMENT),
CATHERBY_ALLOTMENT_S(504,24,PatchType.ALLOTMENT),
ARDOUGNE_ALLOTMENT_S(505,8,PatchType.ALLOTMENT),
ARDOUGNE_ALLOTMENT_N(505,0,PatchType.ALLOTMENT),
PORT_PHAS_ALLOTMENT_NW(505,16,PatchType.ALLOTMENT),
PORT_PHAS_ALLOTMENT_SE(505,24,PatchType.ALLOTMENT),
HARMONY_ISLAND_ALLOTMENT(982,8,PatchType.ALLOTMENT),
//Herb
CATHERBY_HERB_CE(515,8,PatchType.HERB),
S_FALADOR_HERB_NE(515,0,PatchType.HERB),
ARDOUGNE_HERB_CE(515,16,PatchType.HERB),
PORT_PHAS_HERB_NE(515,24,PatchType.HERB),
TROLL_STRONGHOLD_HERB(830,0,PatchType.HERB),
//Flower
S_FALADOR_FLOWER_C(508,0,PatchType.FLOWER),
CATHERBY_FLOWER_C(508,8,PatchType.FLOWER),
ARDOUGNE_FLOWER_C(508,16,PatchType.FLOWER),
PORT_PHAS_FLOWER_C(508,24,PatchType.FLOWER),
WILDERNESS_FLOWER(1183,12,PatchType.FLOWER),
//Tree
N_FALADOR_TREE(502,8,PatchType.TREE),
TAVERLY_TREE(502,0,PatchType.TREE),
GNOME_STRONGHOLD_TREE(830,11,PatchType.TREE),
LUMBRIDGE_TREE(502,24,PatchType.TREE),
VARROCK_TREE(502,16,PatchType.TREE),
//Fruit Tree
GNOME_STRONGHOLD_FRUIT_TREE(503,0,PatchType.FRUIT_TREE),
CATHERBY_FRUIT_TREE(503,24,PatchType.FRUIT_TREE),
TREE_GNOME_VILLAGE_FRUIT_TREE(503,8,PatchType.FRUIT_TREE),
BRIMHAVEN_FRUIT_TREE(503,16,PatchType.FRUIT_TREE),
LLETYA_FRUIT_TREE(830,23,PatchType.FRUIT_TREE),
//Hops
ENTRANA_HOPS(506,8,PatchType.HOPS),
LUMBRIDGE_HOPS(506,16,PatchType.HOPS),
MCGRUBOR_HOPS(506,24,PatchType.HOPS),
YANILLE_HOPS(506,0,PatchType.HOPS),
//Bushes
CHAMPIONS_GUILD_BUSH(509,0,PatchType.BUSH),
RIMMINGTON_BUSH(509,8,PatchType.BUSH),
ARDOUGNE_BUSH(509,24,PatchType.BUSH),
ETCETERIA_BUSH(509,16,PatchType.BUSH),
//Spirit Tree
ETCETERIA_SPIRIT_TREE(507,8,PatchType.SPIRIT_TREE),
PORT_SARIM_SPIRIT_TREE(507,0,PatchType.SPIRIT_TREE),
KARAMJA_SPIRIT_TREE(507,16,PatchType.SPIRIT_TREE),
//Other
DRAYNOR_BELLADONNA(512, 16, PatchType.BELLADONNA),
CANIFIS_MUSHROOM(512, 8, PatchType.MUSHROOM),
ALKHARID_CACTUS(512, 0, PatchType.CACTUS),
EVIL_TURNIP(1171, 7, PatchType.EVIL_TURNIP);
companion object {
@JvmField
val patches = FarmingPatch.values().map { (it.varpIndex shl it.varpOffset) to it }.toMap()
@JvmStatic
fun forObject(obj: Scenery): FarmingPatch?{
return forObjectID(obj.id)
}
@JvmStatic
fun forObjectID(id: Int): FarmingPatch?{
val objDef = SceneryDefinition.forId(id)
val def = VarbitDefinition.forObjectID(objDef.varbitID)
return patches[def.varpId shl def.startBit]
}
}
fun getPatchFor(player: Player): Patch{
var state: FarmingState? = player.states.get("farming") as FarmingState?
return if(state == null){
state = player.registerState("farming") as FarmingState
state.getPatch(this).also { state.init() }
} else
state.getPatch(this)
}
}
@@ -0,0 +1,74 @@
package content.global.skill.farming
import core.api.*
import core.game.dialogue.FacialExpression
import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.world.map.zone.ZoneBorders
import org.rs09.consts.NPCs
import core.game.dialogue.DialogueFile
class FarmingPatchZone : MapArea, TickListener {
private val playersInZone = hashMapOf<Player,Int>()
override fun defineAreaBorders(): Array<ZoneBorders> {
return arrayOf(
getRegionBorders(12083),
getRegionBorders(10548),
ZoneBorders(3594,3521,3608,3532)
)
}
override fun areaEnter(entity: Entity) {
if(entity is Player && playersInZone[entity] == null && getStatLevel(entity, Skills.FARMING) <= 15) {
playersInZone[entity] = 0
}
}
override fun areaLeave(entity: Entity, logout: Boolean) {
if(entity is Player)
playersInZone.remove(entity)
}
override fun tick() {
playersInZone.toList().forEach { (player, ticks) ->
if(ticks == 500){
val npc = NPC(NPCs.GITHAN_7122)
npc.location = player.location
npc.init()
npc.moveStep()
npc.face(player)
openDialogue(player, SpiritDialogue(true), npc)
} else if (ticks == 1000){
val npc = NPC(NPCs.GITHAN_7122)
npc.location = player.location
npc.init()
npc.moveStep()
npc.face(player)
openDialogue(player, SpiritDialogue(false), npc)
playersInZone.remove(player)
}
playersInZone[player] = ticks + 1
}
}
internal class SpiritDialogue(val is5: Boolean) : DialogueFile(){
override fun handle(componentID: Int, buttonID: Int) {
when(stage){
0 -> {
if(is5) npcl(core.game.dialogue.FacialExpression.NEUTRAL, "In case you didn't know, you don't have to stand by your Farming patch. Your crops will grow even if you're not around.").also { stage++ }
else npcl(core.game.dialogue.FacialExpression.NEUTRAL, " Did you know that if your Farming patch has fully grown, it will never catch disease or die? It will be perfectly safe until you choose to harvest it.").also { stage++ }
}
1 -> end()
}
}
override fun end(){
super.end()
poofClear(npc ?: return)
}
}
}
@@ -0,0 +1,172 @@
package content.global.skill.farming
import core.Util.clamp
import core.game.node.entity.player.Player
import core.game.system.task.Pulse
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import core.game.node.entity.state.PlayerState
import core.game.node.entity.state.State
import core.tools.SystemLogger
import java.util.concurrent.TimeUnit
@PlayerState("farming")
class FarmingState(player: Player? = null) : State(player) {
private val patchMap = HashMap<FarmingPatch, Patch>()
private val binMap = HashMap<CompostBins, CompostBin>()
fun getPatch(patch: FarmingPatch): Patch {
return patchMap[patch] ?: (Patch(player!!,patch).also { patchMap[patch] = it })
}
fun getBin(bin: CompostBins) : CompostBin{
return binMap[bin] ?: (CompostBin(player!!,bin).also { binMap[bin] = it })
}
fun getPatches(): MutableCollection<Patch>{
return patchMap.values
}
fun getBins(): MutableCollection<CompostBin>{
return binMap.values
}
override fun save(root: JSONObject) {
val patches = JSONArray()
for((key,patch) in patchMap){
val p = JSONObject()
p.put("patch-ordinal",key.ordinal)
p.put("patch-plantable-ordinal",patch.plantable?.ordinal ?: -1)
p.put("patch-watered",patch.isWatered)
p.put("patch-diseased",patch.isDiseased)
p.put("patch-dead",patch.isDead)
p.put("patch-stage",patch.currentGrowthStage)
p.put("patch-state",patch.getCurrentState())
p.put("patch-nextGrowth",patch.nextGrowth)
p.put("patch-harvestAmt",patch.harvestAmt)
p.put("patch-checkHealth",patch.isCheckHealth)
p.put("patch-compost",patch.compost.ordinal)
p.put("patch-paidprot",patch.protectionPaid)
p.put("patch-croplives", patch.cropLives)
patches.add(p)
}
val bins = JSONArray()
for((key,bin) in binMap){
val b = JSONObject()
b.put("bin-ordinal",key.ordinal)
bin.save(b)
bins.add(b)
}
root.put("farming-patches",patches)
root.put("farming-bins",bins)
}
override fun parse(_data: JSONObject) {
player ?: return
if(_data.containsKey("farming-bins")){
(_data["farming-bins"] as JSONArray).forEach {
val bin = it as JSONObject
val binOrdinal = bin["bin-ordinal"].toString().toInt()
val cBin = CompostBins.values()[binOrdinal]
val b = cBin.getBinForPlayer(player)
b.parse(bin["binData"] as JSONObject)
SystemLogger.logErr(this::class.java, "bin size " + binMap.size)
}
}
if(_data.containsKey("farming-patches")){
val data = _data["farming-patches"] as JSONArray
for(d in data){
val p = d as JSONObject
val patchOrdinal = p["patch-ordinal"].toString().toInt()
val patchPlantableOrdinal = p["patch-plantable-ordinal"].toString().toInt()
val patchWatered = p["patch-watered"] as Boolean
val patchDiseased = p["patch-diseased"] as Boolean
val patchDead = p["patch-dead"] as Boolean
val patchStage = p["patch-stage"].toString().toInt()
val nextGrowth = p["patch-nextGrowth"].toString().toLong()
val harvestAmt = (p["patch-harvestAmt"] ?: 0).toString().toInt()
val checkHealth = p["patch-checkHealth"] as Boolean
val savedState = p["patch-state"].toString().toInt()
val compostOrdinal = p["patch-compost"].toString().toInt()
val protectionPaid = p["patch-paidprot"] as Boolean
val cropLives = if(p["patch-croplives"] != null) p["patch-croplives"].toString().toInt() else 3
val fPatch = FarmingPatch.values()[patchOrdinal]
val plantable = if(patchPlantableOrdinal != -1) Plantable.values()[patchPlantableOrdinal] else null
val patch = Patch(player,fPatch,plantable,patchStage,patchDiseased,patchDead,patchWatered,nextGrowth,harvestAmt,checkHealth)
patch.cropLives = cropLives
patch.compost = CompostType.values()[compostOrdinal]
patch.protectionPaid = protectionPaid
patch.setCurrentState(savedState)
if((savedState - (patch?.plantable?.value ?: 0)) > patch.currentGrowthStage){
patch.setCurrentState(savedState)
} else {
patch.setCurrentState((patch.plantable?.value ?: 0) + patch.currentGrowthStage)
}
val type = patch.patch.type
val shouldPlayCatchup = !patch.isGrown() || (type == PatchType.BUSH && patch.getFruitOrBerryCount() < 4) || (type == PatchType.FRUIT_TREE && patch.getFruitOrBerryCount() < 6)
if(shouldPlayCatchup && patch.plantable != null && !patchDead){
var stagesToSimulate = if (!patch.isGrown()) patch.plantable!!.stages - patch.currentGrowthStage else 0
if (type == PatchType.BUSH)
stagesToSimulate += Math.min(4, 4 - patch.getFruitOrBerryCount())
if (type == PatchType.FRUIT_TREE)
stagesToSimulate += Math.min(6, 6 - patch.getFruitOrBerryCount())
val nowTime = System.currentTimeMillis()
var simulatedTime = patch.nextGrowth
while (simulatedTime < nowTime && stagesToSimulate-- > 0 && !patch.isDead) {
val timeToIncrement = TimeUnit.MINUTES.toMillis(patch.getStageGrowthMinutes().toLong())
patch.update()
simulatedTime += timeToIncrement
}
}
if(patchMap[fPatch] == null) {
patchMap[fPatch] = patch
}
}
}
}
override fun newInstance(player: Player?): State {
return FarmingState(player)
}
override fun createPulse() {
pulse = object : Pulse(3){
override fun pulse(): Boolean {
GlobalScope.launch {
var removeList = ArrayList<FarmingPatch>()
for((_,patch) in patchMap){
if(patch.getCurrentState() in 1..3 && patch.nextGrowth == 0L){
patch.nextGrowth = System.currentTimeMillis() + 60000
continue
}
if(patch.nextGrowth < System.currentTimeMillis() && !patch.isDead){
patch.update()
patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(patch.getStageGrowthMinutes().toLong())
}
}
for((_,bin) in binMap){
if(bin.isReady() && !bin.isFinished){
bin.finish()
}
}
}
return false
}
}
}
}
@@ -0,0 +1,83 @@
package content.global.skill.farming
import core.api.*
import core.cache.def.impl.SceneryDefinition
import core.game.interaction.OptionHandler
import core.game.node.Node
import content.global.skill.summoning.familiar.GiantEntNPC
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
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.Plugin
import java.util.concurrent.TimeUnit
@Initializable
class FruitAndBerryPicker : OptionHandler() {
override fun newInstance(arg: Any?): Plugin<Any> {
SceneryDefinition.setOptionHandler("pick-coconut",this)
SceneryDefinition.setOptionHandler("pick-banana",this)
SceneryDefinition.setOptionHandler("pick-apple",this)
SceneryDefinition.setOptionHandler("pick-orange",this)
SceneryDefinition.setOptionHandler("pick-pineapple",this)
SceneryDefinition.setOptionHandler("pick-papaya",this)
SceneryDefinition.setOptionHandler("pick-leaf",this)
SceneryDefinition.setOptionHandler("pick-from",this)
SceneryDefinition.setOptionHandler("pick-fruit",this)
SceneryDefinition.setOptionHandler("pick-spine",this)
return this
}
override fun handle(player: Player?, node: Node?, option: String?): Boolean {
player ?: return false
node ?: return false
val fPatch = FarmingPatch.forObject(node.asScenery())
fPatch ?: return false
val patch = fPatch.getPatchFor(player)
val plantable = patch.plantable
plantable ?: return false
val animation = Animation(2281)
if(patch.getFruitOrBerryCount() <= 0){
player.sendMessage("This shouldn't be happening. Please report this.")
return true
}
if(!player.inventory.hasSpaceFor(Item(plantable.harvestItem))){
player.sendMessage("You do not have enough inventory space for this.")
return true
}
if(System.currentTimeMillis() - patch.nextGrowth > TimeUnit.MINUTES.toMillis(45)){
patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(45)
}
player.pulseManager.run(object : Pulse(animation.duration){
override fun pulse(): Boolean {
val reward = Item(plantable.harvestItem, 1)
val familiar = player.familiarManager.familiar
if(familiar != null && familiar is GiantEntNPC) {
familiar.modifyFarmingReward(fPatch, reward)
}
player.animator.animate(animation)
player.audioManager.send(2437)
addItemOrDrop(player,reward.id,reward.amount)
player.skills.addExperience(Skills.FARMING,plantable.harvestXP)
patch.setCurrentState(patch.getCurrentState() - 1)
return patch.getFruitOrBerryCount() == 0
}
})
return true
}
}
@@ -0,0 +1,54 @@
package content.global.skill.farming
import core.cache.def.impl.SceneryDefinition
import core.game.interaction.OptionHandler
import core.game.node.Node
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.tools.SystemLogger
import core.plugin.Initializable
import core.plugin.Plugin
import java.util.concurrent.TimeUnit
@Initializable
class HealthChecker : OptionHandler(){
override fun newInstance(arg: Any?): Plugin<Any> {
SceneryDefinition.setOptionHandler("check-health",this)
return this
}
override fun handle(player: Player?, node: Node?, option: String?): Boolean {
player ?: return false
node ?: return false
val fPatch = FarmingPatch.forObject(node.asScenery())
fPatch ?: return false
val patch = fPatch.getPatchFor(player)
val type = patch.patch.type
if(type != PatchType.BUSH && type != PatchType.FRUIT_TREE && type != PatchType.TREE && type != PatchType.CACTUS){
player.sendMessage("This shouldn't be happening. Please report this.")
return true
}
if(!patch.isCheckHealth) return true
player.skills.addExperience(Skills.FARMING,patch.plantable?.checkHealthXP ?: 0.0)
patch.isCheckHealth = false
when(type){
PatchType.TREE -> patch.setCurrentState(patch.getCurrentState() + 1)
PatchType.FRUIT_TREE -> patch.setCurrentState(patch.getCurrentState() - 14)
PatchType.BUSH -> patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 4)
PatchType.CACTUS -> patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 3)
else -> SystemLogger.logErr(this::class.java,"Unreachable patch type from when(type) switch in HealthChecker.kt line 36")
}
if(type == PatchType.FRUIT_TREE){
patch.nextGrowth = TimeUnit.MINUTES.toMillis(45)
}
patch.update()
return true
}
}
@@ -0,0 +1,46 @@
package content.global.skill.farming
import core.cache.def.impl.SceneryDefinition
import core.game.interaction.OptionHandler
import core.game.node.Node
import core.game.node.entity.player.Player
import core.plugin.Initializable
import core.plugin.Plugin
@Initializable
class InspectionHandler : OptionHandler() {
override fun newInstance(arg: Any?): Plugin<Any> {
SceneryDefinition.setOptionHandler("inspect",this)
return this
}
override fun handle(player: Player?, node: Node?, option: String?): Boolean {
node ?: return false
player ?: return false
val patch = FarmingPatch.forObject(node.asScenery())
if(patch == null){
player.sendMessage("This is an improperly handled inspect option. Report this please.")
} else {
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 {
if(p.isDiseased && !p.isDead) "This patch has become diseased."
else if(p.isDead) "The crops in this patch are dead."
else if(p.plantable == Plantable.SCARECROW) "There is a scarecrow in this patch."
else "This patch has something growing in it."
}
val status2 = if(patch.type == PatchType.ALLOTMENT || patch.type == PatchType.FLOWER || patch.type == PatchType.HOPS){
if(p.isWatered) {
"This patch has been watered."
} else if(p.getCurrentState() > 3 && !p.isGrown() && !p.isDead && !p.isDiseased) {
"This patch could use some water."
} else ""
} else ""
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."
player.sendMessage("$status1 $status2")
player.sendMessage(status3)
val varpValue = player.varpManager.get(patch.varpIndex).getBitRangeValue(patch.varpOffset,patch.varpOffset + 7)
}
return true
}
}
@@ -0,0 +1,37 @@
package content.global.skill.farming
import core.api.*
import core.game.node.item.Item
import org.rs09.consts.NPCs
import core.game.interaction.InteractionListener
import core.game.interaction.IntType
class LeprechaunNoter : InteractionListener {
val CROPS = Plantable.values().map{ it.harvestItem }.toIntArray()
val LEPRECHAUNS = intArrayOf(NPCs.TOOL_LEPRECHAUN_3021,NPCs.GOTH_LEPRECHAUN_8000,NPCs.TOOL_LEPRECHAUN_4965,NPCs.TECLYN_2861)
override fun defineListeners() {
onUseWith(IntType.NPC,CROPS,*LEPRECHAUNS){ player, used, with ->
val usedItem = used.asItem()
val npc = with.asNpc()
val expr = when(npc.id){
3021 -> core.game.dialogue.FacialExpression.OLD_NORMAL
else -> core.game.dialogue.FacialExpression.FRIENDLY
}
if(usedItem.noteChange != usedItem.id){
val amt = player.inventory.getAmount(usedItem.id)
if(player.inventory.remove(Item(usedItem.id,amt))){
player.inventory.add(Item(usedItem.noteChange,amt))
}
sendItemDialogue(player,usedItem.id,"The leprechaun exchanges your items for banknotes.")
} else {
// Unsure why the line below no longer functions, despite only changing the line above to be more correct. Using your note(NOT CROP) on the leprechaun no longer functions because of this. - Crash
player.dialogueInterpreter.sendDialogues(npc.id,expr,"That IS a banknote!")
}
return@onUseWith true
}
}
}
@@ -0,0 +1,345 @@
package content.global.skill.farming
import core.game.node.entity.player.Player
import core.tools.RandomFunction
import org.rs09.consts.Items
import core.tools.SystemLogger
import java.util.concurrent.TimeUnit
import kotlin.math.ceil
import kotlin.math.min
class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantable?, var currentGrowthStage: Int, var isDiseased: Boolean, var isDead: Boolean, var isWatered: Boolean, var nextGrowth: Long, var harvestAmt: Int, var isCheckHealth: Boolean) {
constructor(player: Player, patch: FarmingPatch) : this(player,patch,null,0,false,false,false,0L,0,false)
var diseaseMod = 0
var compost = CompostType.NONE
var protectionPaid = false
var cropLives = 3
fun setNewHarvestAmount() {
val compostMod = when(compost) {
CompostType.NONE -> 0
CompostType.NORMAL -> 1
CompostType.SUPER -> 2
}
harvestAmt = when (plantable) {
Plantable.LIMPWURT_SEED, Plantable.WOAD_SEED -> 3
Plantable.MUSHROOM_SPORE -> 6
Plantable.WILLOW_SAPLING -> 0
else -> 1
}
if(plantable != null && plantable?.applicablePatch != PatchType.FLOWER) {
harvestAmt += compostMod
}
cropLives = 3 + compostMod
}
fun rollLivesDecrement(farmingLevel: Int, magicSecateurs: Boolean){
if(patch.type == PatchType.HERB){
//authentic formula thanks to released data.
var herbSaveLow = when(plantable){
Plantable.GUAM_SEED -> min(24 + farmingLevel, 80)
Plantable.MARRENTILL_SEED -> min(28 + farmingLevel, 80)
Plantable.TARROMIN_SEED -> min(31 + farmingLevel, 80)
Plantable.HARRALANDER_SEED -> min(36 + farmingLevel, 80)
Plantable.GOUT_TUBER -> min(39 + farmingLevel, 80)
Plantable.RANARR_SEED -> min(39 + farmingLevel, 80)
Plantable.SPIRIT_WEED_SEED -> min(43 + farmingLevel, 80)
Plantable.TOADFLAX_SEED -> min(43 + farmingLevel, 80)
Plantable.IRIT_SEED -> min(46 + farmingLevel, 80)
Plantable.AVANTOE_SEED -> min(50 + farmingLevel, 80)
Plantable.KWUARM_SEED -> min(54 + farmingLevel, 80)
Plantable.SNAPDRAGON_SEED -> min(57 + farmingLevel, 80)
Plantable.CADANTINE_SEED -> min(60 + farmingLevel, 80)
Plantable.LANTADYME_SEED -> min(64 + farmingLevel, 80)
Plantable.DWARF_WEED_SEED -> min(67 + farmingLevel, 80)
Plantable.TORSTOL_SEED -> min(71 + farmingLevel, 80)
else -> -1
}
if(magicSecateurs) herbSaveLow = ceil(1.10 * herbSaveLow).toInt()
val rand = RandomFunction.random(256)
if(rand > herbSaveLow){
cropLives -= 1
}
} else {
//inauthentic formulae based on reported averages due to lack of formula
var chance = when(patch.type){
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.BELLADONNA -> 2 //average of 2 per life times 3 lives = 6
PatchType.EVIL_TURNIP -> 2 //average 2 per, same as BELLADONNA
PatchType.CACTUS -> 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
}
if(magicSecateurs) chance += ceil(1.10 * chance).toInt() //will increase average yield by roughly 3.
if(RandomFunction.roll(chance)) cropLives -= 1
}
if(cropLives <= 0) clear()
}
fun isWeedy(): Boolean {
return getCurrentState() in 0..3
}
fun getCurrentState(): Int{
return player.varpManager.get(patch.varpIndex).getVarbitValue(patch.varpOffset) ?: 0
}
fun setCurrentState(state: Int){
player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,state)
updateBit()
}
fun isFertilized(): Boolean {
return compost != CompostType.NONE
}
fun isGrown(): Boolean{
return currentGrowthStage == (plantable?.stages ?: 0)
}
private fun updateBit(){
if(isCheckHealth){
when(patch.type){
PatchType.FRUIT_TREE -> player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,plantable!!.value + plantable!!.stages + 20)
PatchType.BUSH -> player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,250 + (plantable!!.ordinal - Plantable.REDBERRY_SEED.ordinal))
PatchType.CACTUS -> player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, 31)
else -> SystemLogger.logWarn(this::class.java, "Invalid setting of isCheckHealth for patch type: " + patch.type.name)
}
} else {
when(patch.type){
PatchType.ALLOTMENT,PatchType.FLOWER,PatchType.HOPS -> {
player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset + 6, if (isWatered || isDead) 1 else 0)
player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset + 7, if (isDiseased) 1 else 0)
}
PatchType.BUSH -> {
if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,getBushDeathValue())
else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,getBushDiseaseValue())
//else player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, (plantable?.value ?: 0) + currentGrowthStage)
}
PatchType.TREE -> {
if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset + 7,1)
else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset + 6, 1)
}
PatchType.FRUIT_TREE -> {
if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,getFruitTreeDeathValue())
else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getFruitTreeDiseaseValue())
}
PatchType.BELLADONNA -> {
if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getBelladonnaDeathValue())
else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getBelladonnaDiseaseValue())
else player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, (plantable?.value ?: 0) + currentGrowthStage)
}
PatchType.CACTUS -> {
if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getCactusDeathValue())
else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getCactusDiseaseValue())
}
PatchType.HERB -> {
if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getHerbDeathValue())
else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getHerbDiseaseValue())
else player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, (plantable?.value ?: 0) + currentGrowthStage)
}
else -> {}
}
}
player.varpManager.get(patch.varpIndex).send(player)
}
fun cureDisease() {
isDiseased = false
when(patch.type){
PatchType.BUSH,PatchType.FRUIT_TREE -> player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,(plantable?.value ?: 0) + currentGrowthStage)
PatchType.TREE -> player.varpManager.get(patch.varpIndex).clearBitRange(patch.varpOffset + 6, patch.varpOffset + 7)
PatchType.CACTUS -> player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, (plantable?.value ?: 0) + currentGrowthStage)
else -> {}
}
updateBit()
}
fun water() {
isWatered = true
updateBit()
}
private fun getBushDiseaseValue(): Int{
if(plantable == Plantable.POISON_IVY_SEED){
return (plantable?.value ?: 0) + currentGrowthStage + 12
} else {
return (plantable?.value ?: 0) + currentGrowthStage + 64
}
}
private fun getBushDeathValue(): Int{
if(plantable == Plantable.POISON_IVY_SEED){
return (plantable?.value ?: 0) + currentGrowthStage + 22
} else {
return (plantable?.value ?: 0) + currentGrowthStage + 126
}
}
private fun getFruitTreeDiseaseValue(): Int {
return (plantable?.value ?: 0) + currentGrowthStage + 12
}
private fun getFruitTreeDeathValue(): Int {
return (plantable?.value ?: 0) + currentGrowthStage + 18
}
private fun getBelladonnaDiseaseValue(): Int {
return (plantable?.value ?: 0) + currentGrowthStage + 4
}
private fun getBelladonnaDeathValue(): Int {
return (plantable?.value ?: 0) + currentGrowthStage + 7
}
private fun getCactusDiseaseValue(): Int {
return (plantable?.value ?: 0) + currentGrowthStage + 10
}
private fun getCactusDeathValue(): Int {
return (plantable?.value ?: 0) + currentGrowthStage + 16
}
private fun getHerbDiseaseValue(): Int {
return if (plantable?.value ?: -1 <= 103) {
128 + (((plantable?.ordinal ?: 0) - Plantable.GUAM_SEED.ordinal) * 3) + currentGrowthStage - 1
} else if (plantable == Plantable.SPIRIT_WEED_SEED) {
211 + currentGrowthStage - 1
} else {
198 + currentGrowthStage -1
}
}
private fun getHerbDeathValue(): Int {
return if(plantable == Plantable.GOUT_TUBER){
201 + currentGrowthStage - 1
} else 170 + currentGrowthStage - 1
}
private fun grow(){
if(isWeedy() && getCurrentState() > 0) {
nextGrowth = System.currentTimeMillis() + 60000
setCurrentState(getCurrentState() - 1)
currentGrowthStage--
return
}
if(isDiseased){
isDead = true
return
}
diseaseMod = when(compost){
CompostType.NONE -> 0
CompostType.NORMAL -> 8
CompostType.SUPER -> 13
}
if(patch != FarmingPatch.TROLL_STRONGHOLD_HERB && RandomFunction.random(128) <= (17 - diseaseMod) && !isWatered && !isGrown() && !protectionPaid && !isFlowerProtected() && patch.type != PatchType.EVIL_TURNIP ){
//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)) {
isDiseased = true
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){
isCheckHealth = true
}
if((patch.type == PatchType.FRUIT_TREE || patch.type == PatchType.BUSH || patch.type == PatchType.CACTUS) && plantable?.stages == currentGrowthStage){
if((patch.type == PatchType.BUSH && getFruitOrBerryCount() < 4) || (patch.type == PatchType.FRUIT_TREE && getFruitOrBerryCount() < 6) || (patch.type == PatchType.CACTUS && getFruitOrBerryCount() < 3)){
setCurrentState(getCurrentState() + 1)
}
}
if(patch.type == PatchType.TREE) {
// Willow branches
if(harvestAmt < 6) {
harvestAmt++;
}
}
if(plantable?.stages ?: 0 > currentGrowthStage && !isGrown()){
currentGrowthStage++
setCurrentState(getCurrentState() + 1)
isWatered = false
}
regrowIfTreeStump()
}
fun regrowIfTreeStump() {
if(patch.type == PatchType.TREE && plantable != null) {
// 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) {
setCurrentState(getCurrentState() - 1)
isWatered = false
}
}
}
fun update(){
grow()
updateBit()
}
fun plant(plantable: Plantable){
nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(plantable.applicablePatch.stageGrowthTime.toLong())
this.plantable = plantable
isDead = false
isDiseased = false
currentGrowthStage = 0
setCurrentState(plantable.value)
}
fun clear(){
isCheckHealth = false
isDiseased = false
isDead = false
plantable = null
player.varpManager.get(patch.varpIndex).clearBitRange(patch.varpOffset,patch.varpOffset + 7)
nextGrowth = 0L
currentGrowthStage = 3
setCurrentState(3)
compost = CompostType.NONE
protectionPaid = false
}
fun getFruitOrBerryCount() : Int {
plantable ?: return 0
return getCurrentState() - plantable!!.value - plantable!!.stages
}
fun getStageGrowthMinutes() : Int {
var minutes = patch.type.stageGrowthTime
if(patch.type == PatchType.FRUIT_TREE && isGrown()) {
// Fruit trees take 160 minutes per stage to grow, but
// restocking their fruit should take 40 minutes per fruit
minutes = 40
}
return minutes
}
fun isFlowerProtected(): Boolean{
if(patch.type != PatchType.ALLOTMENT) return false
val fpatch = when(patch){
FarmingPatch.S_FALADOR_ALLOTMENT_SE,FarmingPatch.S_FALADOR_ALLOTMENT_NW -> FarmingPatch.S_FALADOR_FLOWER_C
FarmingPatch.ARDOUGNE_ALLOTMENT_S,FarmingPatch.ARDOUGNE_ALLOTMENT_N -> FarmingPatch.ARDOUGNE_FLOWER_C
FarmingPatch.CATHERBY_ALLOTMENT_S,FarmingPatch.CATHERBY_ALLOTMENT_N -> FarmingPatch.CATHERBY_FLOWER_C
FarmingPatch.PORT_PHAS_ALLOTMENT_SE,FarmingPatch.PORT_PHAS_ALLOTMENT_NW -> FarmingPatch.PORT_PHAS_FLOWER_C
else -> return false
}.getPatchFor(player)
return (fpatch.plantable != null &&
(fpatch.plantable == plantable?.protectionFlower || fpatch.plantable == Plantable.forItemID(Items.WHITE_LILY_SEED_14589))
&& fpatch.isGrown())
}
}
@@ -0,0 +1,34 @@
package content.global.skill.farming
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items
object PatchRaker {
val RAKE_ANIM = Animation(2273)
@JvmStatic
fun rake(player: Player, patch: FarmingPatch){
player.pulseManager.run(object : Pulse(){
override fun pulse(): Boolean {
var patchStage = patch.getPatchFor(player).getCurrentState()
if(patchStage <= 2){
player.animator.animate(RAKE_ANIM)
player.audioManager.send(2442)
}
if(delay < 5) {
delay = 5
} else {
patch.getPatchFor(player).currentGrowthStage++
patch.getPatchFor(player).setCurrentState(++patchStage)
player.inventory.add(Item(Items.WEEDS_6055))
player.skills.addExperience(Skills.FARMING,4.0)
}
return patchStage >= 3
}
})
}
}
@@ -0,0 +1,16 @@
package content.global.skill.farming
enum class PatchType(val stageGrowthTime: Int) {
ALLOTMENT(10),
HOPS(10),
TREE(40),
FRUIT_TREE(160),
BUSH(20),
FLOWER(5),
HERB(20),
SPIRIT_TREE(293),
MUSHROOM(30),
BELLADONNA(80),
CACTUS(60),
EVIL_TURNIP(5)
}
@@ -0,0 +1,102 @@
package content.global.skill.farming
import core.game.node.item.Item
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) {
//Flowers
MARIGOLD_SEED(5096,8,4,8.5,47.0,0.0,2,PatchType.FLOWER,Items.MARIGOLDS_6010),
ROSEMARY_SEED(5097,13,4,12.0,66.5,0.0,11,PatchType.FLOWER, Items.ROSEMARY_6014),
NASTURTIUM_SEED(5098,18,4,19.5,111.0,0.0,24,PatchType.FLOWER,Items.NASTURTIUMS_6012),
WOAD_SEED(5099,23,4,20.5,115.5,0.0,25,PatchType.FLOWER,Items.WOAD_LEAF_1793),
LIMPWURT_SEED(5100,28,4,21.5,120.0,0.0,26,PatchType.FLOWER,Items.LIMPWURT_ROOT_225),
WHITE_LILY_SEED(14589,37,4,42.0,250.0,0.0,52,PatchType.FLOWER,Items.WHITE_LILY_14583),
//Flower(Technically)
SCARECROW(6059,33,3,0.0,0.0,0.0,23,PatchType.FLOWER,Items.SCARECROW_6059),
//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),
ONION_SEED(5319, 13, 4, 9.5, 10.5,0.0, 5, PatchType.ALLOTMENT,Items.ONION_1957,Item(Items.POTATOES10_5438),MARIGOLD_SEED),
CABBAGE_SEED(5324, 20, 4, 10.0, 11.5, 0.0,7, PatchType.ALLOTMENT,Items.CABBAGE_1965,Item(Items.ONIONS10_5458),ROSEMARY_SEED),
TOMATO_SEED(5322,27,4,12.5,14.0,0.0,12,PatchType.ALLOTMENT,Items.TOMATO_1982,Item(Items.CABBAGES10_5478,2),MARIGOLD_SEED),
SWEETCORN_SEED(5320,34,6,17.0,19.0,0.0,20,PatchType.ALLOTMENT,Items.SWEETCORN_5986,Item(Items.JUTE_FIBRE_5931,10),SCARECROW),
STRAWBERRY_SEED(5323,43,6,26.0,29.0,0.0,31,PatchType.ALLOTMENT,Items.STRAWBERRY_5504,Item(Items.APPLES5_5386)),
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
BARLEY_SEED(5305,49,4,8.5,9.5,0.0,3,PatchType.HOPS,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)),
ASGARNIAN_SEED(5308,11,5,10.9,12.0,0.0,8,PatchType.HOPS,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)),
YANILLIAN_SEED(5309,19,6,14.5,16.0,0.0,16,PatchType.HOPS,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)),
WILDBLOOD_SEED(5311,38,8,23.0,26.0,0.0,28,PatchType.HOPS,Items.WILDBLOOD_HOPS_6002,Item(Items.NASTURTIUMS_6012)),
//Trees
OAK_SAPLING(5370,8,4,14.0,0.0,467.3,15,PatchType.TREE,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)),
MAPLE_SAPLING(5372,24,8,45.0,0.0,3403.4,45,PatchType.TREE,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)),
MAGIC_SAPLING(5374,48,12,145.5,0.0,13768.3,75,PatchType.TREE,Items.MAGIC_ROOTS_6051,Item(Items.COCONUT_5974,25)),
//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)),
BANANA_SAPLING(5497,35,6,28.0,10.5,1750.5,33,PatchType.FRUIT_TREE,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)),
CURRY_SAPLING(5499,99,6,40.0,15.0,2906.9,42,PatchType.FRUIT_TREE,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)),
PAPAYA_SAPLING(5501,163,6,72.0,27.0,6146.4,57,PatchType.FRUIT_TREE,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)),
//Bushes
REDBERRY_SEED(5101,5,5,11.5,4.5,64.0,10,PatchType.BUSH,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)),
DWELLBERRY_SEED(5103,26,27,31.5,12.0,177.5,36,PatchType.BUSH,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)),
WHITEBERRY_SEED(5105,51,8,78.0,29.0,437.5,59,PatchType.BUSH,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),
//Herbs
GUAM_SEED(5291,4,4,11.0,12.5,0.0,9,PatchType.HERB,Items.GRIMY_GUAM_199),
MARRENTILL_SEED(5292,11,4,13.5,15.0,0.0,14,PatchType.HERB,Items.GRIMY_MARRENTILL_201),
TARROMIN_SEED(5293,18,4,16.0,18.0,0.0,19,PatchType.HERB,Items.GRIMY_TARROMIN_203),
HARRALANDER_SEED(5294,25,4,21.5,24.0,0.0,26,PatchType.HERB,Items.GRIMY_HARRALANDER_205),
RANARR_SEED(5295,32,4,27.0,30.5,0.0,32,PatchType.HERB,Items.GRIMY_RANARR_207),
AVANTOE_SEED(5298,39,4,54.5,61.5,0.0,50,PatchType.HERB,Items.GRIMY_AVANTOE_211),
TOADFLAX_SEED(5296,46,4,34.0,38.5,0.0,38,PatchType.HERB,Items.GRIMY_TOADFLAX_3049),
IRIT_SEED(5297,53,4,43.0,48.5,0.0,44,PatchType.HERB,Items.GRIMY_IRIT_209),
KWUARM_SEED(5299,68,4,69.0,78.0,0.0,56,PatchType.HERB,Items.GRIMY_KWUARM_213),
SNAPDRAGON_SEED(5300,75,4,87.5,98.5,0.0,62,PatchType.HERB,Items.GRIMY_SNAPDRAGON_3051),
CADANTINE_SEED(5301,82,4,106.5,120.0,0.0,67,PatchType.HERB,Items.GRIMY_CADANTINE_215),
LANTADYME_SEED(5302,89,4,134.5,151.5,0.0,73,PatchType.HERB,Items.GRIMY_LANTADYME_2485),
DWARF_WEED_SEED(5303,96,4,170.5,192.0,0.0,79,PatchType.HERB,Items.GRIMY_DWARF_WEED_217),
TORSTOL_SEED(5304,103,4,199.5,224.5,0.0,85,PatchType.HERB,Items.GRIMY_TORSTOL_219),
GOUT_TUBER(6311,192,4,105.0,45.0,0.0,29,PatchType.HERB,Items.GOUTWEED_3261),
SPIRIT_WEED_SEED(12176, 204, 4, 32.0, 36.0, 0.0, 36, PatchType.HERB, Items.GRIMY_SPIRIT_WEED_12174),
//Other
BELLADONNA_SEED(5281, 4, 4, 91.0, 128.0, 0.0, 63, PatchType.BELLADONNA, Items.CAVE_NIGHTSHADE_2398),
MUSHROOM_SPORE(Items.MUSHROOM_SPORE_5282, 6, 7, 61.5, 57.7, 0.0, 53, PatchType.MUSHROOM, Items.MUSHROOM_6004),
CACTUS_SEED(Items.CACTUS_SEED_5280, 8, 7, 66.5, 25.0, 374.0, 55, PatchType.CACTUS, 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)
;
constructor(itemID: Int, value: Int, stages: Int, plantingXP: Double, harvestXP: Double, checkHealthXP: Double, requiredLevel: Int, applicablePatch: PatchType, harvestItem: Int, protectionFlower: Plantable)
: this(itemID,value,stages,plantingXP,harvestXP,checkHealthXP,requiredLevel,applicablePatch,harvestItem,null,protectionFlower)
companion object {
@JvmField
val plantables = values().map { it.itemID to it }.toMap()
@JvmStatic
fun forItemID(id: Int): Plantable?{
return plantables[id]
}
@JvmStatic
fun forItem(item: Item): Plantable?{
return forItemID(item.id)
}
}
}
@@ -0,0 +1,140 @@
package content.global.skill.farming
import core.cache.def.impl.ItemDefinition
import core.game.interaction.OptionHandler
import core.game.node.Node
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.plugin.Initializable
import core.plugin.Plugin
import org.rs09.consts.Items
@Initializable
class SackBasketOptionHandler : OptionHandler() {
private companion object {
val fruit = arrayOf(Items.ORANGE_2108,Items.COOKING_APPLE_1955,Items.BANANA_1963,Items.STRAWBERRY_5504,Items.TOMATO_1982)
val produce = arrayOf(Items.POTATO_1942,Items.ONION_1957,Items.CABBAGE_1965)
}
override fun newInstance(arg: Any?): Plugin<Any> {
BasketsAndSacks.values().forEach { it.containers.forEach { id ->
val def = ItemDefinition.forId(id)
def.handlers["option:fill"] = this
def.handlers["option:empty"] = this
def.handlers["option:remove-one"] = this
} }
var def = ItemDefinition.forId(Items.EMPTY_SACK_5418)
def.handlers["option:fill"] = this
def = ItemDefinition.forId(Items.BASKET_5376)
def.handlers["option:fill"] = this
return this
}
override fun handle(player: Player?, node: Node?, option: String?): Boolean {
player ?: return false
node ?: return false
when(option){
"fill" -> tryFill(player,node.asItem())
"empty" -> tryEmpty(player,node.asItem())
"remove-one" -> tryTakeOne(player,node.asItem())
}
return true
}
private fun tryFill(player: Player?, item: Item){
player ?: return
val containerID = item.id
val appropriateProduce = getAppropriateProduce(player,containerID) ?: return
val container = BasketsAndSacks.forId(containerID) ?: BasketsAndSacks.forId(appropriateProduce.id) ?: return
val isLast = container.checkIsLast(containerID)
val specific = container.checkWhich(containerID)
val max = container.containers.size - 1
if(isLast){
player.sendMessage("This is already full.")
return
}
if(specific + appropriateProduce.amount > max){
appropriateProduce.amount = (max - specific)
}
if(player.inventory.remove(item) && player.inventory.remove(appropriateProduce))
player.inventory.add(Item(container.containers[specific + appropriateProduce.amount]))
}
private fun tryEmpty(player: Player?, item: Item){
val container = BasketsAndSacks.forId(item.id)
if(container == null) return
player ?: return
val emptyItem = if(produce.contains(container.produceID)) Items.EMPTY_SACK_5418 else Items.BASKET_5376
val returnItem = Item(container.produceID,container.checkWhich(item.id) + 1)
if(!player.inventory.hasSpaceFor(returnItem)){
player.sendMessage("You don't have enough inventory space to do this.")
return
}
if(player.inventory.remove(item)){
player.inventory.add(Item(emptyItem))
player.inventory.add(returnItem)
}
}
private fun tryTakeOne(player: Player?,item: Item){
val container = BasketsAndSacks.forId(item.id)
if(container == null) return
player ?: return
val emptyItem = if(produce.contains(container.produceID)) Items.EMPTY_SACK_5418 else Items.BASKET_5376
val isLast = container.checkIsFirst(item.id)
val withdrawnItem = Item(container.produceID)
if(!player.inventory.hasSpaceFor(withdrawnItem)){
player.sendMessage("You don't have enough inventory space to do this.")
return
}
if(player.inventory.remove(item)){
if(isLast){
player.inventory.add(Item(emptyItem))
} else {
val it = Item(container.containers[container.checkWhich(item.id) - 1])
player.inventory.add(it)
}
player.inventory.add(withdrawnItem)
}
}
private fun getAppropriateProduce(player: Player?, containerID: Int): Item?{
player ?: return null
val container = BasketsAndSacks.forId(containerID)
val produce = if(container == null){
var selected = 0
if(containerID == Items.EMPTY_SACK_5418) {
for (i in (produce)) {
if (player.inventory.contains(i, 1)) {
selected = i
break
}
}
selected
} else {
for (i in (fruit)) {
if (player.inventory.contains(i, 1)) {
selected = i
break
}
}
selected
}
} else {
container.produceID
}
return if(produce == 0) null else Item(produce,player.inventory.getAmount(produce))
}
}
@@ -0,0 +1,117 @@
package content.global.skill.farming
import core.api.addItem
import core.api.inInventory
import core.api.removeItem
import core.api.sendDialogue
import core.game.node.Node
import core.game.node.entity.player.Player
import org.rs09.consts.Items
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
class SeedlingListener : InteractionListener {
override fun defineListeners() {
onUseWith(IntType.ITEM, TREE_SEEDS, Items.PLANT_POT_5354, handler = ::addSeedToPot)
onUseWith(IntType.ITEM, TREE_SEEDLINGS, *WATERING_CANS, handler = ::waterSeedling)
}
fun addSeedToPot(player: Player, used: Node, with: Node) : Boolean {
val seed = used.asItem() ?: return false
val pot = with.asItem() ?: return false
if(!inInventory(player, Items.GARDENING_TROWEL_5325)){
sendDialogue(player, "You need a gardening trowel on you to do this.")
return false
}
val seedling = getSeedling(seed.id)
if (seedling == -1) return false
if (!removeItem(player, seed.id) || !removeItem(player, pot)) return true
addItem(player, seedling)
return true
}
fun waterSeedling(player: Player, used: Node, with: Node) : Boolean {
val seedling = used.asItem() ?: return false
val can = with.asItem() ?: return false
val nextCan = can.id.getNext()
val wateredSeedling = if (seedling.id > 5400) seedling.id + 8 else seedling.id + 6
if (!removeItem(player, can) || !removeItem(player, seedling)) return false
addItem(player, wateredSeedling)
addItem(player, nextCan)
var state = player.states["seedling"] as SeedlingState?
if (state != null) {
state.addSeedling(wateredSeedling)
return true
}
state = player.registerState("seedling") as SeedlingState?
state?.addSeedling(wateredSeedling)
state?.init()
return true
}
private fun Int.getNext(): Int{
val index = WATERING_CANS.indexOf(this)
if (index == -1) return 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 {
return when (id) {
Items.ACORN_5312 -> Items.OAK_SEEDLING_5358
Items.WILLOW_SEED_5313 -> Items.WILLOW_SEEDLING_5359
Items.MAPLE_SEED_5314 -> Items.MAPLE_SEEDLING_5360
Items.YEW_SEED_5315 -> Items.YEW_SEEDLING_5361
Items.MAGIC_SEED_5316 -> Items.MAGIC_SEEDLING_5362
Items.APPLE_TREE_SEED_5283 -> Items.APPLE_SEEDLING_5480
Items.BANANA_TREE_SEED_5284 -> Items.BANANA_SEEDLING_5481
Items.ORANGE_TREE_SEED_5285 -> Items.ORANGE_SEEDLING_5482
Items.CURRY_TREE_SEED_5286 -> Items.CURRY_SEEDLING_5483
Items.PINEAPPLE_SEED_5287 -> Items.PINEAPPLE_SEEDLING_5484
Items.PAPAYA_TREE_SEED_5288 -> Items.PAPAYA_SEEDLING_5485
Items.PALM_TREE_SEED_5289 -> Items.PALM_SEEDLING_5486
Items.SPIRIT_SEED_5317 -> Items.SPIRIT_SEEDLING_5363
else -> -1
}
}
val TREE_SEEDS = intArrayOf(
Items.ACORN_5312,
Items.WILLOW_SEED_5313,
Items.MAPLE_SEED_5314,
Items.YEW_SEED_5315,
Items.MAGIC_SEED_5316,
Items.APPLE_TREE_SEED_5283,
Items.BANANA_TREE_SEED_5284,
Items.ORANGE_TREE_SEED_5285,
Items.CURRY_TREE_SEED_5286,
Items.PINEAPPLE_SEED_5287,
Items.PAPAYA_TREE_SEED_5288,
Items.PALM_TREE_SEED_5289,
Items.SPIRIT_SEED_5317
)
val TREE_SEEDLINGS = intArrayOf(
Items.OAK_SEEDLING_5358,
Items.WILLOW_SEEDLING_5359,
Items.MAPLE_SEEDLING_5360,
Items.YEW_SEEDLING_5361,
Items.MAGIC_SEEDLING_5362,
Items.APPLE_SEEDLING_5480,
Items.BANANA_SEEDLING_5481,
Items.ORANGE_SEEDLING_5482,
Items.CURRY_SEEDLING_5483,
Items.PINEAPPLE_SEEDLING_5484,
Items.PAPAYA_SEEDLING_5485,
Items.PALM_SEEDLING_5486,
Items.SPIRIT_SEEDLING_5363
)
private val WATERING_CANS = intArrayOf(Items.WATERING_CAN8_5340,Items.WATERING_CAN7_5339,Items.WATERING_CAN6_5338,Items.WATERING_CAN5_5337,Items.WATERING_CAN4_5336,Items.WATERING_CAN3_5335,Items.WATERING_CAN2_5334,Items.WATERING_CAN1_5333)
}
@@ -0,0 +1,5 @@
package content.global.skill.farming
class Seedling(val id: Int, val TTL: Long, val sapling: Int) {
}
@@ -0,0 +1,83 @@
package content.global.skill.farming
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.system.task.Pulse
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import core.game.node.entity.state.PlayerState
import core.game.node.entity.state.State
import java.util.concurrent.TimeUnit
@PlayerState("seedling")
class SeedlingState(player: Player? = null) : State(player) {
val seedlings = ArrayList<Seedling>()
fun addSeedling(seedling: Int){
seedlings.add(Seedling(seedling, System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5),seedling + if(seedling > 5400) 8 else 6))
}
override fun save(root: JSONObject) {
val seedArray = JSONArray()
for(s in seedlings){
val seed = JSONObject()
seed.put("id",s.id)
seed.put("ttl",s.TTL)
seed.put("sapling",s.sapling)
seedArray.add(seed)
}
root.put("seedlings",seedArray)
}
override fun parse(_data: JSONObject) {
if(_data.containsKey("seedlings")){
(_data["seedlings"] as JSONArray).forEach {
val s = it as JSONObject
val id = s["id"].toString().toInt()
val ttl = s["ttl"].toString().toLong()
val sapling = s["sapling"].toString().toInt()
seedlings.add(Seedling(id,ttl,sapling))
}
}
}
override fun newInstance(player: Player?): State {
return SeedlingState(player)
}
override fun createPulse() {
if(seedlings.isEmpty()) return
player ?: return
pulse = object : Pulse(5){
override fun pulse(): Boolean {
val removeList = ArrayList<Seedling>()
GlobalScope.launch {
for (seed in seedlings) {
if (System.currentTimeMillis() > seed.TTL) {
val inInventory = player.inventory.get(Item(seed.id))
if (inInventory != null) {
player.inventory.replace(Item(seed.sapling), inInventory.slot)
removeList.add(seed)
} else {
val inBank = player.bank.get(Item(seed.id))
if(inBank == null) removeList.add(seed)
else {
player.bank.remove(Item(inBank.id,1))
player.bank.add(Item(seed.sapling))
removeList.add(seed)
}
}
}
}
seedlings.removeAll(removeList)
}
return false
}
}
}
}
@@ -0,0 +1,37 @@
package content.global.skill.farming
import core.cache.def.impl.NPCDefinition
import core.game.component.Component
import core.game.dialogue.FacialExpression
import core.game.interaction.OptionHandler
import core.game.node.Node
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.plugin.Initializable
import core.plugin.Plugin
import org.rs09.consts.Components
import org.rs09.consts.NPCs
import content.minigame.vinesweeper.Vinesweeper
val TL_IDS = arrayOf(NPCs.TOOL_LEPRECHAUN_3021,NPCs.GOTH_LEPRECHAUN_8000,NPCs.TOOL_LEPRECHAUN_4965,NPCs.TECLYN_2861)
@Initializable
class ToolLeprechaunHandler : OptionHandler() {
override fun newInstance(arg: Any?): Plugin<Any> {
for(id in TL_IDS){
val def = NPCDefinition.forId(id)
def.handlers["option:exchange"] = this
def.handlers["option:teleport"] = this
}
return this
}
override fun handle(player: Player?, node: Node?, option: String?): Boolean {
node ?: return false
when(option){
"exchange" -> player?.interfaceManager?.open(Component(Components.FARMING_TOOLS_125))
"teleport" -> Vinesweeper.Companion.VinesweeperTeleport.teleport(node!! as NPC, player!!)
}
return true
}
}
@@ -0,0 +1,355 @@
package content.global.skill.farming
import core.api.*
import core.game.component.Component
import core.game.node.entity.player.Player
import core.game.node.item.Item
import org.rs09.consts.Components
import org.rs09.consts.Items
import core.game.interaction.InterfaceListener
private const val varp = 615
class ToolLeprechaunInterface : InterfaceListener {
private val FARMING_TOOLS = Components.FARMING_TOOLS_125
private val TOOLS_SIDE = Components.FARMING_TOOLS_SIDE_126
override fun defineInterfaceListeners() {
onOpen(FARMING_TOOLS){player, component ->
player.varpManager?.flagSave(varp)
player.interfaceManager.openSingleTab(Component(TOOLS_SIDE))
return@onOpen true
}
onClose(FARMING_TOOLS){player, _ ->
player.interfaceManager.closeSingleTab()
}
on(FARMING_TOOLS){player, _, opcode, buttonID, _, _ ->
when(buttonID){
33 -> doWithdrawal(player, Items.RAKE_5341,::setHasRake,::hasRake)
34 -> doWithdrawal(player,Items.SEED_DIBBER_5343,::setHasDibber,::hasDibber)
35 -> doWithdrawal(player,Items.SPADE_952,::setHasSpade,::hasSpade)
36 -> {
val sec = if(hasMagicSecateurs(player)) Items.MAGIC_SECATEURS_7409 else Items.SECATEURS_5329
doWithdrawal(player,sec,::setHasSecateurs,::hasSecateurs)
}
37 -> {
if(!hasWateringCan(player)){
player.dialogueInterpreter.sendDialogue("You don't have any of those stored.")
} else {
player.inventory.add(Item(getWateringCan(player)))
setNoWateringCan(player)
}
}
38 -> doWithdrawal(player,Items.GARDENING_TROWEL_5325,::setHasGardeningTrowel,::hasGardeningTrowel)
39 -> doStackedWithdrawal(player,Items.BUCKET_1925,getAmount(opcode),::updateBuckets,::getNumBuckets)
40 -> doStackedWithdrawal(player,Items.COMPOST_6032,getAmount(opcode),::updateCompost,::getNumCompost)
41 -> doStackedWithdrawal(player,Items.SUPERCOMPOST_6034,getAmount(opcode),::updateSuperCompost,::getNumSuperCompost)
}
player.varpManager.get(varp).send(player)
return@on true
}
on(TOOLS_SIDE){player, _, opcode, buttonID, _, _ ->
when(buttonID){
18 -> doDeposit(player,Items.RAKE_5341,::setHasRake,::hasRake)
19 -> doDeposit(player,Items.SEED_DIBBER_5343,::setHasDibber,::hasDibber)
20 -> doDeposit(player,Items.SPADE_952,::setHasSpade,::hasSpade)
21 -> {
if(!player.inventory.contains(Items.SECATEURS_5329,1) && !player.inventory.contains(Items.MAGIC_SECATEURS_7409,1)){
player.dialogueInterpreter.sendDialogue("You don't have any of those to store.")
} else if (!hasSecateurs(player)){
if(player.inventory.contains(Items.MAGIC_SECATEURS_7409,1)){
player.inventory.remove(Item(Items.MAGIC_SECATEURS_7409))
setHasSecateurs(player,true)
setHasMagicSecateurs(player,true)
} else {
player.inventory.remove(Item(Items.SECATEURS_5329))
setHasSecateurs(player,true)
setHasMagicSecateurs(player,false)
}
} else {
player.dialogueInterpreter.sendDialogue("You already have one of those stored.")
}
}
22 -> {
val can = getHighestCan(player)
if(can == null){
player.dialogueInterpreter.sendDialogue("You don't have any of those to store.")
} else if(!hasWateringCan(player)){
player.inventory.remove(can)
setWateringCan(player,can)
} else {
player.dialogueInterpreter.sendDialogue("You already have one of those stored.")
}
}
23 -> doDeposit(player,Items.GARDENING_TROWEL_5325,::setHasGardeningTrowel,::hasGardeningTrowel)
24 -> doStackedDeposit(player,Items.BUCKET_1925,getAmount(opcode),::updateBuckets,::getNumBuckets)
25 -> doStackedDeposit(player,Items.COMPOST_6032,getAmount(opcode),::updateCompost,::getNumCompost)
26 -> doStackedDeposit(player,Items.SUPERCOMPOST_6034,getAmount(opcode),::updateSuperCompost,::getNumSuperCompost)
}
player.varpManager.get(varp).send(player)
return@on true
}
}
private fun doWithdrawal(player: Player?, item: Int, withdrawMethod: (Player?,Boolean) -> Unit, checkMethod: (Player?) -> Boolean){
player ?: return
if(!checkMethod.invoke(player)){
player.dialogueInterpreter.sendDialogue("You don't have any of those stored.")
} else {
if(!player.inventory.hasSpaceFor(Item(item))){
player.dialogueInterpreter.sendDialogue("You don't have enough space for that.")
return
}
withdrawMethod.invoke(player,false)
player.inventory.add(Item(item))
}
}
private fun doDeposit(player: Player?, item: Int, depositMethod: (Player?,Boolean) -> Unit, checkMethod: (Player?) -> Boolean){
player ?: return
if(!player.inventory.contains(item,1)){
player.dialogueInterpreter.sendDialogue("You don't have any of those to store.")
}
if(!checkMethod.invoke(player)){
depositMethod.invoke(player,true)
player.inventory.remove(Item(item))
} else {
player.dialogueInterpreter.sendDialogue("You already have one of those stored.")
}
}
private fun doStackedDeposit(player: Player?, item: Int, amount: Int, updateQuantityMethod: (Player?, Int) -> Unit, quantityCheckMethod: (Player?) -> Int){
player ?: return
val hasAmount = player.inventory.getAmount(item)
var finalAmount = amount
val spaceLeft = (if(item == Items.BUCKET_1925) 31 else 255) - quantityCheckMethod.invoke(player)
if(hasAmount == 0){
player.dialogueInterpreter.sendDialogue("You don't have any of those to store.")
return
}
if(amount == -2){
sendInputDialogue(player, true, "Enter the amount:"){value ->
var amt = value as Int
if(amt > hasAmount){
amt = hasAmount
}
if(amt > spaceLeft){
amt = spaceLeft
}
if(amt == 0){
player.dialogueInterpreter.sendDialogue("You don't have any of those to store.")
return@sendInputDialogue
}
player.inventory.remove(Item(item,amt))
updateQuantityMethod.invoke(player,amt)
player.varpManager.get(varp).send(player)
}
return
}
if(amount == -1){
finalAmount = hasAmount
if(finalAmount > spaceLeft){
finalAmount = spaceLeft
}
}
if(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){
player.dialogueInterpreter.sendDialogue("You can't store any more of those.")
return
}
player.inventory.remove(Item(item,finalAmount))
updateQuantityMethod.invoke(player,finalAmount)
}
private fun doStackedWithdrawal(player: Player?,item: Int, amount: Int,updateQuantityMethod: (Player?,Int) -> Unit, quantityCheckMethod: (Player?) -> Int){
player ?: return
val hasAmount = quantityCheckMethod.invoke(player)
var finalAmount = amount
if(hasAmount == 0){
player.dialogueInterpreter.sendDialogue("You don't have any of those stored.")
} else {
if(amount == -2){
sendInputDialogue(player, true, "Enter the amount:"){value ->
var amt = value as Int
if(amt > hasAmount){
amt = hasAmount
}
if(amt > player.inventory.freeSlots()){
amt = player.inventory.freeSlots()
}
if(amt <= 0){
player.dialogueInterpreter.sendDialogue("You don't have enough inventory space for that.")
} else {
player.inventory.add(Item(item, amt))
updateQuantityMethod.invoke(player, -amt)
player.varpManager.get(varp).send(player)
}
}
return
}
if(amount == -1){
finalAmount = player.inventory.freeSlots()
}
if(finalAmount > hasAmount){
finalAmount = hasAmount
}
if(!player.inventory.hasSpaceFor(Item(item,finalAmount)) || finalAmount == 0){
player.dialogueInterpreter.sendDialogue("You don't have enough inventory space for that.")
return
}
player.inventory.add(Item(item,finalAmount))
updateQuantityMethod.invoke(player,-finalAmount)
}
}
fun getAmount(opcode: Int): Int{
return when(opcode) {
155 -> 1
196 -> 5
124 -> -1
199 -> -2
else -> 0
}
}
private fun hasRake(player: Player?): Boolean{
return checkBit(player,0)
}
private fun setHasRake(player: Player?,hasRake: Boolean){
player?.varpManager?.get(varp)?.setVarbit(0,if(hasRake) 1 else 0)
}
private fun hasDibber(player: Player?): Boolean{
return checkBit(player,1)
}
private fun setHasDibber(player: Player?,hasDibber: Boolean){
player?.varpManager?.get(varp)?.setVarbit(1,if(hasDibber) 1 else 0)
}
private fun hasSpade(player: Player?): Boolean{
return checkBit(player,2)
}
private fun setHasSpade(player: Player?,hasSpade: Boolean){
player?.varpManager?.get(varp)?.setVarbit(2,if(hasSpade) 1 else 0)
}
private fun hasSecateurs(player: Player?): Boolean{
return checkBit(player,3)
}
private fun setHasSecateurs(player: Player?,hasSecateurs: Boolean){
player?.varpManager?.get(varp)?.setVarbit(3,if(hasSecateurs) 1 else 0)
}
private fun hasWateringCan(player: Player?): Boolean{
return checkBitRange(player,4,7) > 0
}
private fun getWateringCan(player: Player?): Int{
var can = checkBitRange(player,4,7) //Watering cans are stored in the Varp as a number between 1 and 9. Watering Can(0) is 1 and Watering Can(8) is 9
if(can == 1) can = 0
return Items.WATERING_CAN_5331 + can
}
private fun setWateringCan(player: Player?, item: Item){
val can = when(item.id){
Items.WATERING_CAN_5331 -> 1
Items.WATERING_CAN1_5333 -> 2
Items.WATERING_CAN2_5334 -> 3
Items.WATERING_CAN3_5335 -> 4
Items.WATERING_CAN4_5336 -> 5
Items.WATERING_CAN5_5337 -> 6
Items.WATERING_CAN6_5338 -> 7
Items.WATERING_CAN7_5339 -> 8
Items.WATERING_CAN8_5340 -> 9
else -> 0
}
player?.varpManager?.get(varp)?.setVarbit(4,can)
}
private fun setNoWateringCan(player: Player?){
player?.varpManager?.get(varp)?.setVarbit(4,0)
}
private fun hasGardeningTrowel(player: Player?): Boolean {
return checkBit(player,8)
}
private fun setHasGardeningTrowel(player: Player?,hasTrowel: Boolean){
player?.varpManager?.get(varp)?.setVarbit(8,if(hasTrowel) 1 else 0)
}
private fun getNumBuckets(player: Player?): Int {
return checkBitRange(player,9,13)
}
private fun updateBuckets(player: Player?,amount: Int){
player?.varpManager?.get(varp)?.setVarbit(9,getNumBuckets(player) + amount)
}
private fun getNumCompost(player: Player?): Int {
return checkBitRange(player,14,21)
}
private fun updateCompost(player: Player?,amount: Int){
player?.varpManager?.get(varp)?.setVarbit(14,getNumCompost(player) + amount)
}
private fun getNumSuperCompost(player: Player?): Int {
return checkBitRange(player,22,29)
}
private fun updateSuperCompost(player: Player?,amount: Int){
player?.varpManager?.get(varp)?.setVarbit(22,getNumSuperCompost(player) + amount)
}
private fun hasMagicSecateurs(player: Player?): Boolean {
return checkBit(player,30)
}
private fun setHasMagicSecateurs(player: Player?,hasMagic: Boolean){
player?.varpManager?.get(varp)?.setVarbit(30,if(hasMagic) 1 else 0)
}
private fun checkBitRange(player: Player?,start: Int, end: Int): Int{
return (player?.varpManager?.get(varp)?.getBitRangeValue(start,end) ?: 0) ushr start
}
private fun checkBit(player: Player?,offset: Int): Boolean{
return player?.varpManager?.get(varp)?.getVarbitValue(offset) == 1
}
private fun getHighestCan(player: Player?): Item?{
player ?: return null
var highestCan = Item(0)
for(item in player.inventory.toArray()){
if(item == null) continue
if(item.name.contains("Watering")){
if(item.id > highestCan.id) highestCan = item
}
}
return if(highestCan.id == 0) null else highestCan
}
}
@@ -0,0 +1,103 @@
package content.global.skill.farming
import core.api.toIntArray
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
class UseWithBinHandler : InteractionListener {
@JvmField
val allowedNodes = ArrayList<Int>(100)
val fillAnim = Animation(832)
val compostPotionAnimation = Animation(2259)
val scoopAnimation = Animation(8905)
val bins = 7836..7840
override fun defineListeners() {
loadNodes()
onUseWith(IntType.SCENERY, allowedNodes.toIntArray(), *bins.toIntArray()) { player, usedNode, with ->
val cBin = CompostBins.forObject(with.asScenery()) ?: return@onUseWith true
val bin = cBin.getBinForPlayer(player)
val used = usedNode.id
when(used){
Items.COMPOST_POTION1_6476,Items.COMPOST_POTION2_6474, Items.COMPOST_POTION3_6472,Items.COMPOST_POTION4_6470 -> {
if(!bin.isSuperCompost && bin.isFinished && !bin.isClosed){
player.animator.animate(compostPotionAnimation)
player.pulseManager.run(object : Pulse(compostPotionAnimation.duration){
override fun pulse(): Boolean {
if(player.inventory.remove(usedNode.asItem())) {
bin.convert()
player.inventory.add(Item(used.getNext()))
}
return true
}
})
} else {
player.dialogueInterpreter.sendDialogue("You can only do this with an open bin of","finished regular compost.")
}
}
Items.BUCKET_1925 -> {
if(bin.isFinished && !bin.isClosed){
player.pulseManager.run(object : Pulse(scoopAnimation.duration){
override fun pulse(): Boolean {
if(!player.inventory.containsItem(usedNode.asItem())) return true
player.animator.animate(scoopAnimation)
val item = bin.takeItem()
if(item != null && player.inventory.remove(usedNode.asItem())){
player.inventory.add(item)
}
return item == null || !player.inventory.containsItem(usedNode.asItem())
}
})
} else {
player.dialogueInterpreter.sendDialogue("You can only scoop an opened bin of finished compost.")
}
}
else ->
if(bin.isFull()){
player.sendMessage("This compost bin is already full.")
return@onUseWith true
} else {
player.pulseManager.run(object : Pulse(fillAnim.duration){
override fun pulse(): Boolean {
player.animator.animate(fillAnim)
if(player.inventory.remove(usedNode.asItem())){
bin.addItem(usedNode.asItem())
}
return bin.isFull() || player.inventory.getAmount(usedNode.asItem()) == 0
}
})
}
}
return@onUseWith true
}
}
fun loadNodes() {
for(p in Plantable.values()){
if(p.harvestItem != Items.SCARECROW_6059) {
allowedNodes.add(p.harvestItem)
}
}
allowedNodes.add(Items.COCONUT_SHELL_5978)
allowedNodes.add(Items.WEEDS_6055)
allowedNodes.add(Items.COMPOST_POTION4_6470)
allowedNodes.add(Items.COMPOST_POTION3_6472)
allowedNodes.add(Items.COMPOST_POTION2_6474)
allowedNodes.add(Items.COMPOST_POTION1_6476)
allowedNodes.add(Items.BUCKET_1925)
}
private fun Int.getNext(): Int{
if(this != 6476) return this + 2
else return Items.VIAL_229
}
}
@@ -0,0 +1,256 @@
package content.global.skill.farming
import core.api.*
import core.game.node.entity.player.link.diary.DiaryType
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
class UseWithPatchHandler : InteractionListener {
val RAKE = Items.RAKE_5341
val SEED_DIBBER = Items.SEED_DIBBER_5343
val SPADE = Items.SPADE_952
val SECATEURS = Items.SECATEURS_5329
val TROWEL = Items.GARDENING_TROWEL_5325
val pourBucketAnim = Animation(2283)
val wateringCanAnim = Animation(2293)
val plantCureAnim = Animation(2288)
val secateursAnim = Animation(7227)
@JvmField
val allowedNodes = ArrayList<Int>()
val patches = ArrayList<Int>()
override fun defineListeners() {
loadNodes()
onUseWith(IntType.SCENERY, allowedNodes.toIntArray(), *patches.toIntArray()) { player, used, with ->
val patch = FarmingPatch.forObject(with.asScenery()) ?: return@onUseWith true
val usedItem = used.asItem()
player.faceLocation(with.location)
when(usedItem.id){
RAKE -> PatchRaker.rake(player,patch)
SEED_DIBBER -> player.sendMessage("I should plant a seed, not the seed dibber.")
SPADE -> player.dialogueInterpreter.open(67984003,patch.getPatchFor(player)) //DigUpPatchDialogue.kt
SECATEURS -> {
val p = patch.getPatchFor(player)
if(patch.type == PatchType.TREE) {
if(p.isDiseased && !p.isDead) {
player.pulseManager.run(object: Pulse(){
override fun pulse(): Boolean {
player.animator.animate(secateursAnim)
p.cureDisease()
return true
}
})
} else if(p.plantable == Plantable.WILLOW_SAPLING && p.harvestAmt > 0) {
val pulse = CropHarvester.harvestPulse(player, with, Items.WILLOW_BRANCH_5933) ?: return@onUseWith false
player.pulseManager.run(pulse)
}
}
}
TROWEL, Items.PLANT_POT_5350 -> {
if(!player.inventory.containsAtLeastOneItem(TROWEL)) {
player.sendMessage("You need a trowel to fill plant pots with dirt.")
return@onUseWith true
}
val p = patch.getPatchFor(player)
if(!p.isWeedy()){
player.sendMessage("This patch has something growing in it.")
return@onUseWith true
} else if (p.currentGrowthStage != 3){
player.sendMessage("I should clear this of weeds before trying to take some dirt.")
return@onUseWith true
}
val potAmount = player.inventory.getAmount(Items.PLANT_POT_5350)
if(potAmount == 0){
player.sendMessage("You have no plant pots to fill.")
return@onUseWith true
}
val anim = Animation(2272)
player.pulseManager.run(object : Pulse(anim.duration){
override fun pulse(): Boolean {
if(player.inventory.remove(Item(Items.PLANT_POT_5350))){
player.animator.animate(anim)
player.inventory.add(Item(Items.PLANT_POT_5354))
} else return true
return false
}
})
}
Items.PLANT_CURE_6036 -> {
val p = patch.getPatchFor(player)
if(p.isDiseased && !p.isDead){
player.pulseManager.run(object: Pulse(){
override fun pulse(): Boolean {
player.animator.animate(plantCureAnim)
player.audioManager.send(2438)
if(player.inventory.remove(usedItem)){
player.inventory.add(Item(Items.VIAL_229))
p.cureDisease()
}
return true
}
})
} else {
player.sendMessage("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 -> {
val p = patch.getPatchFor(player)
val t = p.patch.type
if(!p.isWatered && (t == PatchType.ALLOTMENT || t == PatchType.FLOWER || t == PatchType.HOPS) && !p.isGrown()){
player.pulseManager.run(object : Pulse(){
override fun pulse(): Boolean {
if(p.isWeedy()){
player.sendMessage("You should grow something first.")
return true
}
player.animator.animate(wateringCanAnim)
player.audioManager.send(2446)
if(player.inventory.remove(usedItem)){
player.inventory.add(Item(usedItem.id.getNext()))
p.water()
}
return true
}
})
}
}
Items.SUPERCOMPOST_6034, Items.COMPOST_6032 -> {
val p = patch.getPatchFor(player)
if(p.compost == CompostType.NONE) {
player.animator.animate(pourBucketAnim)
player.audioManager.send(2427)
player.pulseManager.run(object : Pulse(){
override fun pulse(): Boolean {
if(player.inventory.remove(usedItem,false)){
p.compost = if(usedItem.id == Items.SUPERCOMPOST_6034) CompostType.SUPER else CompostType.NORMAL
if(p.compost == CompostType.SUPER) rewardXP(player, Skills.FARMING, 26.0) else rewardXP(player, Skills.FARMING, 18.5)
if(p.plantable != null && p.plantable?.applicablePatch != PatchType.FLOWER) {
p.harvestAmt += if(p.compost == CompostType.NORMAL) 1 else if(p.compost == CompostType.SUPER) 2 else 0
}
p.cropLives += if(p.compost == CompostType.SUPER) 2 else 1
player.inventory.add(Item(Items.BUCKET_1925))
}
return true
}
})
} else {
player.sendMessage("This patch has already been treated with compost.")
}
}
else -> {
val plantable = Plantable.forItemID(usedItem.id) ?: return@onUseWith false
if(plantable.applicablePatch != patch.type){
player.sendMessage("You can't plant that seed in this patch.")
return@onUseWith true
}
if(plantable.requiredLevel > player.skills.getLevel(Skills.FARMING)){
player.sendMessage("You need a Farming level of ${plantable.requiredLevel} to plant this.")
return@onUseWith true
}
val p = patch.getPatchFor(player)
if(p.getCurrentState() < 3 && p.isWeedy()){
player.sendMessage("You must weed your patch before you can plant a seed in it.")
return@onUseWith true
} else if(p.getCurrentState() > 3){
player.sendMessage("There is already something growing in this patch.")
return@onUseWith true
}
val plantItem =
if(patch.type == PatchType.ALLOTMENT) Item(plantable.itemID,3) else if(patch.type == PatchType.HOPS){
if(plantable == Plantable.JUTE_SEED) Item(plantable.itemID,3) else Item(plantable.itemID,4)
} else {
Item(plantable.itemID,1)
}
if(patch.type == PatchType.ALLOTMENT){
if(!player.inventory.containsItem(plantItem)){
player.sendMessage("You need 3 seeds to plant an allotment patch.")
return@onUseWith true
}
}
if(patch.type != PatchType.FRUIT_TREE && patch.type != PatchType.TREE){
if(!player.inventory.contains(Items.SEED_DIBBER_5343,1)){
player.sendMessage("You need a seed dibber to plant that.")
return@onUseWith true
}
} else {
if(!player.inventory.contains(Items.SPADE_952,1)){
player.sendMessage("You need a spade to plant that.")
return@onUseWith true
}
}
player.lock()
if(player.inventory.remove(plantItem)) {
player.animator.animate(Animation(2291))
player.audioManager.send(2432)
player.pulseManager.run(object : Pulse(3) {
override fun pulse(): Boolean {
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)
}
p.plant(plantable)
player.skills.addExperience(Skills.FARMING, plantable.plantingXP)
p.setNewHarvestAmount()
if(p.patch.type == PatchType.TREE || p.patch.type == PatchType.FRUIT_TREE){
player.inventory.add(Item(Items.PLANT_POT_5350))
}
player.unlock()
return true
}
})
}
}
}
return@onUseWith true
}
}
fun loadNodes(){
patches.addAll(8550..8557) //allotment wrappers
patches.addAll(7847..7853) //flower patch wrappers
patches.addAll(8150..8156) //herb patch wrappers
patches.addAll(8388..8391) // Tree patches
patches.add(19147) //Tree patch
patches.addAll(7962..7965) //fruit trees
patches.addAll(8173..8176) //hops
patches.addAll(7577..7580) //bush
patches.add(23760) //evil turnip
patches.add(7572) //belladonna
patches.add(8337) //mushroom
patches.add(27197) //jade vine
patches.add(7771) //cactus
patches.add(7807) //calquat
patches.addAll(8382..8383)//spirit trees
patches.add(8338) //spirit tree
patches.add(18816) //death plateau wrapper
for(p in Plantable.values()){
allowedNodes.add(p.itemID)
}
allowedNodes.addAll(arrayListOf(RAKE,SEED_DIBBER,SPADE,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))
}
private fun Int.getNext(): Int {
if(this == Items.WATERING_CAN1_5333) return Items.WATERING_CAN_5331
else return this - 1
}
}