Miscellaneous farming fixes

This commit is contained in:
Ceikry
2021-07-30 12:42:46 +00:00
parent 2210f8af85
commit aece20453c
11 changed files with 213 additions and 40 deletions
@@ -204,6 +204,7 @@ public final class DialogueInterpreter {
public boolean close() { public boolean close() {
if (dialogue != null || dialogueStage != null) { if (dialogue != null || dialogueStage != null) {
actions.clear(); actions.clear();
if (player.getInterfaceManager().getChatbox() != null && player.getInterfaceManager().getChatbox().getCloseEvent() != null) { if (player.getInterfaceManager().getChatbox() != null && player.getInterfaceManager().getChatbox().getCloseEvent() != null) {
return true; return true;
} }
@@ -211,8 +212,10 @@ public final class DialogueInterpreter {
dialogueStage = null; dialogueStage = null;
player.getInterfaceManager().closeChatbox(); player.getInterfaceManager().closeChatbox();
} }
if (dialogue != null && dialogue.close()) { if (dialogue != null) {
DialoguePlugin d = dialogue;
dialogue = null; dialogue = null;
d.close();
} }
} }
return dialogue == null && dialogueStage == null; return dialogue == null && dialogueStage == null;
@@ -122,6 +122,7 @@ public abstract class DialoguePlugin implements Plugin<Player> {
public boolean close() { public boolean close() {
player.getInterfaceManager().closeChatbox(); player.getInterfaceManager().closeChatbox();
player.getInterfaceManager().openChatbox(137); player.getInterfaceManager().openChatbox(137);
if(file != null) file.end();
finished = true; finished = true;
return true; return true;
} }
+22 -2
View File
@@ -167,6 +167,26 @@ object ContentAPI {
if(!player.inventory.add(item)) GroundItemManager.create(item,player) if(!player.inventory.add(item)) GroundItemManager.create(item,player)
} }
/**
* Clears an NPC with the "poof" smoke graphics commonly seen with random event NPCs.
* @param npc the NPC object to initialize
*/
@JvmStatic
fun poofClear(npc: NPC){
submitWorldPulse(object : Pulse(){
var counter = 0
override fun pulse(): Boolean {
when(counter++){
2 -> {
npc.isInvisible = true; Graphics.send(Graphics(86), npc.location)
}
3 -> npc.clear().also { return true }
}
return false
}
})
}
/** /**
* Check if an item exists in a player's bank * Check if an item exists in a player's bank
* @param player the player whose bank to check * @param player the player whose bank to check
@@ -546,8 +566,8 @@ object ContentAPI {
@JvmStatic @JvmStatic
fun openDialogue(player: Player, dialogue: Any, vararg args: Any) { fun openDialogue(player: Player, dialogue: Any, vararg args: Any) {
when (dialogue) { when (dialogue) {
is Int -> player.dialogueInterpreter.open(dialogue, args) is Int -> player.dialogueInterpreter.open(dialogue, *args)
is DialogueFile -> player.dialogueInterpreter.open(dialogue, args) is DialogueFile -> player.dialogueInterpreter.open(dialogue, *args)
else -> SystemLogger.logErr("Invalid object type passed to openDialogue() -> ${dialogue.javaClass.simpleName}") else -> SystemLogger.logErr("Invalid object type passed to openDialogue() -> ${dialogue.javaClass.simpleName}")
} }
} }
@@ -1,5 +1,6 @@
package rs09.game.content.ame package rs09.game.content.ame
import api.ContentAPI
import core.game.interaction.MovementPulse import core.game.interaction.MovementPulse
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
@@ -36,18 +37,7 @@ abstract class RandomEventNPC(id: Int) : NPC(id) {
open fun terminate(){ open fun terminate(){
player.antiMacroHandler.event = null player.antiMacroHandler.event = null
if(initialized){ if(initialized){
Pulser.submit(object : Pulse(){ ContentAPI.poofClear(this)
var counter = 0
override fun pulse(): Boolean {
when(counter++){
2 -> {
isInvisible = true; Graphics.send(SMOKE_GRAPHICS, location)
}
3 -> clear().also { return true }
}
return false
}
})
} }
} }
@@ -75,7 +75,7 @@ abstract class DialogueFile {
return player(expr, *splitLines(msg!!)) return player(expr, *splitLines(msg!!))
} }
fun end(){ open fun end(){
if(interpreter != null) interpreter!!.close() if(interpreter != null) interpreter!!.close()
} }
@@ -0,0 +1,98 @@
package rs09.game.content.zone
import api.ContentAPI
import core.game.content.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.system.task.Pulse
import core.game.world.map.zone.MapZone
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.ZoneBuilder
import core.plugin.Initializable
import core.plugin.Plugin
import org.rs09.consts.NPCs
import rs09.game.content.dialogue.DialogueFile
import rs09.tools.secondsToTicks
import java.util.concurrent.TimeUnit
@Initializable
class FarmingPatchZone : MapZone("farming patch", true), Plugin<Any> {
val playersInZone = hashMapOf<Player,Int>()
override fun configure() {
registerRegion(12083)
registerRegion(10548)
register(ZoneBorders(3594,3521,3608,3532))
ContentAPI.submitWorldPulse(zonePulse)
}
override fun newInstance(arg: Any?): Plugin<Any> {
ZoneBuilder.configure(this)
return this
}
override fun fireEvent(identifier: String?, vararg args: Any?): Any {
return Unit
}
override fun enter(e: Entity?): Boolean {
if(e is Player && playersInZone[e] == null && ContentAPI.getStatLevel(e, Skills.FARMING) <= 15) {
playersInZone[e] = 0
}
return super.enter(e)
}
override fun leave(e: Entity?, logout: Boolean): Boolean {
if(e is Player){
playersInZone.remove(e)
}
return super.enter(e)
}
val zonePulse = object : Pulse(){
override fun pulse(): Boolean {
playersInZone.toList().forEach { (player, ticks) ->
if(ticks == secondsToTicks(TimeUnit.MINUTES.toSeconds(5).toInt())){
val npc = NPC(NPCs.GITHAN_7122)
npc.location = player.location
npc.init()
npc.moveStep()
npc.face(player)
ContentAPI.openDialogue(player, SpiritDialogue(true), npc)
} else if (ticks == secondsToTicks(TimeUnit.MINUTES.toSeconds(10).toInt())){
val npc = NPC(NPCs.GITHAN_7122)
npc.location = player.location
npc.init()
npc.moveStep()
npc.face(player)
ContentAPI.openDialogue(player, SpiritDialogue(false), npc)
playersInZone.remove(player)
}
playersInZone[player] = ticks + 1
}
return false
}
}
internal class SpiritDialogue(val is5: Boolean) : DialogueFile(){
override fun handle(componentID: Int, buttonID: Int) {
when(stage){
0 -> {
if(is5) npcl(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(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()
ContentAPI.poofClear(npc ?: return)
}
}
}
@@ -1,5 +1,6 @@
package rs09.game.node.entity.skill.farming package rs09.game.node.entity.skill.farming
import api.ContentAPI
import core.cache.def.impl.SceneryDefinition import core.cache.def.impl.SceneryDefinition
import core.game.interaction.OptionHandler import core.game.interaction.OptionHandler
import core.game.node.Node import core.game.node.Node
@@ -66,12 +67,8 @@ class CropHarvester : OptionHandler() {
delay = 2 delay = 2
player.inventory.add(Item(plantable.harvestItem,1)) player.inventory.add(Item(plantable.harvestItem,1))
player.skills.addExperience(Skills.FARMING,plantable.harvestXP) player.skills.addExperience(Skills.FARMING,plantable.harvestXP)
patch.harvestAmt-- patch.rollLivesDecrement(ContentAPI.getDynLevel(player, Skills.FARMING), requiredItem == Items.MAGIC_SECATEURS_7409)
return patch.cropLives <= 0
if(patch.harvestAmt <= 0){
patch.clear()
}
return patch.harvestAmt <= 0
} }
}) })
@@ -4,6 +4,8 @@ import core.game.node.entity.player.Player
import core.tools.RandomFunction import core.tools.RandomFunction
import rs09.game.system.SystemLogger import rs09.game.system.SystemLogger
import java.util.concurrent.TimeUnit 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) { 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) constructor(player: Player, patch: FarmingPatch) : this(player,patch,null,0,false,false,false,0L,0,false)
@@ -11,22 +13,58 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
var diseaseMod = 0 var diseaseMod = 0
var compost = CompostType.NONE var compost = CompostType.NONE
var protectionPaid = false var protectionPaid = false
var cropLives = 3
fun setNewHarvestAmount() { fun setNewHarvestAmount() {
if(patch.type == PatchType.ALLOTMENT){ cropLives = 3
harvestAmt = RandomFunction.random(4,17) }
} else if(patch.type == PatchType.FLOWER || patch.type == PatchType.EVIL_TURNIP) {
harvestAmt = when (plantable) { fun rollLivesDecrement(farmingLevel: Int, magicSecateurs: Boolean){
Plantable.LIMPWURT_SEED, Plantable.WOAD_SEED -> 3 if(patch.type == PatchType.HERB){
else -> 1 //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 if(patch.type == PatchType.HOPS){
harvestAmt = RandomFunction.random(3,35)
} else { } else {
harvestAmt = RandomFunction.random(3,5) //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.CACTUS -> 3 //average of 3 per life times 3 lives = 9
else -> return
} }
if(compost == CompostType.NORMAL) harvestAmt += 1
if(compost == CompostType.SUPER) harvestAmt += 2 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 { fun isWeedy(): Boolean {
@@ -82,6 +120,11 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getCactusDeathValue()) if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getCactusDeathValue())
else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getCactusDiseaseValue()) 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 -> {} else -> {}
} }
} }
@@ -144,6 +187,22 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
return (plantable?.value ?: 0) + currentGrowthStage + 16 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(){ private fun grow(){
if(isWeedy() && getCurrentState() > 0) { if(isWeedy() && getCurrentState() > 0) {
nextGrowth = System.currentTimeMillis() + 60000 nextGrowth = System.currentTimeMillis() + 60000
@@ -164,8 +223,8 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
} }
if(RandomFunction.random(128) <= (17 - diseaseMod) && !isWatered && !isGrown() && !protectionPaid && !isFlowerProtected() && patch.type != PatchType.EVIL_TURNIP){ if(RandomFunction.random(128) <= (17 - diseaseMod) && !isWatered && !isGrown() && !protectionPaid && !isFlowerProtected() && patch.type != PatchType.EVIL_TURNIP){
//bush, tree, fruit tree and cactus can not disease on stage 1(0) of growth. //bush, tree, fruit tree, herb and cactus can not disease on stage 1(0) of growth.
if(!((patch.type == PatchType.BUSH || patch.type == PatchType.TREE || patch.type == PatchType.FRUIT_TREE || patch.type == PatchType.CACTUS) && currentGrowthStage == 0)) { 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 isDiseased = true
return return
} }
@@ -63,17 +63,18 @@ enum class Plantable(val itemID: Int, val value: Int, val stages: Int, val plant
MARRENTILL_SEED(5292,11,4,13.5,15.0,0.0,14,PatchType.HERB,Items.GRIMY_MARRENTILL_201), MARRENTILL_SEED(5292,11,4,13.5,15.0,0.0,14,PatchType.HERB,Items.GRIMY_MARRENTILL_201),
TARROMIN_SEED(5293,18,4,16.0,18.0,0.0,19,PatchType.HERB,Items.GRIMY_TARROMIN_203), TARROMIN_SEED(5293,18,4,16.0,18.0,0.0,19,PatchType.HERB,Items.GRIMY_TARROMIN_203),
HARRALANDER_SEED(5294,25,4,21.5,24.0,0.0,26,PatchType.HERB,Items.GRIMY_HARRALANDER_205), HARRALANDER_SEED(5294,25,4,21.5,24.0,0.0,26,PatchType.HERB,Items.GRIMY_HARRALANDER_205),
GOUT_TUBER(6311,32,4,105.0,45.0,0.0,29,PatchType.HERB,Items.GOUTWEED_3261), RANARR_SEED(5295,32,4,27.0,30.5,0.0,32,PatchType.HERB,Items.GRIMY_RANARR_207),
RANARR_SEED(5295,39,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,47,4,34.0,38.5,0.0,38,PatchType.HERB,Items.GRIMY_TOADFLAX_3049), TOADFLAX_SEED(5296,46,4,34.0,38.5,0.0,38,PatchType.HERB,Items.GRIMY_TOADFLAX_3049),
IRIT_SEED(5297,54,4,43.0,48.5,0.0,44,PatchType.HERB,Items.GRIMY_IRIT_209), IRIT_SEED(5297,53,4,43.0,48.5,0.0,44,PatchType.HERB,Items.GRIMY_IRIT_209),
AVANTOE_SEED(5298,204,4,54.5,61.5,0.0,50,PatchType.HERB,Items.GRIMY_AVANTOE_211),
KWUARM_SEED(5299,68,4,69.0,78.0,0.0,56,PatchType.HERB,Items.GRIMY_KWUARM_213), KWUARM_SEED(5299,68,4,69.0,78.0,0.0,56,PatchType.HERB,Items.GRIMY_KWUARM_213),
SNAPDRAGON_SEED(5300,75,4,87.5,98.5,0.0,62,PatchType.HERB,Items.GRIMY_SNAPDRAGON_3051), SNAPDRAGON_SEED(5300,75,4,87.5,98.5,0.0,62,PatchType.HERB,Items.GRIMY_SNAPDRAGON_3051),
CADANTINE_SEED(5301,82,4,106.5,120.0,0.0,67,PatchType.HERB,Items.GRIMY_CADANTINE_215), CADANTINE_SEED(5301,82,4,106.5,120.0,0.0,67,PatchType.HERB,Items.GRIMY_CADANTINE_215),
LANTADYME_SEED(5302,89,4,134.5,151.5,0.0,73,PatchType.HERB,Items.GRIMY_LANTADYME_2485), LANTADYME_SEED(5302,89,4,134.5,151.5,0.0,73,PatchType.HERB,Items.GRIMY_LANTADYME_2485),
DWARF_WEED_SEED(5303,96,4,170.5,192.0,0.0,79,PatchType.HERB,Items.GRIMY_DWARF_WEED_217), DWARF_WEED_SEED(5303,96,4,170.5,192.0,0.0,79,PatchType.HERB,Items.GRIMY_DWARF_WEED_217),
TORSTOL_SEED(5304,103,4,199.5,224.5,0.0,85,PatchType.HERB,Items.GRIMY_TORSTOL_219), TORSTOL_SEED(5304,103,4,199.5,224.5,0.0,85,PatchType.HERB,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 //Other
BELLADONNA_SEED(5281, 4, 4, 91.0, 128.0, 0.0, 63, PatchType.BELLADONNA, Items.CAVE_NIGHTSHADE_2398), BELLADONNA_SEED(5281, 4, 4, 91.0, 128.0, 0.0, 63, PatchType.BELLADONNA, Items.CAVE_NIGHTSHADE_2398),
@@ -128,6 +128,7 @@ object UseWithPatchHandler{
if(p.plantable != null){ if(p.plantable != null){
p.harvestAmt += if(p.compost == CompostType.NORMAL) 1 else if(p.compost == CompostType.SUPER) 2 else 0 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)) player.inventory.add(Item(Items.BUCKET_1925))
} }
return true return true
@@ -50,6 +50,7 @@ class FarmingState(player: Player? = null) : State(player) {
p.put("patch-checkHealth",patch.isCheckHealth) p.put("patch-checkHealth",patch.isCheckHealth)
p.put("patch-compost",patch.compost.ordinal) p.put("patch-compost",patch.compost.ordinal)
p.put("patch-paidprot",patch.protectionPaid) p.put("patch-paidprot",patch.protectionPaid)
p.put("patch-croplives", patch.cropLives)
patches.add(p) patches.add(p)
} }
val bins = JSONArray() val bins = JSONArray()
@@ -91,10 +92,12 @@ class FarmingState(player: Player? = null) : State(player) {
val savedState = p["patch-state"].toString().toInt() val savedState = p["patch-state"].toString().toInt()
val compostOrdinal = p["patch-compost"].toString().toInt() val compostOrdinal = p["patch-compost"].toString().toInt()
val protectionPaid = p["patch-paidprot"] as Boolean 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 fPatch = FarmingPatch.values()[patchOrdinal]
val plantable = if(patchPlantableOrdinal != -1) Plantable.values()[patchPlantableOrdinal] else null val plantable = if(patchPlantableOrdinal != -1) Plantable.values()[patchPlantableOrdinal] else null
val patch = Patch(player,fPatch,plantable,patchStage,patchDiseased,patchDead,patchWatered,nextGrowth,harvestAmt,checkHealth) val patch = Patch(player,fPatch,plantable,patchStage,patchDiseased,patchDead,patchWatered,nextGrowth,harvestAmt,checkHealth)
patch.cropLives = cropLives
patch.compost = CompostType.values()[compostOrdinal] patch.compost = CompostType.values()[compostOrdinal]
patch.protectionPaid = protectionPaid patch.protectionPaid = protectionPaid
patch.setCurrentState(savedState) patch.setCurrentState(savedState)