Added farming patch safety checks
This commit is contained in:
@@ -74,6 +74,33 @@ enum class FarmingPatch(val varbit: Int, val type: PatchType) {
|
|||||||
companion object {
|
companion object {
|
||||||
@JvmField
|
@JvmField
|
||||||
val patches = FarmingPatch.values().map { it.varbit to it }.toMap()
|
val patches = FarmingPatch.values().map { it.varbit to it }.toMap()
|
||||||
|
val patchNodes = ArrayList<Int>()
|
||||||
|
val nodeMap = HashMap<Int, SceneryDefinition>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
patchNodes.addAll(8550..8557) //allotment wrappers
|
||||||
|
patchNodes.addAll(7847..7853) //flower patch wrappers
|
||||||
|
patchNodes.addAll(8150..8156) //herb patch wrappers
|
||||||
|
patchNodes.addAll(8388..8391) // Tree patches
|
||||||
|
patchNodes.add(19147) //Tree patch
|
||||||
|
patchNodes.addAll(7962..7965) //fruit trees
|
||||||
|
patchNodes.addAll(8173..8176) //hops
|
||||||
|
patchNodes.addAll(7577..7580) //bush
|
||||||
|
patchNodes.add(23760) //evil turnip
|
||||||
|
patchNodes.add(7572) //belladonna
|
||||||
|
patchNodes.add(8337) //mushroom
|
||||||
|
patchNodes.add(27197) //jade vine
|
||||||
|
patchNodes.add(7771) //cactus
|
||||||
|
patchNodes.add(7807) //calquat
|
||||||
|
patchNodes.addAll(8382..8383)//spirit trees
|
||||||
|
patchNodes.add(8338) //spirit tree
|
||||||
|
patchNodes.add(18816) //death plateau wrapper
|
||||||
|
|
||||||
|
for (patch in patchNodes) {
|
||||||
|
val def = SceneryDefinition.forId(patch)
|
||||||
|
nodeMap[def.varbitID] = def
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun forObject(obj: Scenery): FarmingPatch?{
|
fun forObject(obj: Scenery): FarmingPatch?{
|
||||||
@@ -85,6 +112,10 @@ enum class FarmingPatch(val varbit: Int, val type: PatchType) {
|
|||||||
val objDef = SceneryDefinition.forId(id)
|
val objDef = SceneryDefinition.forId(id)
|
||||||
return patches[objDef.varbitID]
|
return patches[objDef.varbitID]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSceneryDefByVarbit (id: Int) : SceneryDefinition? {
|
||||||
|
return nodeMap[id]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPatchFor(player: Player): Patch{
|
fun getPatchFor(player: Player): Patch{
|
||||||
|
|||||||
@@ -98,6 +98,35 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
|||||||
updateBit()
|
updateBit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setVisualState (state: Int) {
|
||||||
|
val finalState = ensureStateSanity(state)
|
||||||
|
setVarbit(player, patch.varbit, finalState)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ensureStateSanity (state: Int) : Int {
|
||||||
|
val patchDef = FarmingPatch.getSceneryDefByVarbit(patch.varbit) ?: return state
|
||||||
|
val currentStateDef = patchDef.getChildObjectAtIndex(state)
|
||||||
|
if (currentStateDef.name == patchDef.getChildObjectAtIndex(3).name) { //if we're weedy
|
||||||
|
if (state and 0x40 != 0) { //if this invalid state was caused by water/death
|
||||||
|
//remove water/death
|
||||||
|
isDead = false
|
||||||
|
isWatered = false
|
||||||
|
log(this::class.java, Log.DEBUG, "Patch for ${player.username} at varbit ${patch.varbit} with plantable ${plantable?.name ?: "none"} was set to watered/dead at stage $currentGrowthStage, which isn't valid.")
|
||||||
|
return (state and (0x40.inv()))
|
||||||
|
}
|
||||||
|
else if (state and 0x80 != 0) { //if this invalid state was caused by disease
|
||||||
|
//remove disease
|
||||||
|
isDiseased = false
|
||||||
|
log(this::class.java, Log.DEBUG, "Patch for ${player.username} at varbit ${patch.varbit} with plantable ${plantable?.name ?: "none"} was set to diseased at stage $currentGrowthStage, which isn't valid.")
|
||||||
|
return (state and (0x80.inv()))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log (this::class.java, Log.ERR, "Patch for ${player.username} at varbit ${patch.varbit} with plantable ${plantable?.name ?: "none"} was set to state $state at growth stage $currentGrowthStage, which isn't valid. We're not sure why this is happening.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
fun isFertilized(): Boolean {
|
fun isFertilized(): Boolean {
|
||||||
return compost != CompostType.NONE
|
return compost != CompostType.NONE
|
||||||
}
|
}
|
||||||
@@ -106,7 +135,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
|||||||
return currentGrowthStage == (plantable?.stages ?: 0)
|
return currentGrowthStage == (plantable?.stages ?: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateBit(){
|
fun updateBit(){
|
||||||
if(isCheckHealth){
|
if(isCheckHealth){
|
||||||
when(patch.type){
|
when(patch.type){
|
||||||
PatchType.FRUIT_TREE -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages + 20)
|
PatchType.FRUIT_TREE -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages + 20)
|
||||||
@@ -122,11 +151,11 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
|||||||
if (isDiseased) state = state or 0x80
|
if (isDiseased) state = state or 0x80
|
||||||
|
|
||||||
if (state != getVarbit(player, patch.varbit))
|
if (state != getVarbit(player, patch.varbit))
|
||||||
setVarbit(player, patch.varbit, state)
|
setVisualState(state)
|
||||||
}
|
}
|
||||||
PatchType.BUSH -> {
|
PatchType.BUSH -> {
|
||||||
if(isDead) setVarbit(player, patch.varbit, getBushDeathValue())
|
if(isDead) setVisualState(getBushDeathValue())
|
||||||
else if(isDiseased && !isDead) setVarbit(player, patch.varbit, getBushDiseaseValue())
|
else if(isDiseased && !isDead) setVisualState(getBushDiseaseValue())
|
||||||
}
|
}
|
||||||
PatchType.TREE -> {
|
PatchType.TREE -> {
|
||||||
var state = getVarbit(player, patch.varbit)
|
var state = getVarbit(player, patch.varbit)
|
||||||
@@ -135,25 +164,25 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
|||||||
else if (isDiseased) state = state or 0x40
|
else if (isDiseased) state = state or 0x40
|
||||||
|
|
||||||
if (state != getVarbit(player, patch.varbit))
|
if (state != getVarbit(player, patch.varbit))
|
||||||
setVarbit(player, patch.varbit, state)
|
setVisualState(state)
|
||||||
}
|
}
|
||||||
PatchType.FRUIT_TREE -> {
|
PatchType.FRUIT_TREE -> {
|
||||||
if(isDead) setVarbit(player, patch.varbit, getFruitTreeDeathValue())
|
if(isDead) setVisualState(getFruitTreeDeathValue())
|
||||||
else if(isDiseased && !isDead) setVarbit(player, patch.varbit, getFruitTreeDiseaseValue())
|
else if(isDiseased && !isDead) setVisualState(getFruitTreeDiseaseValue())
|
||||||
}
|
}
|
||||||
PatchType.BELLADONNA -> {
|
PatchType.BELLADONNA -> {
|
||||||
if(isDead) setVarbit(player, patch.varbit, getBelladonnaDeathValue())
|
if(isDead) setVisualState(getBelladonnaDeathValue())
|
||||||
else if(isDiseased && !isDead) setVarbit(player, patch.varbit, getBelladonnaDiseaseValue())
|
else if(isDiseased && !isDead) setVisualState(getBelladonnaDiseaseValue())
|
||||||
else setVarbit(player, patch.varbit, (plantable?.value ?: 0) + currentGrowthStage)
|
else setVisualState((plantable?.value ?: 0) + currentGrowthStage)
|
||||||
}
|
}
|
||||||
PatchType.CACTUS -> {
|
PatchType.CACTUS -> {
|
||||||
if(isDead) setVarbit(player, patch.varbit, getCactusDeathValue())
|
if(isDead) setVisualState(getCactusDeathValue())
|
||||||
else if(isDiseased && !isDead) setVarbit(player, patch.varbit, getCactusDiseaseValue())
|
else if(isDiseased && !isDead) setVisualState(getCactusDiseaseValue())
|
||||||
}
|
}
|
||||||
PatchType.HERB -> {
|
PatchType.HERB -> {
|
||||||
if(isDead) setVarbit(player, patch.varbit, getHerbDeathValue())
|
if(isDead) setVisualState(getHerbDeathValue())
|
||||||
else if(isDiseased && !isDead) setVarbit(player, patch.varbit, getHerbDiseaseValue())
|
else if(isDiseased && !isDead) setVisualState(getHerbDiseaseValue())
|
||||||
else setVarbit(player, patch.varbit, (plantable?.value ?: 0) + currentGrowthStage)
|
else setVisualState((plantable?.value ?: 0) + currentGrowthStage)
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,10 @@ class UseWithPatchHandler : InteractionListener {
|
|||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val allowedNodes = ArrayList<Int>()
|
val allowedNodes = ArrayList<Int>()
|
||||||
val patches = ArrayList<Int>()
|
|
||||||
|
|
||||||
override fun defineListeners() {
|
override fun defineListeners() {
|
||||||
loadNodes()
|
loadNodes()
|
||||||
onUseWith(IntType.SCENERY, allowedNodes.toIntArray(), *patches.toIntArray()) { player, used, with ->
|
onUseWith(IntType.SCENERY, allowedNodes.toIntArray(), *FarmingPatch.patchNodes.toIntArray()) { player, used, with ->
|
||||||
val patch = FarmingPatch.forObject(with.asScenery()) ?: return@onUseWith true
|
val patch = FarmingPatch.forObject(with.asScenery()) ?: return@onUseWith true
|
||||||
val usedItem = used.asItem()
|
val usedItem = used.asItem()
|
||||||
|
|
||||||
@@ -233,23 +232,6 @@ class UseWithPatchHandler : InteractionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun loadNodes(){
|
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()){
|
for(p in Plantable.values()){
|
||||||
allowedNodes.add(p.itemID)
|
allowedNodes.add(p.itemID)
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-3
@@ -896,15 +896,23 @@ public class SceneryDefinition extends Definition<Scenery> {
|
|||||||
} else {
|
} else {
|
||||||
configValue = 0;
|
configValue = 0;
|
||||||
}
|
}
|
||||||
if (configValue < 0 || configValue >= childrenIds.length - 1 || childrenIds[configValue] == -1) {
|
SceneryDefinition childDef = getChildObjectAtIndex(configValue);
|
||||||
|
if (childDef != null) childDef.configFileId = this.configFileId;
|
||||||
|
return childDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SceneryDefinition getChildObjectAtIndex (int index) {
|
||||||
|
if (childrenIds == null || childrenIds.length < 1) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (index < 0 || index >= childrenIds.length - 1 || childrenIds[index] == -1) {
|
||||||
int objectId = childrenIds[childrenIds.length - 1];
|
int objectId = childrenIds[childrenIds.length - 1];
|
||||||
if (objectId != -1) {
|
if (objectId != -1) {
|
||||||
return forId(objectId);
|
return forId(objectId);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
forId(childrenIds[configValue]).configFileId = this.configFileId;
|
return forId(childrenIds[index]);
|
||||||
return forId(childrenIds[configValue]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import content.global.ame.RandomEventManager
|
import content.global.ame.RandomEventManager
|
||||||
|
import content.global.skill.farming.timers.CropGrowth
|
||||||
import core.cache.Cache
|
import core.cache.Cache
|
||||||
import core.cache.crypto.ISAACCipher
|
import core.cache.crypto.ISAACCipher
|
||||||
import core.cache.crypto.ISAACPair
|
import core.cache.crypto.ISAACPair
|
||||||
@@ -83,6 +84,7 @@ object TestUtils {
|
|||||||
fun registerTimers() { //allow timers to be registered for use by tests
|
fun registerTimers() { //allow timers to be registered for use by tests
|
||||||
TimerRegistry.registerTimer(Poison())
|
TimerRegistry.registerTimer(Poison())
|
||||||
TimerRegistry.registerTimer(Disease())
|
TimerRegistry.registerTimer(Disease())
|
||||||
|
TimerRegistry.registerTimer(CropGrowth())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadFile(path: String) : URI? {
|
fun loadFile(path: String) : URI? {
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package content.skill.farming
|
||||||
|
|
||||||
|
import TestUtils
|
||||||
|
import content.global.skill.farming.FarmingPatch
|
||||||
|
import content.global.skill.farming.PatchRaker
|
||||||
|
import content.global.skill.farming.PatchType
|
||||||
|
import content.global.skill.farming.Plantable
|
||||||
|
import content.global.skill.farming.timers.CropGrowth
|
||||||
|
import core.cache.def.impl.SceneryDefinition
|
||||||
|
import core.game.node.entity.skill.Skills
|
||||||
|
import core.game.system.timer.TimerRegistry
|
||||||
|
import org.junit.jupiter.api.Assertions
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class PatchTests {
|
||||||
|
init { TestUtils.preTestSetup() }
|
||||||
|
@Test fun patchGettingDiseasedOrDyingShouldNotGoIntoInvalidState() {
|
||||||
|
//Dead/Watered (state | 0x40) and Diseased (state | 0x80) are both invalid stages for limpwurt when fully grown (stage 4/state 32), so we use this as our testcase
|
||||||
|
//specifically, limpwurt has an issue when the patch is ONLY watered (state | 0x40) OR diseased (state | 0x80), having both (state | 0x40 | 0x80) is fine.
|
||||||
|
TestUtils.getMockPlayer("invalidPatchState").use {p ->
|
||||||
|
p.skills.setStaticLevel(Skills.FARMING, 99)
|
||||||
|
p.skills.setLevel(Skills.FARMING, 99)
|
||||||
|
|
||||||
|
val patch = FarmingPatch.S_FALADOR_FLOWER_C.getPatchFor(p)
|
||||||
|
patch.plant(Plantable.LIMPWURT_SEED)
|
||||||
|
|
||||||
|
for (i in 0 until 4) { //grow to full growth while making sure we don't die/get diseased
|
||||||
|
patch.currentGrowthStage = 4
|
||||||
|
patch.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
Assertions.assertEquals(32, patch.getCurrentState())
|
||||||
|
|
||||||
|
patch.isWatered = true
|
||||||
|
patch.update()
|
||||||
|
Assertions.assertEquals(false, patch.isDiseased)
|
||||||
|
Assertions.assertEquals(false, patch.isDead)
|
||||||
|
Assertions.assertEquals(false, patch.isWatered)
|
||||||
|
Assertions.assertEquals(32, patch.getCurrentState())
|
||||||
|
|
||||||
|
patch.isDiseased = true
|
||||||
|
patch.updateBit()
|
||||||
|
Assertions.assertEquals(false, patch.isDiseased)
|
||||||
|
Assertions.assertEquals(false, patch.isDead)
|
||||||
|
Assertions.assertEquals(false, patch.isWatered)
|
||||||
|
Assertions.assertEquals(32, patch.getCurrentState())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun rakingPatchAtInvalidStagesShouldDoNothing() {
|
||||||
|
TestUtils.getMockPlayer("Patchraker").use {p ->
|
||||||
|
val patch = FarmingPatch.S_FALADOR_FLOWER_C.getPatchFor(p)
|
||||||
|
patch.update()
|
||||||
|
|
||||||
|
Assertions.assertEquals(0, patch.getCurrentState()) //assert that it's in the max weedy state
|
||||||
|
|
||||||
|
PatchRaker.rake(p, patch.patch)
|
||||||
|
TestUtils.advanceTicks(25, false)
|
||||||
|
Assertions.assertEquals(3, patch.getCurrentState())
|
||||||
|
|
||||||
|
PatchRaker.rake(p, patch.patch)
|
||||||
|
TestUtils.advanceTicks(10, false)
|
||||||
|
Assertions.assertEquals(3, patch.getCurrentState())
|
||||||
|
|
||||||
|
patch.plant(Plantable.POTATO_SEED)
|
||||||
|
val plantedBaseline = patch.getCurrentState()
|
||||||
|
PatchRaker.rake(p, patch.patch)
|
||||||
|
TestUtils.advanceTicks(10, false)
|
||||||
|
Assertions.assertEquals(plantedBaseline, patch.getCurrentState())
|
||||||
|
|
||||||
|
patch.currentGrowthStage = Plantable.POTATO_SEED.stages
|
||||||
|
patch.update()
|
||||||
|
Assertions.assertEquals(true, patch.isGrown())
|
||||||
|
|
||||||
|
val stateBaseline = patch.getCurrentState()
|
||||||
|
PatchRaker.rake(p, patch.patch)
|
||||||
|
TestUtils.advanceTicks(10, false)
|
||||||
|
Assertions.assertEquals(stateBaseline, patch.getCurrentState())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user