diff --git a/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt b/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt index 7b29a5568..24c4dd90a 100644 --- a/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt +++ b/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt @@ -11,6 +11,14 @@ import org.rs09.consts.Items class FarmerPayOptionDialogue(val patch: Patch, val quickPay: Boolean = false): DialogueFile() { var item: Item? = null + var itemSecondary: Item? = null + var itemTertiary: Item? = null + + fun hasAllItems(): Boolean { + return (inInventory(player!!, item!!.id, item!!.amount) || inInventory(player!!, note(item!!).id, note(item!!).amount)) + && (inInventory(player!!, itemSecondary!!.id, itemSecondary!!.amount) || inInventory(player!!, note(itemSecondary!!).id, note(itemSecondary!!).amount)) + && (inInventory(player!!, itemTertiary!!.id, itemTertiary!!.amount) || inInventory(player!!, note(itemTertiary!!).id, note(itemTertiary!!).amount)) + } override fun handle(componentID: Int, buttonID: Int) { when (stage) { START_DIALOGUE -> { @@ -33,6 +41,9 @@ class FarmerPayOptionDialogue(val patch: Patch, val quickPay: Boolean = false): npc("That patch is already fully grown!", "I don't know what you want me to do with it!").also { stage = END_DIALOGUE } } else { item = patch.plantable?.protectionItem + itemSecondary = patch.plantable?.protectionItemSecondary + itemTertiary = patch.plantable?.protectionItemTertiary + 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" @@ -51,10 +62,22 @@ class FarmerPayOptionDialogue(val patch: Patch, val quickPay: Boolean = false): } if (item == null) { npc("Sorry, I won't protect that.").also { stage = END_DIALOGUE } + } else if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH && quickPay + && !hasAllItems()) { + val amount = if (item?.amount == 1) "one" else item?.amount + npc(FacialExpression.HAPPY, "I want $amount $protectionText, one monkey bar,", "and one ground tooth for that.") + stage = 200 } else if (quickPay && !(inInventory(player!!, item!!.id, item!!.amount) || inInventory(player!!, note(item!!).id, note(item!!).amount))) { val amount = if (item?.amount == 1) "one" else item?.amount npc(FacialExpression.HAPPY, "I want $amount $protectionText for that.") stage = 200 + } else if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH && quickPay) { + showTopics( + //Found a 2011 source for quick-pay, but no earlier dialogue sources (https://www.youtube.com/watch?v=RdIcNH50v7I) + Topic("Yes", 20, true), + Topic("No", END_DIALOGUE, true), + title = "Pay the gnome?" + ) } else if (quickPay) { val amount = if (item?.amount == 1) "one" else item?.amount showTopics( @@ -62,6 +85,10 @@ class FarmerPayOptionDialogue(val patch: Patch, val quickPay: Boolean = false): Topic("No", END_DIALOGUE, true), title = "Pay $amount $protectionText?" ) + } else if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH) { + val amount = if (item?.amount == 1) "one" else item?.amount + npc("If you like, but I want $amount $protectionText,", "one monkey bar, and one ground tooth for that.") + stage++ } else { val amount = if (item?.amount == 1) "one" else item?.amount npc("If you like, but I want $amount $protectionText for that.") @@ -71,7 +98,9 @@ class FarmerPayOptionDialogue(val patch: Patch, val quickPay: Boolean = false): } 1 -> { - if (!(inInventory(player!!, item!!.id, item!!.amount) || inInventory(player!!, note(item!!).id, note(item!!).amount))) { + if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH && !hasAllItems()) { + player("I'm afraid I don't have any of those at the moment.").also { stage = 10 } + } else if (!(inInventory(player!!, item!!.id, item!!.amount) || inInventory(player!!, note(item!!).id, note(item!!).amount))) { player("I'm afraid I don't have any of those at the moment.").also { stage = 10 } } else { showTopics( @@ -84,7 +113,14 @@ class FarmerPayOptionDialogue(val patch: Patch, val quickPay: Boolean = false): 10 -> npc("Well, I'm not wasting my time for free.").also { stage = END_DIALOGUE } 20 -> { - if (removeItem(player!!, item) || removeItem(player!!, note(item!!))) { + if (patch.patch.type == PatchType.SPIRIT_TREE_PATCH + && (removeItem(player!!, item) || removeItem(player!!, note(item!!))) + && (removeItem(player!!, itemSecondary) || removeItem(player!!, note(itemSecondary!!))) + && (removeItem(player!!, itemTertiary) || removeItem(player!!, note(itemTertiary!!)))) { + patch.protectionPaid = true + npc("That'll do nicely, ${if (player!!.isMale) "sir" else "madam"}. Leave it with me - I'll make sure", "that tree grows up good and strong.").also { stage = END_DIALOGUE } + } else if (patch.patch.type != PatchType.SPIRIT_TREE_PATCH + && removeItem(player!!, item) || removeItem(player!!, note(item!!))) { patch.protectionPaid = true // Note: A slight change in this dialogue was seen in a December 2009 video - https://youtu.be/7gVh42ylQ48?t=138 npc("That'll do nicely, ${if (player!!.isMale) "sir" else "madam"}. Leave it with me - I'll make sure", "those crops grow for you.").also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/global/skill/farming/Farmers.kt b/Server/src/main/content/global/skill/farming/Farmers.kt index 4a57feeef..206eb254b 100644 --- a/Server/src/main/content/global/skill/farming/Farmers.kt +++ b/Server/src/main/content/global/skill/farming/Farmers.kt @@ -21,7 +21,11 @@ enum class Farmers(val id: Int, val patches: Array) { 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)); + TORRELL(2338, arrayOf(FarmingPatch.ARDOUGNE_BUSH)), + FRIZZY(4560, arrayOf(FarmingPatch.PORT_SARIM_SPIRIT_TREE)), + YULF(4561, arrayOf(FarmingPatch.ETCETERIA_SPIRIT_TREE)), + PRAISTAN(4562, arrayOf(FarmingPatch.KARAMJA_SPIRIT_TREE)) + ; companion object{ @JvmField diff --git a/Server/src/main/content/global/skill/farming/HealthChecker.kt b/Server/src/main/content/global/skill/farming/HealthChecker.kt index 5c819f1a3..23b53864b 100644 --- a/Server/src/main/content/global/skill/farming/HealthChecker.kt +++ b/Server/src/main/content/global/skill/farming/HealthChecker.kt @@ -26,7 +26,7 @@ class HealthChecker : OptionHandler() { val patch = fPatch.getPatchFor(player) val type = patch.patch.type - if (type != PatchType.BUSH_PATCH && type != PatchType.FRUIT_TREE_PATCH && type != PatchType.TREE_PATCH && type != PatchType.CACTUS_PATCH) { + if (!patch.isCheckable()) { sendMessage(player, "This shouldn't be happening. Please report this.") return true } @@ -44,6 +44,10 @@ class HealthChecker : OptionHandler() { patch.setCurrentState(patch.getCurrentState() - 14) sendMessage(player, "You examine the tree for signs of disease and find that it is in perfect health.") } + PatchType.SPIRIT_TREE_PATCH -> { + patch.setCurrentState(patch.getCurrentState() - 24) + sendMessage(player, "You examine the tree for signs of disease and find that it is in perfect health.") + } PatchType.BUSH_PATCH -> { patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 4) sendMessage(player, "You examine the bush for signs of disease and find that it's in perfect health.") diff --git a/Server/src/main/content/global/skill/farming/Patch.kt b/Server/src/main/content/global/skill/farming/Patch.kt index f8ce4dfce..b150b0590 100644 --- a/Server/src/main/content/global/skill/farming/Patch.kt +++ b/Server/src/main/content/global/skill/farming/Patch.kt @@ -16,6 +16,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl var compost = CompostType.NONE var protectionPaid = false var cropLives = 3 + val checkablePatches = arrayOf(PatchType.TREE_PATCH, PatchType.BUSH_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH, PatchType.CACTUS_PATCH) fun setNewHarvestAmount() { val compostMod = when(compost) { @@ -162,6 +163,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl PatchType.BUSH_PATCH -> setVarbit(player, patch.varbit, 250 + (plantable!!.ordinal - Plantable.REDBERRY_SEED.ordinal)) PatchType.CACTUS_PATCH -> setVarbit(player, patch.varbit, 31) PatchType.TREE_PATCH -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages) + PatchType.SPIRIT_TREE_PATCH -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages + 24) else -> log(this::class.java, Log.WARN, "Invalid setting of isCheckHealth for patch type: " + patch.type.name + "at" + patch.name) } } else { @@ -187,6 +189,10 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl if (state != getVarbit(player, patch.varbit)) setVisualState(state) } + PatchType.SPIRIT_TREE_PATCH -> { + if(isDead) setVisualState(getSpiritTreeDeathValue()) + else if(isDiseased && !isDead) setVisualState(getSpiritTreeDiseaseValue()) + } PatchType.FRUIT_TREE_PATCH -> { if(isDead) setVisualState(getFruitTreeDeathValue()) else if(isDiseased && !isDead) setVisualState(getFruitTreeDiseaseValue()) @@ -256,6 +262,14 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl } } + private fun getSpiritTreeDiseaseValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 12 + } + + private fun getSpiritTreeDeathValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 24 + } + private fun getFruitTreeDiseaseValue(): Int { return (plantable?.value ?: 0) + currentGrowthStage + 12 } @@ -338,7 +352,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl return } - 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){ + if(isCheckable() && plantable != null && plantable?.stages == currentGrowthStage + 1){ isCheckHealth = true } @@ -432,4 +446,8 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl (fpatch.plantable == plantable?.protectionFlower || fpatch.plantable == Plantable.forItemID(Items.WHITE_LILY_SEED_14589)) && fpatch.isGrown()) } + + fun isCheckable(): Boolean{ + return (patch.type in checkablePatches) + } } diff --git a/Server/src/main/content/global/skill/farming/Plantable.kt b/Server/src/main/content/global/skill/farming/Plantable.kt index ddb8069dd..1cf2ecdbd 100644 --- a/Server/src/main/content/global/skill/farming/Plantable.kt +++ b/Server/src/main/content/global/skill/farming/Plantable.kt @@ -3,7 +3,7 @@ package content.global.skill.farming import core.game.node.item.Item import org.rs09.consts.Items -enum class Plantable(val itemID: Int, val displayName: String, 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 displayName: String, 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, val protectionItemSecondary: Item? = null, val protectionItemTertiary: Item? = null) { // Flowers MARIGOLD_SEED(Items.MARIGOLD_SEED_5096,"marigold seed",8,4,8.5,47.0,0.0,2,PatchType.FLOWER_PATCH,Items.MARIGOLDS_6010), @@ -80,7 +80,8 @@ enum class Plantable(val itemID: Int, val displayName: String, val value: Int, v BELLADONNA_SEED(Items.BELLADONNA_SEED_5281, "belladonna seed", 4, 4, 91.0, 128.0, 0.0, 63, PatchType.BELLADONNA_PATCH, Items.CAVE_NIGHTSHADE_2398), MUSHROOM_SPORE(Items.MUSHROOM_SPORE_5282, "mushroom spore", 4, 6, 61.5, 57.7, 0.0, 53, PatchType.MUSHROOM_PATCH, Items.MUSHROOM_6004), CACTUS_SEED(Items.CACTUS_SEED_5280, "cactus seed", 8, 7, 66.5, 25.0, 374.0, 55, PatchType.CACTUS_PATCH, Items.CACTUS_SPINE_6016), - EVIL_TURNIP_SEED(Items.EVIL_TURNIP_SEED_12148, "evil turnip seed", 4, 1, 41.0, 46.0, 0.0, 42, PatchType.EVIL_TURNIP_PATCH, Items.EVIL_TURNIP_12134) + EVIL_TURNIP_SEED(Items.EVIL_TURNIP_SEED_12148, "evil turnip seed", 4, 1, 41.0, 46.0, 0.0, 42, PatchType.EVIL_TURNIP_PATCH, Items.EVIL_TURNIP_12134), + SPIRIT_SAPLING(Items.SPIRIT_SAPLING_5375, "spirit tree sapling", 8, 12, 199.5, 0.0, 19301.0, 83, PatchType.SPIRIT_TREE_PATCH, 0, Item(Items.MONKEY_NUTS_4012, 5), null, Item(Items.MONKEY_BAR_4014, 1), Item(Items.GROUND_TOOTH_9082, 1) ) ; constructor(itemID: Int, displayName: String, value: Int, stages: Int, plantingXP: Double, harvestXP: Double, checkHealthXP: Double, requiredLevel: Int, applicablePatch: PatchType, harvestItem: Int, protectionFlower: Plantable) diff --git a/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt b/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt index 538357dd0..eb0c5de69 100644 --- a/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt +++ b/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt @@ -38,6 +38,7 @@ class UseWithPatchHandler : InteractionListener { onUseWith(IntType.SCENERY, allowedNodes.toIntArray(), *FarmingPatch.patchNodes.toIntArray()) { player, used, with -> val patch = FarmingPatch.forObject(with.asScenery()) ?: return@onUseWith true val usedItem = used.asItem() + val trimmablePatches = arrayOf(PatchType.TREE_PATCH, PatchType.BUSH_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH) if (patch == FarmingPatch.TROLL_STRONGHOLD_HERB) { if (!hasRequirement(player, Quests.MY_ARMS_BIG_ADVENTURE)) @@ -79,7 +80,7 @@ class UseWithPatchHandler : InteractionListener { } SECATEURS, MAGIC_SECATEURS -> { val p = patch.getPatchFor(player) - if (patch.type == PatchType.TREE_PATCH) { + if (patch.type in trimmablePatches) { if (p.isDiseased && !p.isDead) { submitIndividualPulse(player, object: Pulse() { override fun pulse(): Boolean { @@ -131,7 +132,7 @@ class UseWithPatchHandler : InteractionListener { Items.PLANT_CURE_6036 -> { val p = patch.getPatchFor(player) val patchName = p.patch.type.displayName() - if (p.isDiseased && !p.isDead) { + if (p.isDiseased && !p.isDead && patch.type !in trimmablePatches) { sendMessage(player, "You treat the $patchName with the plant cure.") queueScript(player, 0, QueueStrength.WEAK) { stage: Int -> when (stage) { @@ -260,12 +261,17 @@ class UseWithPatchHandler : InteractionListener { }*/ val requiredItem = when (patch.type) { - PatchType.TREE_PATCH, PatchType.FRUIT_TREE_PATCH -> Items.SPADE_952 + PatchType.TREE_PATCH, PatchType.FRUIT_TREE_PATCH, PatchType.SPIRIT_TREE_PATCH -> Items.SPADE_952 PatchType.FLOWER_PATCH -> if (plantable == Plantable.SCARECROW) null else Items.SEED_DIBBER_5343 else -> Items.SEED_DIBBER_5343 } if (requiredItem != null && !inInventory(player, requiredItem)) { - sendMessage(player, "You need ${prependArticle(requiredItem.asItem().name.lowercase())} to plant that.") + sendDialogue(player, "You need ${prependArticle(requiredItem.asItem().name.lowercase())} to plant that.") + return@onUseWith true + } + val spiritTreePlanted = (getVarbit(player, 720) > 3 || getVarbit(player, 722) > 3 || getVarbit(player, 724) > 3) + if (plantable == Plantable.SPIRIT_SAPLING && spiritTreePlanted){ + sendDialogue(player, "You already have a spirit tree. You can't plant another one.") return@onUseWith true } @@ -278,7 +284,7 @@ class UseWithPatchHandler : InteractionListener { } animate(player, anim) playAudio(player, sound) - val delay = if (patch.type == PatchType.TREE_PATCH || patch.type == PatchType.FRUIT_TREE_PATCH || plantable == Plantable.SCARECROW) 3 else 0 + val delay = if (patch.type == PatchType.TREE_PATCH || patch.type == PatchType.FRUIT_TREE_PATCH || patch.type == PatchType.SPIRIT_TREE_PATCH || plantable == Plantable.SCARECROW) 3 else 0 return@queueScript delayScript(player,anim.duration + delay) } 1 -> { @@ -289,12 +295,12 @@ class UseWithPatchHandler : InteractionListener { p.plant(plantable) rewardXP(player, Skills.FARMING, plantable.plantingXP) p.setNewHarvestAmount() - if (p.patch.type == PatchType.TREE_PATCH || p.patch.type == PatchType.FRUIT_TREE_PATCH) { + if (p.patch.type == PatchType.TREE_PATCH || p.patch.type == PatchType.FRUIT_TREE_PATCH || p.patch.type == PatchType.SPIRIT_TREE_PATCH) { addItem(player, Items.PLANT_POT_5350) } val itemAmount = - if (p.patch.type == PatchType.TREE_PATCH || p.patch.type == PatchType.FRUIT_TREE_PATCH) "the" + if (p.patch.type == PatchType.TREE_PATCH || p.patch.type == PatchType.FRUIT_TREE_PATCH || p.patch.type == PatchType.SPIRIT_TREE_PATCH) "the" else if (plantItem.amount == 1) "a" else plantItem.amount val itemName = diff --git a/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt b/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt index 838473c20..2f6e9a7ed 100644 --- a/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt +++ b/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt @@ -1,9 +1,5 @@ package content.global.travel.trees -import core.api.isQuestComplete -import core.api.openDialogue -import core.api.sendDialogue -import core.api.teleport import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType @@ -18,9 +14,10 @@ import core.game.interaction.IntType import core.game.world.GameWorld.Pulser import core.tools.END_DIALOGUE import content.data.Quests +import core.api.* class GnomeSpiritTreeListener: InteractionListener { - val spiritTrees = intArrayOf(1317,1293,1294) + val spiritTrees = intArrayOf(1317,1293,1294,8355) override fun defineListeners() { on(spiritTrees, IntType.SCENERY, "talk-to"){ player, _ -> @@ -39,7 +36,11 @@ class GnomeSpiritTreeTeleportDialogue: DialogueFile() { Location(2542, 3170, 0), Location(2461, 3444, 0), Location(2556, 3259, 0), - Location(3184, 3508, 0) + Location(3184, 3508, 0), + Location(3060, 3256, 0), //Port Sarim + Location(2613, 3856, 0), //Etceteria + Location(2800, 3203, 0) //Brimhaven + ) private val ANIMATIONS = arrayOf(Animation(7082), Animation(7084)) @@ -86,6 +87,15 @@ class GnomeSpiritTreeTeleportDialogue: DialogueFile() { stage = END_DIALOGUE return } + + var plantedSpiritTreeLocation = when { + getVarbit(player!!, 720) == 20 -> "Port Sarim" + getVarbit(player!!, 722) == 20 -> "Etceteria" + getVarbit(player!!, 724) == 20 -> "Brimhaven" + else -> null + //There should never be a case where more than one spirit tree is planted. + } + if(plantedSpiritTreeLocation == null) { when (stage) { 0 -> interpreter!!.sendOptions( "Where would you like to go?", @@ -100,6 +110,29 @@ class GnomeSpiritTreeTeleportDialogue: DialogueFile() { 3 -> sendTeleport(player!!, LOCATIONS[2]) 4 -> sendTeleport(player!!, LOCATIONS[3]) } + } + } + else when (stage) { + 0 -> interpreter!!.sendOptions( + "Where would you like to go?", + "Tree Gnome Village", + "Tree Gnome Stronghold", + "Battlefield of Khazard", + "Grand Exchange", + plantedSpiritTreeLocation + ).also { stage++ } + 1 -> when (buttonID) { + 1 -> sendTeleport(player!!, LOCATIONS[0]) + 2 -> sendTeleport(player!!, LOCATIONS[1]) + 3 -> sendTeleport(player!!, LOCATIONS[2]) + 4 -> sendTeleport(player!!, LOCATIONS[3]) + 5 -> when (plantedSpiritTreeLocation) { + "Port Sarim" -> sendTeleport(player!!,LOCATIONS[4]) + "Etceteria" -> sendTeleport(player!!,LOCATIONS[5]) + "Brimhaven" -> sendTeleport(player!!,LOCATIONS[6]) + else -> stage = END_DIALOGUE + } + } } } }