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 {
|
||||
@JvmField
|
||||
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
|
||||
fun forObject(obj: Scenery): FarmingPatch?{
|
||||
@@ -85,6 +112,10 @@ enum class FarmingPatch(val varbit: Int, val type: PatchType) {
|
||||
val objDef = SceneryDefinition.forId(id)
|
||||
return patches[objDef.varbitID]
|
||||
}
|
||||
|
||||
fun getSceneryDefByVarbit (id: Int) : SceneryDefinition? {
|
||||
return nodeMap[id]
|
||||
}
|
||||
}
|
||||
|
||||
fun getPatchFor(player: Player): Patch{
|
||||
|
||||
@@ -98,6 +98,35 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
||||
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 {
|
||||
return compost != CompostType.NONE
|
||||
}
|
||||
@@ -106,7 +135,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl
|
||||
return currentGrowthStage == (plantable?.stages ?: 0)
|
||||
}
|
||||
|
||||
private fun updateBit(){
|
||||
fun updateBit(){
|
||||
if(isCheckHealth){
|
||||
when(patch.type){
|
||||
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 (state != getVarbit(player, patch.varbit))
|
||||
setVarbit(player, patch.varbit, state)
|
||||
setVisualState(state)
|
||||
}
|
||||
PatchType.BUSH -> {
|
||||
if(isDead) setVarbit(player, patch.varbit, getBushDeathValue())
|
||||
else if(isDiseased && !isDead) setVarbit(player, patch.varbit, getBushDiseaseValue())
|
||||
if(isDead) setVisualState(getBushDeathValue())
|
||||
else if(isDiseased && !isDead) setVisualState(getBushDiseaseValue())
|
||||
}
|
||||
PatchType.TREE -> {
|
||||
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
|
||||
|
||||
if (state != getVarbit(player, patch.varbit))
|
||||
setVarbit(player, patch.varbit, state)
|
||||
setVisualState(state)
|
||||
}
|
||||
PatchType.FRUIT_TREE -> {
|
||||
if(isDead) setVarbit(player, patch.varbit, getFruitTreeDeathValue())
|
||||
else if(isDiseased && !isDead) setVarbit(player, patch.varbit, getFruitTreeDiseaseValue())
|
||||
if(isDead) setVisualState(getFruitTreeDeathValue())
|
||||
else if(isDiseased && !isDead) setVisualState(getFruitTreeDiseaseValue())
|
||||
}
|
||||
PatchType.BELLADONNA -> {
|
||||
if(isDead) setVarbit(player, patch.varbit, getBelladonnaDeathValue())
|
||||
else if(isDiseased && !isDead) setVarbit(player, patch.varbit, getBelladonnaDiseaseValue())
|
||||
else setVarbit(player, patch.varbit, (plantable?.value ?: 0) + currentGrowthStage)
|
||||
if(isDead) setVisualState(getBelladonnaDeathValue())
|
||||
else if(isDiseased && !isDead) setVisualState(getBelladonnaDiseaseValue())
|
||||
else setVisualState((plantable?.value ?: 0) + currentGrowthStage)
|
||||
}
|
||||
PatchType.CACTUS -> {
|
||||
if(isDead) setVarbit(player, patch.varbit, getCactusDeathValue())
|
||||
else if(isDiseased && !isDead) setVarbit(player, patch.varbit, getCactusDiseaseValue())
|
||||
if(isDead) setVisualState(getCactusDeathValue())
|
||||
else if(isDiseased && !isDead) setVisualState(getCactusDiseaseValue())
|
||||
}
|
||||
PatchType.HERB -> {
|
||||
if(isDead) setVarbit(player, patch.varbit, getHerbDeathValue())
|
||||
else if(isDiseased && !isDead) setVarbit(player, patch.varbit, getHerbDiseaseValue())
|
||||
else setVarbit(player, patch.varbit, (plantable?.value ?: 0) + currentGrowthStage)
|
||||
if(isDead) setVisualState(getHerbDeathValue())
|
||||
else if(isDiseased && !isDead) setVisualState(getHerbDiseaseValue())
|
||||
else setVisualState((plantable?.value ?: 0) + currentGrowthStage)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,10 @@ class UseWithPatchHandler : InteractionListener {
|
||||
|
||||
@JvmField
|
||||
val allowedNodes = ArrayList<Int>()
|
||||
val patches = ArrayList<Int>()
|
||||
|
||||
override fun defineListeners() {
|
||||
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 usedItem = used.asItem()
|
||||
|
||||
@@ -233,23 +232,6 @@ class UseWithPatchHandler : InteractionListener {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
+11
-3
@@ -896,15 +896,23 @@ public class SceneryDefinition extends Definition<Scenery> {
|
||||
} else {
|
||||
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];
|
||||
if (objectId != -1) {
|
||||
return forId(objectId);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
forId(childrenIds[configValue]).configFileId = this.configFileId;
|
||||
return forId(childrenIds[configValue]);
|
||||
return forId(childrenIds[index]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import content.global.ame.RandomEventManager
|
||||
import content.global.skill.farming.timers.CropGrowth
|
||||
import core.cache.Cache
|
||||
import core.cache.crypto.ISAACCipher
|
||||
import core.cache.crypto.ISAACPair
|
||||
@@ -83,6 +84,7 @@ object TestUtils {
|
||||
fun registerTimers() { //allow timers to be registered for use by tests
|
||||
TimerRegistry.registerTimer(Poison())
|
||||
TimerRegistry.registerTimer(Disease())
|
||||
TimerRegistry.registerTimer(CropGrowth())
|
||||
}
|
||||
|
||||
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