Rewrote blast furnace

This commit is contained in:
Ceikry
2023-08-27 13:35:14 +00:00
committed by Ryan
parent e9fa9d0d41
commit 50dce880c1
21 changed files with 1825 additions and 886 deletions
@@ -0,0 +1,61 @@
package content.minigame.blastfurnace
import core.api.animate
import core.api.queueScript
import core.api.teleport
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.world.map.Direction
import core.game.world.map.Location
import org.json.simple.JSONObject
class BFBeltOre (val player: Player, val id: Int, val amount: Int, var location: Location, var npcInstance: NPC? = null) {
val state = BlastFurnace.getPlayerState(player)
fun tick(): Boolean {
if (location == ORE_END_LOC && npcInstance != null) {
state.container.addOre(id, amount)
npcInstance?.clear()
npcInstance = null
return true
}
else if (npcInstance != null) {
location = location.transform(Direction.SOUTH, 1)
teleport(npcInstance!!, location)
if (location == ORE_END_LOC) {
val npc = npcInstance!!
queueScript(npc, 1, QueueStrength.STRONG) {_ ->
animate(npc, ORE_DEPOSIT_ANIM)
return@queueScript true
}
}
}
return false
}
fun createNpc() {
if (npcInstance != null) return
val npcId = BlastFurnace.getNpcForOre(id)
val npc = NPC.create(npcId, location)
npc.isWalks = false
npc.init()
npcInstance = npc
}
fun toJson(): JSONObject {
val root = JSONObject()
root["id"] = id.toString()
root["amount"] = amount.toString()
root["location"] = location.toString()
return root
}
companion object {
val ORE_START_LOC = Location.create(1942, 4966, 0)
val ORE_END_LOC = Location.create(1942, 4963, 0)
val ORE_DEPOSIT_ANIM = 2434
}
}
@@ -0,0 +1,194 @@
package content.minigame.blastfurnace
import content.global.skill.smithing.smelting.Bar
import content.minigame.blastfurnace.BlastConsts.BAR_LIMIT
import content.minigame.blastfurnace.BlastFurnace.Companion.getBarForOreId
import content.minigame.blastfurnace.BlastFurnace.Companion.getNeededCoal
import core.game.node.item.Item
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import org.rs09.consts.Items
class BFOreContainer {
private var coalRemaining = 0 //this is the actual important value needed for the calculations and is unit tested
private var ores = Array(BlastConsts.ORE_LIMIT * 2) {-1}
private var barAmounts = Array(9) {0}
fun addCoal(amount: Int) : Int {
val maxAdd = BlastConsts.COAL_LIMIT - coalRemaining
val toAdd = amount.coerceAtMost(maxAdd)
coalRemaining += toAdd
return amount - toAdd
}
fun coalAmount() : Int {
return coalRemaining
}
fun addOre (id: Int, amount: Int): Int {
if (id == Items.COAL_453)
return addCoal(amount)
var limit = BlastConsts.ORE_LIMIT
if (getBarForOreId(id, -1, 99) == Bar.BRONZE)
limit *= 2
var amountLeft = amount
var maxAdd = getAvailableSpace(id, 99)
for (i in 0 until limit) {
if (ores[i] == -1) {
ores[i] = id
if (--amountLeft == 0 || --maxAdd == 0) break
}
}
return amountLeft
}
fun getOreAmount (id: Int) : Int {
if (id == Items.COAL_453)
return coalAmount()
var oreCount = 0
for (i in 0 until BlastConsts.ORE_LIMIT) {
if (ores[i] == id) oreCount++
}
return oreCount
}
fun indexOfOre (id: Int) : Int {
for (i in ores.indices)
if (ores[i] == id) return i
return -1
}
fun getOreAmounts() : HashMap<Int, Int> {
val map = HashMap<Int,Int>()
for (ore in ores) {
if (ore == -1) break
map[ore] = (map[ore] ?: 0) + 1
}
return map
}
fun convertToBars(level: Int = 99) : Double {
val newOres = Array(BlastConsts.ORE_LIMIT * 2) {-1}
var oreIndex = 0
var xpReward = 0.0
for (i in 0 until BlastConsts.ORE_LIMIT) {
val bar = getBarForOreId(ores[i], coalRemaining, level)
if (bar == null) {
ores[i] = -1
continue
}
if (barAmounts[bar.ordinal] >= BAR_LIMIT) {
newOres[oreIndex++] = ores[i]
continue
}
val coalNeeded = getNeededCoal(bar)
//special handling for bronze bar edge case, no other ore does this.
if (bar == Bar.BRONZE) {
val indexOfComplement = when (ores[i]) {
Items.COPPER_ORE_436 -> indexOfOre(Items.TIN_ORE_438)
Items.TIN_ORE_438 -> indexOfOre(Items.COPPER_ORE_436)
else -> -1
}
if (indexOfComplement == -1) {
newOres[oreIndex++] = ores[i]
continue
}
ores[indexOfComplement] = -1
}
if (coalRemaining >= coalNeeded) {
coalRemaining -= coalNeeded
ores[i] = -1
barAmounts[bar.ordinal]++
xpReward += bar.experience
} else {
newOres[oreIndex++] = ores[i]
}
}
ores = newOres
return xpReward
}
fun getBarAmount (bar: Bar) : Int {
return barAmounts[bar.ordinal]
}
fun getTotalBarAmount() : Int {
var total = 0
for (i in barAmounts) total += i
return total
}
fun takeBars (bar: Bar, amount: Int) : Item? {
val amt = amount.coerceAtMost(barAmounts[bar.ordinal])
if (amt == 0) return null
barAmounts[bar.ordinal] -= amt
return Item(bar.product.id, amt)
}
fun getAvailableSpace (ore: Int, level: Int = 99) : Int {
if (ore == Items.COAL_453)
return BlastConsts.COAL_LIMIT - coalRemaining
var freeSlots = 0
val bar = getBarForOreId(ore, coalRemaining, level)!!
val oreAmounts = HashMap<Int,Int>()
for (i in 0 until BlastConsts.ORE_LIMIT)
if (ores[i] == -1) {
var oreLimit = BlastConsts.ORE_LIMIT
if (bar == Bar.BRONZE) oreLimit *= 2
freeSlots = oreLimit - i
break
}
else oreAmounts[ores[i]] = (oreAmounts[ores[i]] ?: 0) + 1
val currentAmount = oreAmounts[ore] ?: 0
freeSlots = (BlastConsts.ORE_LIMIT - currentAmount).coerceAtMost(freeSlots)
return (freeSlots - getBarAmount(bar)).coerceAtLeast(0)
}
fun hasAnyOre() : Boolean {
return ores[0] != -1
}
fun toJson() : JSONObject {
val root = JSONObject()
val ores = JSONArray()
val bars = JSONArray()
for (ore in this.ores)
ores.add(ore.toString())
for (amount in barAmounts)
bars.add(amount.toString())
root["ores"] = ores
root["bars"] = bars
root["coal"] = coalRemaining.toString()
return root
}
companion object {
fun fromJson (root: JSONObject) : BFOreContainer {
val cont = BFOreContainer()
val jsonOres = root["ores"] as? JSONArray ?: return cont
val jsonBars = root["bars"] as? JSONArray ?: return cont
for (i in 0 until BlastConsts.ORE_LIMIT)
cont.ores[i] = jsonOres[i].toString().toIntOrNull() ?: -1
for (i in 0 until 9)
cont.barAmounts[i] = jsonBars[i].toString().toIntOrNull() ?: 0
cont.coalRemaining = root["coal"].toString().toIntOrNull() ?: 0
return cont
}
}
}
@@ -0,0 +1,156 @@
package content.minigame.blastfurnace
import content.global.skill.smithing.smelting.Bar
import core.api.*
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.world.map.Location
import org.json.simple.JSONArray
import org.json.simple.JSONObject
class BFPlayerState (val player: Player) {
var container = BFOreContainer()
val oresOnBelt = ArrayList<BFBeltOre>()
var barsNeedCooled = false
private set
fun processOresIntoBars() : Boolean {
if (barsNeedCooled && getVarbit(player, DISPENSER_STATE) == 1) {
setVarbit(player, DISPENSER_STATE, 2, true)
return false
}
if (getVarbit(player, DISPENSER_STATE) != 0 || !container.hasAnyOre())
return false
val xpReward = container.convertToBars(getStatLevel(player, Skills.SMITHING))
if (xpReward > 0) {
rewardXP(player, Skills.SMITHING, xpReward)
setVarbit(player, DISPENSER_STATE, 1, true)
barsNeedCooled = true
return true
}
return false
}
fun updateOres() {
val toRemove = ArrayList<BFBeltOre>()
for (ore in oresOnBelt) {
if (ore.tick())
toRemove.add(ore)
}
oresOnBelt.removeAll(toRemove)
}
fun coolBars() {
barsNeedCooled = false
setVarbit(player, DISPENSER_STATE, 3, true)
}
fun checkBars() {
if (getVarbit(player, DISPENSER_STATE) == 3)
setVarbit(player, DISPENSER_STATE, 0, true)
}
fun hasBarsClaimable(): Boolean {
return container.getTotalBarAmount() > 0
}
fun claimBars (bar: Bar, amount: Int) : Boolean {
if (barsNeedCooled) return false
val maxAmt = amount.coerceAtMost(freeSlots(player))
if (maxAmt == 0) return false
val reward = container.takeBars(bar, maxAmt) ?: return false
addItem(player, reward.id, reward.amount)
setBarClaimVarbits()
return true
}
fun setBarClaimVarbits() {
for (bar in Bar.values()) {
val amount = container.getBarAmount(bar)
val varbit = getVarbitForBar(bar)
if (varbit == 0) continue
if (getVarbit(player, varbit) == amount)
continue
setVarbit(player, varbit, amount, true)
}
var totalCoalNeeded = 0
val level = getStatLevel(player, Skills.SMITHING)
for ((id, amount) in container.getOreAmounts()) {
val barType = BlastFurnace.getBarForOreId(id, container.coalAmount(), level)
totalCoalNeeded += BlastFurnace.getNeededCoal(barType!!) * amount
}
setVarbit(player, COAL_NEEDED, (totalCoalNeeded - container.coalAmount()).coerceAtLeast(0))
}
private fun getVarbitForBar (bar: Bar) : Int {
return when (bar) {
Bar.BRONZE -> BRONZE_COUNT
Bar.IRON -> IRON_COUNT
Bar.STEEL -> STEEL_COUNT
Bar.MITHRIL -> MITHRIL_COUNT
Bar.ADAMANT -> ADDY_COUNT
Bar.RUNITE -> RUNITE_COUNT
Bar.SILVER -> SILVER_COUNT
Bar.GOLD -> GOLD_COUNT
else -> 0
}
}
fun toJson() : JSONObject {
val save = JSONObject()
save["bf-ore-cont"] = container.toJson()
val beltOres = JSONArray()
for (ore in oresOnBelt) {
beltOres.add(ore.toJson())
}
if (beltOres.isNotEmpty())
save["bf-belt-ores"] = beltOres
save["barsHot"] = barsNeedCooled
return save
}
fun readJson (data: JSONObject) {
oresOnBelt.clear()
if (data.containsKey("bf-ore-cont")) {
val contJson = data["bf-ore-cont"] as JSONObject
container = BFOreContainer.fromJson(contJson)
}
if (data.containsKey("bf-belt-ores")) {
val beltArray = data["bf-belt-ores"] as JSONArray
for (oreObj in beltArray) {
val oreInfo = oreObj as JSONObject
val id = oreInfo["id"].toString().toInt()
val amount = oreInfo["amount"].toString().toInt()
val location = Location.fromString(oreInfo["location"].toString())
val ore = BFBeltOre(player, id, amount, location)
oresOnBelt.add(ore)
}
}
if (data.containsKey("barsHot"))
barsNeedCooled = data["barsHot"] as Boolean
}
companion object {
val DISPENSER_STATE = 936
val COAL_NEEDED = 940
val BRONZE_COUNT = 941
val IRON_COUNT = 942
val STEEL_COUNT = 943
val MITHRIL_COUNT = 944
val ADDY_COUNT = 945
val RUNITE_COUNT = 946
val GOLD_COUNT = 947
val SILVER_COUNT = 948
}
}
@@ -0,0 +1,110 @@
package content.minigame.blastfurnace
import core.api.animateScenery
import core.api.getScenery
import core.api.replaceScenery
import core.game.world.map.Location
class BFSceneryController {
fun updateBreakable (potPipeBroken: Boolean, pumpPipeBroken: Boolean, beltBroken: Boolean, cogBroken: Boolean) {
val beltObj = getScenery(beltGearRight)!!
val gearObj = getScenery(cogRightLoc)!!
val potPipe = getScenery(potPipeLoc)!!
val pumpPipe = getScenery(pumpPipeLoc)!!
if (potPipeBroken && potPipe.id != BROKEN_POT_PIPE)
replaceScenery(potPipe, BROKEN_POT_PIPE, -1)
else if (!potPipeBroken && potPipe.id == BROKEN_POT_PIPE)
replaceScenery(potPipe, DEFAULT_POT_PIPE, -1)
if (pumpPipeBroken && pumpPipe.id != BROKEN_PUMP_PIPE)
replaceScenery(pumpPipe, BROKEN_PUMP_PIPE, -1)
else if (!pumpPipeBroken && pumpPipe.id == BROKEN_PUMP_PIPE)
replaceScenery(pumpPipe, DEFAULT_PUMP_PIPE, -1)
if (beltBroken && beltObj.id != BROKEN_BELT)
replaceScenery(beltObj, BROKEN_BELT, -1)
else if (!beltBroken && beltObj.id == BROKEN_BELT)
replaceScenery(beltObj, DEFAULT_BELT, -1)
if (cogBroken && gearObj.id != BROKEN_COG)
replaceScenery(gearObj, BROKEN_COG, -1)
else if (!cogBroken && gearObj.id == BROKEN_COG)
replaceScenery(gearObj, DEFAULT_COG, -1)
}
fun updateAnimations (pedaling: Boolean, beltBroken: Boolean, cogBroken: Boolean) {
val belt1 = getScenery(belt1Loc)!!
val belt2 = getScenery(belt2Loc)!!
val belt3 = getScenery(belt3Loc)!!
val beltGearLeft = getScenery(beltGearLeft)!!
val beltGearRight = getScenery(beltGearRight)!!
val cogLeft = getScenery(cogLeftLoc)!!
val cogRight = getScenery(cogRightLoc)!!
val cogCenter = getScenery(centralGearLoc)!!
val beltAnim = if (pedaling && !beltBroken && !cogBroken) BELT_ANIM else -1
val gearAnim = if (pedaling && !beltBroken && !cogBroken) GEAR_ANIM else -1
animateScenery(belt1, beltAnim)
animateScenery(belt2, beltAnim)
animateScenery(belt3, beltAnim)
animateScenery(beltGearLeft, gearAnim)
animateScenery(beltGearRight, gearAnim)
animateScenery(cogLeft, gearAnim)
animateScenery(cogRight, gearAnim)
animateScenery(cogCenter, gearAnim)
}
fun updateStove (temp: Int) {
val stoveObj = getScenery(stoveLoc)!!
if (temp >= 67 && stoveObj.id != STOVE_HOT)
replaceScenery(stoveObj, STOVE_HOT, -1)
else if (temp in 34..66 && stoveObj.id != STOVE_WARM)
replaceScenery(stoveObj, STOVE_WARM, -1)
else if (temp in 0..33 && stoveObj.id != STOVE_COLD)
replaceScenery(stoveObj, STOVE_COLD, -1)
}
fun resetAllScenery() {
val beltObj = getScenery(beltGearRight)!!
val gearObj = getScenery(cogRightLoc)!!
val potPipe = getScenery(potPipeLoc)!!
val pumpPipe = getScenery(pumpPipeLoc)!!
val stoveObj = getScenery(stoveLoc)!!
replaceScenery(gearObj, DEFAULT_COG, -1)
replaceScenery(beltObj, DEFAULT_BELT, -1)
replaceScenery(pumpPipe, DEFAULT_PUMP_PIPE, -1)
replaceScenery(potPipe, DEFAULT_POT_PIPE, -1)
replaceScenery(stoveObj, STOVE_COLD, -1)
}
companion object {
val belt1Loc = Location(1943, 4967, 0)
val belt2Loc = Location(1943, 4966, 0)
val belt3Loc = Location(1943, 4965, 0)
var potPipeLoc = Location(1943, 4961, 0)
var pumpPipeLoc = Location(1947, 4961, 0)
var cogLeftLoc = Location(1945, 4965, 0)
var cogRightLoc = Location(1945, 4967, 0)
var beltGearLeft = Location(1944, 4965, 0)
var beltGearRight = Location(1944, 4967, 0)
var centralGearLoc = Location(1945, 4966, 0)
var stoveLoc = Location(1948, 4963, 0)
val DEFAULT_BELT = 9102
val BROKEN_BELT = 9103
val DEFAULT_COG = 9104
val BROKEN_COG = 9105
val DEFAULT_POT_PIPE = 9116
val BROKEN_POT_PIPE = 9117
val DEFAULT_PUMP_PIPE = 9120
val BROKEN_PUMP_PIPE = 9121
val STOVE_COLD = 9085
val STOVE_WARM = 9086
val STOVE_HOT = 9087
val BELT_ANIM = 2435
val GEAR_ANIM = 2436
}
}
@@ -0,0 +1,19 @@
package content.minigame.blastfurnace
import core.api.resetAnimator
import core.api.teleport
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.system.timer.PersistTimer
class BFTempEntranceTimer : PersistTimer(BlastConsts.FEE_ENTRANCE_DURATION, "bf-tempentrance") {
override fun run(entity: Entity): Boolean {
if (entity !is Player) return false
if (BlastFurnace.insideBorders(entity)) {
teleport(entity, BlastConsts.EXIT_LOC)
entity.pulseManager.clear()
resetAnimator(entity)
}
return false
}
}
@@ -0,0 +1,28 @@
package content.minigame.blastfurnace
import core.game.world.map.Location
object BlastConsts {
val ENTRANCE_LOC = Location (1940, 4958, 0)
val EXIT_LOC = Location (2931, 10197, 0)
val STAIRLOC_ENTRANCE = Location (2930, 10196)
val STAIRLOC_EXIT = Location (1939, 4956)
val STAIR_ENTRANCE_ID = 9084
val STAIR_EXIT_ID = 9138
val BELT = 9100
val PEDALS = 9097
val STOVE = intArrayOf(BFSceneryController.STOVE_COLD, BFSceneryController.STOVE_WARM, BFSceneryController.STOVE_HOT)
val PUMP = 9090
val COKE = 9088
val TEMP_GAUGE = 9089
val SINK = 9143
val DISPENSER = intArrayOf(9093, 9094, 9095, 9096)
val SMITH_REQ = 60
val ENTRANCE_FEE = 2500
val FEE_ENTRANCE_DURATION = 1000
val COAL_LIMIT = 226
val ORE_LIMIT = 28
val BAR_LIMIT = 28
val COKE_LIMIT = 15
}
@@ -2,308 +2,259 @@ package content.minigame.blastfurnace
import content.global.skill.smithing.smelting.Bar import content.global.skill.smithing.smelting.Bar
import core.api.* import core.api.*
import core.game.container.impl.EquipmentContainer import core.game.node.entity.Entity
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import core.game.node.item.Item import core.game.node.item.Item
import core.game.system.task.Pulse import core.game.world.map.zone.ZoneBorders
import core.game.world.map.Location
import core.tools.RandomFunction import core.tools.RandomFunction
import org.json.simple.JSONObject
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.Items.GOLDSMITH_GAUNTLETS_776 import org.rs09.consts.NPCs
import kotlin.math.max
/**le Blast Furnace has arrived: class BlastFurnace : MapArea, PersistPlayer, TickListener {
* Handles most of the Blast Furnace's operating logic, there is some crosstalk between override fun defineAreaBorders(): Array<ZoneBorders> {
* this file and BlastFurnaceListeners.kt as well as the BlastFurnaceZone.kt. return arrayOf(bfArea)
* This also handles a lot of the varbit fuckery for the furnace. The other
* varbit fuckery is handled in BlastFurnaceListeners.
* Varbits are still slightly busted but not in a way that breaks the operation or usage of
* Blast Furnace, just visual and I've had enough of the funny binary numbers to say fuck it
* and will just come back to it at a later date.
* @author phil lips*/
object BlastFurnace {
val belt1 = getScenery(1943, 4967, 0)
val belt2 = getScenery(1943, 4966, 0)
val belt3 = getScenery(1943, 4965, 0)
val disLoc = getScenery(1941, 4963, 0)
var pipe1 = getScenery(1943, 4961, 0)
var pipe2 = getScenery(1947, 4961, 0)
var cogs1 = getScenery(1945, 4965, 0)
var cogs2 = getScenery(1945, 4967, 0)
var beltLoc1 = getScenery(1944, 4965, 0)
var beltLoc2 = getScenery(1944, 4967, 0)
var gearsLoc = getScenery(1945, 4966, 0)
var stoveLoc = getScenery(1948, 4963, 0)
val mPot = 9098
var gaugeViewList = ArrayList<Player>()
var blastFurnacePlayerList = ArrayList<Player>()
var furnaceTemp = 0
var pumpPipeBroken = false
var potPipeBroken = false
var stoveCoke = 0
var stoveTemp = 0
var beltRunning = false
var makeBars = false
var beltBroken = false
var cogBroken = false
var pumping = false
var pedaling = false
var barsHot = false
var giveSmithXp = 0
/**This pulse object handles checks between*/
var blastPulse = object : Pulse() {
override fun pulse(): Boolean {
gaugeViewList.forEach {
var anim = furnaceTemp + 2452
animateInterface(it, 30, 4, anim)
} }
blastFurnacePlayerList.forEach {
if(it.getAttribute("BlastTimer",0) > 0 && it.getSkills().getLevel(Skills.SMITHING) < 60){ override fun savePlayer(player: Player, save: JSONObject) {
it.incrementAttribute("BlastTimer",-1) val state = playerStates[player.details.uid]
} else if(it.getAttribute("BlastTimer",0) <= 0 && it.getSkills().getLevel(Skills.SMITHING) < 60){ if (state != null) {
it.removeAttribute("BlastTimer") save["bf-state"] = state.toJson()
sendDialogue(it,"Your time in the Blast Furnace has run out!")
it.properties.teleportLocation = Location.create(2931, 10197, 0)
} else if(it.getAttribute("BlastTimer",false) && it.getSkills().getLevel(Skills.SMITHING) >= 60){
it.removeAttribute("BlastTimer")
}
if(giveSmithXp > 0){
rewardXP(it,Skills.SMITHING,1.0)
giveSmithXp--
}
}
//interfaceManager()
runConveyor()
stoveCokeTemperature()
furnaceTemperature()
operateFurnace()
breakStuff()
fixStuff()
return false
} }
} }
/**This handles the coke stove temperature, if there is coke in the override fun parsePlayer(player: Player, data: JSONObject) {
* coke oven the temperature will raise, otherwise the temperature playerStates.remove(player.details.uid)
* will lower, 0-33 is cold, 34-66 is warm, 67-100 is hot*/ if (data.containsKey("bf-state")) {
fun stoveCokeTemperature() { val stateObj = data["bf-state"] as JSONObject
if (getWorldTicks() % 10 == 0 && stoveCoke > 0) { getPlayerState(player).readJson(stateObj)
stoveCoke--
}
if (stoveCoke > 0) {
if (stoveTemp < 100) {
stoveTemp += 1
}
} else if (stoveTemp > 0) {
stoveTemp--
}
if (stoveTemp > 66 && stoveLoc!!.id != 9087) {
stoveLoc = getScenery(1948, 4963, 0)
replaceScenery(stoveLoc!!, 9087, -1)
} else if (stoveTemp in 34..66 && stoveLoc!!.id != 9086) {
stoveLoc = getScenery(1948, 4963, 0)
replaceScenery(stoveLoc!!, 9086, -1)
} else if (stoveTemp < 32 && stoveLoc!!.id != 9085) {
stoveLoc = getScenery(1948, 4963, 0)
replaceScenery(stoveLoc!!, 9085, -1)
} }
} }
/**This handles the raising and lowering of the furnace temperature override fun areaEnter(entity: Entity) {
* depending on whether or not the coke stove is hot and if the if (entity is Player) {
* furnace pump is being operated. A hot coke stove will raise the playersInArea.add(entity)
* temperature the fastest, a warm coke stove will raise it slowly, val state = getPlayerState(entity)
* and a cold stove will raise it the slowest*/ for (ore in state.oresOnBelt)
fun furnaceTemperature() { ore.createNpc()
if (stoveTemp >= 67 && pumping && furnaceTemp < 100 && (!pumpPipeBroken || !pumpPipeBroken)) {
furnaceTemp += 3
} else if (stoveTemp >= 34 && pumping && furnaceTemp < 100 && (!pumpPipeBroken || !pumpPipeBroken)) {
furnaceTemp += 2
} else if (stoveTemp > 0 && pumping && furnaceTemp < 100 && (!pumpPipeBroken || !pumpPipeBroken)) {
furnaceTemp += 1
} else if (furnaceTemp > 0) {
furnaceTemp -= 1
} }
} }
/**Tells the furnace whether or not to smelt bars depending on if override fun areaLeave(entity: Entity, logout: Boolean) {
* the furnace temperature is in the right range. Also contains the if (entity is Player) {
* logic for smelting the players ore given the fact that they actually playersInArea.remove(entity)
* have ore in their ore container and/or have coal in their coal container*/ val state = getPlayerState(entity)
fun operateFurnace() { for (ore in state.oresOnBelt) {
makeBars = furnaceTemp in 51..66 ore.npcInstance?.clear()
blastFurnacePlayerList.forEach { player -> ore.npcInstance = null
var playerCoal = player.blastCoal.getAmount(Items.COAL_453)
var playerOre = player.blastOre.toArray().filterNotNull()
var barsAmountFree = player.blastBars.freeSlots()
var barsAmount = player.blastBars.itemCount()
var oreAmount = player.blastOre.itemCount()
var totalAmount = barsAmount + oreAmount
if (barsHot) {
setVarbit(player, 936, 2)
}else if (makeBars && playerOre.isNotEmpty() && barsAmountFree > 0 && totalAmount < 56 && player.getAttribute("OreInPot",false) == true) {
playerOre.forEach { oreID ->
playerCoal = player.blastCoal.getAmount(Items.COAL_453)
var bars = arrayOf(Bar.forOre(oreID.id)!!)
if(oreID.id == Items.IRON_ORE_440) {
bars = arrayOf(Bar.STEEL, Bar.IRON)
}
inner@ for(bar in bars) {
var hasRequirements = true
for(required in bar.ores) {
if(required.id == Items.COAL_453) {
if(!player.blastCoal.contains(Items.COAL_453, required.amount / 2)) {
hasRequirements = false
}
} else if(!player.blastOre.containsItem(required)) {
hasRequirements = false
}
}
if(hasRequirements) {
var removed = true
for(required in bar.ores) {
if(required.id == Items.COAL_453) {
if(!player.blastCoal.remove(Item(Items.COAL_453, required.amount / 2))) {
removed = false
}
} else {
if(!player.blastOre.remove(required)) {
removed = false
}
}
}
if(removed) {
setVarbit(player, 936, 1)
barsHot = true
player.blastBars.add(bar.product)
var experience = bar.experience
if(bar.product.id == Items.GOLD_BAR_2357 &&
player.equipment[EquipmentContainer.SLOT_HANDS] != null &&
player.equipment[EquipmentContainer.SLOT_HANDS].id == GOLDSMITH_GAUNTLETS_776) {
experience *= 2.5
}
rewardXP(player, Skills.SMITHING, experience)
totalAmount = barsAmount + oreAmount
giveSmithXp++
break@inner
}
}
}
}
}
if(!barsHot && playerOre.isNotEmpty()) {
var coalToAdd = 0
playerOre.forEach { oreID ->
when(oreID.id){
Items.RUNITE_ORE_451 -> coalToAdd = (player.blastOre.getAmount(451) * 4) - playerCoal
Items.ADAMANTITE_ORE_449 -> coalToAdd = (player.blastOre.getAmount(449) * 3) - playerCoal
Items.MITHRIL_ORE_447 -> coalToAdd = (player.blastOre.getAmount(447) * 2) - playerCoal
Items.IRON_ORE_440 -> coalToAdd = player.blastOre.getAmount(440) - playerCoal
}
}
if (coalToAdd < 0){
setVarbit(player, 940, 0)
} else setVarbit(player, 940, coalToAdd)
}
else if(playerOre.isEmpty()) {
setVarbit(player, 940, 0)
}
if(playerOre.isEmpty() && player.getAttribute("OreInPot",false)){
player.removeAttribute("OreInPot")
} }
} }
} }
/**This handles the conveyor belt, if the pedals are running and the belt override fun tick() {
* isn't broken then the conveyor belt will run*/ if (state.beltBroken || state.cogBroken) pedaler = null
fun runConveyor() { if (state.potPipeBroken || state.pumpPipeBroken) pumper = null
if (pedaling && (!beltBroken && !cogBroken)) { state.tick(pumper != null, pedaler != null)
animateScenery(belt1!!, 2435)
animateScenery(belt2!!, 2435) if (playersInArea.size > 0) {
animateScenery(belt3!!, 2435) updateVisuals()
animateScenery(beltLoc1!!, 2436) if (getWorldTicks() % 2 == 0) {
animateScenery(beltLoc2!!, 2436) updatePedaler()
animateScenery(cogs1!!, 2436) updatePumper()
animateScenery(cogs2!!, 2436) }
animateScenery(gearsLoc!!, 2436) processBars()
beltRunning = true
} else {
animateScenery(belt1!!, -1)
animateScenery(belt2!!, -1)
animateScenery(belt3!!, -1)
animateScenery(beltLoc1!!, -1)
animateScenery(beltLoc2!!, -1)
animateScenery(cogs1!!, -1)
animateScenery(cogs2!!, -1)
animateScenery(gearsLoc!!, -1)
beltRunning = false
} }
//Reset each tick
pumper = null
pedaler = null
} }
/**Its just one of those days*/ private fun updatePumper() {
fun breakStuff() { if (pumper != null) {
if (pumping && furnaceTemp > 76) { if (state.stoveTemp == 0) return
if (RandomFunction.random(1, 100) > 20 && (!potPipeBroken || !pumpPipeBroken)) { if (state.furnaceTemp == 100 && RandomFunction.roll(5)) {
if (RandomFunction.random(1, 100) > 50 && !potPipeBroken) { impact(pumper!!, (0.2 * pumper!!.skills.maximumLifepoints).toInt())
pipe1 = getScenery(1943, 4961, 0) sendMessage(pumper!!, "A blast of hot air cooks you a bit.")
potPipeBroken = true pumper!!.pulseManager.clear()
return
} }
if (RandomFunction.random(1, 100) <= 50 && !pumpPipeBroken) { rewardXP(pumper!!, Skills.STRENGTH, 4.0)
pipe2 = getScenery(1947, 4961, 0)
pumpPipeBroken = true
}
}
}
if (beltRunning && (!beltBroken || !cogBroken)) {
if (RandomFunction.random(1, 100) <= 2) {
beltLoc2 = getScenery(1944, 4967, 0)
beltBroken = true
} else if (RandomFunction.random(1, 100) <= 2) {
cogs2 = getScenery(1945, 4967, 0)
cogBroken = true
}
}
if (beltBroken && beltLoc2!!.id != 9103) {
beltLoc2 = getScenery(1944, 4967, 0)
replaceScenery(beltLoc2!!, 9103, -1)
}
if (cogBroken && cogs2!!.id != 9105) {
cogs2 = getScenery(1945, 4967, 0)
replaceScenery(cogs2!!, 9105, -1)
}
if (potPipeBroken && pipe1!!.id != 9117) {
pipe1 = getScenery(1943, 4961, 0)
replaceScenery(pipe1!!, 9117, -1)
}
if (pumpPipeBroken && pipe2!!.id != 9121) {
pipe2 = getScenery(1947, 4961, 0)
replaceScenery(pipe2!!, 9121, -1)
} }
} }
/**This replaces the breakable objects IDs back to their unbroken state*/ private fun updatePedaler() {
fun fixStuff() { if (pedaler == null) return
if (!potPipeBroken && pipe1!!.id != 9116) { var oresPedaled = false
pipe1 = getScenery(1943, 4961, 0) for (state in playerStates.values) {
replaceScenery(pipe1!!, 9116, -1) if (state.oresOnBelt.isEmpty()) continue
state.updateOres()
oresPedaled = true
} }
if (!pumpPipeBroken && pipe2!!.id != 9120) { if (oresPedaled) {
pipe2 = getScenery(1947, 4961, 0) rewardXP(pedaler!!, Skills.AGILITY, 2.0)
replaceScenery(pipe2!!, 9120, -1) pedaler!!.settings.runEnergy -= 2
} }
if (!beltBroken && beltLoc2!!.id != 9102) {
beltLoc2 = getScenery(1944, 4967, 0)
replaceScenery(beltLoc2!!, 9102, -1)
} }
if (!cogBroken && cogs2!!.id != 9104) {
cogs2 = getScenery(1945, 4967, 0) private fun updateVisuals() {
replaceScenery(cogs2!!, 9104, -1) sceneryController.updateStove(state.stoveTemp)
sceneryController.updateBreakable(
state.potPipeBroken,
state.pumpPipeBroken,
state.beltBroken,
state.cogBroken
)
sceneryController.updateAnimations(pedaler != null, state.beltBroken, state.cogBroken)
}
private fun processBars() {
if (state.furnaceTemp !in 51..66) return
var totalProcessed = 0
for (player in playersInArea) {
if (getPlayerState(player).processOresIntoBars())
totalProcessed++
}
if (totalProcessed == 0) return
for (player in playersInArea)
rewardXP(player, Skills.SMITHING, totalProcessed.toDouble())
}
companion object {
val bfArea = ZoneBorders (1934, 4955, 1958, 4975)
val playersInArea = ArrayList<Player>()
val playerStates = HashMap<Int, BFPlayerState>()
val state = BlastState()
val sceneryController = BFSceneryController()
var pedaler: Player? = null
var pumper: Player? = null
fun insideBorders (player: Player) : Boolean {
return bfArea.insideBorder(player.location)
}
fun placeAllOre (p: Player, id: Int = -1, accountForSkill: Boolean = false) {
val oreCounts = HashMap<Int, Int>()
val oreContainer = getOreContainer(p)
val level = if (accountForSkill) getStatLevel(p, Skills.SMITHING) else 99
for (item in p.inventory.toArray()) {
if (item == null) continue
if (getNpcForOre(item.id) == -1) continue
if (id != -1 && item.id != id) continue
val bar = getBarForOreId(item.id, oreContainer.coalAmount(), level)!!
if (bar.level > level) continue
oreCounts[item.id] = (oreCounts[item.id] ?: 0) + item.amount
}
for ((oreId, amount) in oreCounts) {
var maxAmt = oreContainer.getAvailableSpace(oreId, level)
if (oreId == Items.COPPER_ORE_436 || oreId == Items.TIN_ORE_438)
maxAmt += (BlastConsts.ORE_LIMIT - getAmountOnBelt(p, oreId))
if (oreId == Items.COAL_453)
maxAmt -= getAmountOnBelt(p, oreId)
else
maxAmt -= getTotalOreOnBelt(p)
maxAmt = maxAmt.coerceAtMost(amount).coerceAtLeast(0)
if (maxAmt == 0) continue
if (removeItem(p, Item(oreId, maxAmt)))
addOreToBelt(p, oreId, maxAmt)
}
}
fun getPlayerState (p: Player) : BFPlayerState {
if (playerStates[p.details.uid] != null)
return playerStates[p.details.uid]!!
val state = BFPlayerState(p)
playerStates[p.details.uid] = state
return state
}
fun getOreContainer (p: Player) : BFOreContainer {
return getPlayerState(p).container
}
fun addOreToBelt (p: Player, id: Int, amount: Int) : BFBeltOre {
val beltOre = BFBeltOre(p, id, amount, BFBeltOre.ORE_START_LOC)
beltOre.createNpc()
getPlayerState(p).oresOnBelt.add(beltOre)
return beltOre
}
fun getAmountOnBelt (p: Player, id: Int) : Int {
var total = 0
for (ore in getPlayerState(p).oresOnBelt) {
if (ore.id == id)
total += ore.amount
}
return total
}
fun getTotalOreOnBelt (p: Player) : Int {
var total = 0
for (ore in getPlayerState(p).oresOnBelt)
if (ore.id != Items.COAL_453) total += ore.amount
return total
}
fun getNeededCoal (bar: Bar) : Int {
var coalAmount = 0
if (bar.ores.size == 1)
return coalAmount
for (ore in bar.ores) {
if (ore.id == Items.COAL_453) {
coalAmount = ore.amount
break
}
}
if (coalAmount > 1) coalAmount /= 2
return coalAmount
}
fun getBarForOreId (id: Int, coalAmount: Int, level: Int) : Bar? {
return when (id) {
Items.COPPER_ORE_436, Items.TIN_ORE_438 -> Bar.BRONZE
Items.IRON_ORE_440 -> if (coalAmount >= 1 && level >= Bar.STEEL.level) Bar.STEEL else Bar.IRON
else -> Bar.forOre(id)
}
}
fun getNpcForOre (id: Int) : Int {
return when (id) {
Items.IRON_ORE_440 -> NPCs.IRON_ORE_2556
Items.COPPER_ORE_436 -> NPCs.COPPER_ORE_2555
Items.TIN_ORE_438 -> NPCs.TIN_ORE_2554
Items.COAL_453 -> NPCs.COAL_2562
Items.MITHRIL_ORE_447 -> NPCs.MITHRIL_ORE_2557
Items.ADAMANTITE_ORE_449 -> NPCs.ADAMANTITE_ORE_2558
Items.SILVER_ORE_442 -> NPCs.SILVER_ORE_2560
Items.GOLD_ORE_444 -> NPCs.GOLD_ORE_2561
Items.RUNITE_ORE_451 -> NPCs.RUNITE_ORE_2559
else -> -1
}
}
fun getEntranceFee (hasCharos: Boolean, smithLevel: Int) : Int {
if (smithLevel >= BlastConsts.SMITH_REQ) return 0
return if (hasCharos) BlastConsts.ENTRANCE_FEE / 2 else BlastConsts.ENTRANCE_FEE
}
fun enter (player: Player, feePaid: Boolean) {
if (feePaid && !hasTimerActive<BFTempEntranceTimer>(player))
registerTimer(player, BFTempEntranceTimer())
teleport(player, BlastConsts.ENTRANCE_LOC)
} }
} }
} }
@@ -1,117 +1,51 @@
package content.minigame.blastfurnace package content.minigame.blastfurnace
import content.global.skill.smithing.smelting.Bar
import core.api.* import core.api.*
import core.game.interaction.InterfaceListener import core.game.interaction.InterfaceListener
import core.game.node.item.Item import core.game.system.task.Pulse
import org.rs09.consts.Components import org.rs09.consts.Components
import org.rs09.consts.Items
/**
* Handles the blast furnace bar stock interface.
* @author phil, bushtail
* @version 2.0
*/
class BlastFurnaceInterfaceListener : InterfaceListener { class BlastFurnaceInterfaceListener : InterfaceListener {
override fun defineInterfaceListeners() { override fun defineInterfaceListeners() {
onOpen(Components.BLAST_FURNACE_BAR_STOCK_28) { player, component ->
val state = BlastFurnace.getPlayerState(player)
state.setBarClaimVarbits()
state.checkBars()
return@onOpen true
}
onOpen(28,) { player, component -> onOpen(Components.BLAST_FURNACE_TEMP_GAUGE_30) {player, _ ->
var playerBars = player.blastBars.toArray().filterNotNull() submitIndividualPulse(player, object : Pulse() {
var bronzeBit = 1 override fun pulse(): Boolean {
var ironBit = 1 val anim = BlastFurnace.state.furnaceTemp + 2452
var steelBit = 1 animateInterface(player, 30, 4, anim)
var mithrilBit = 1 return false
var adamantiteBit = 1
var runiteBit = 1
var silverBit = 1
var goldBit = 1
var barTotal = 0
val barCounts = HashMap<Int, Int>()
if (playerBars.isEmpty()) {
return@onOpen true
} else {
playerBars.forEach { barItem ->
when (barItem.id) {
Items.BRONZE_BAR_2349 -> barCounts[barItem.id] = bronzeBit++
Items.IRON_BAR_2351 -> barCounts[barItem.id] = ironBit++
Items.STEEL_BAR_2353 -> barCounts[barItem.id] = steelBit++
Items.MITHRIL_BAR_2359 -> barCounts[barItem.id] = mithrilBit++
Items.ADAMANTITE_BAR_2361 -> barCounts[barItem.id] = adamantiteBit++
Items.RUNITE_BAR_2363 -> barCounts[barItem.id] = runiteBit++
Items.SILVER_BAR_2355 -> barCounts[barItem.id] = silverBit++
Items.GOLD_BAR_2357 -> barCounts[barItem.id] = goldBit++
}
}
for ((id, amount) in barCounts) {
when (id) {
Items.BRONZE_BAR_2349 -> setVarbit(player, 941, amount)
Items.IRON_BAR_2351 -> setVarbit(player, 942, amount)
Items.STEEL_BAR_2353 -> setVarbit(player, 943, amount)
Items.MITHRIL_BAR_2359 -> setVarbit(player, 944, amount)
Items.ADAMANTITE_BAR_2361 -> setVarbit(player, 945, amount)
Items.RUNITE_BAR_2363 -> setVarbit(player, 946, amount)
Items.SILVER_BAR_2355 -> setVarbit(player, 948, amount)
Items.GOLD_BAR_2357 -> setVarbit(player, 947, amount)
}
} }
})
return@onOpen true return@onOpen true
} }
} onClose(Components.BLAST_FURNACE_TEMP_GAUGE_30) {player, _ -> player.pulseManager.clear(); return@onClose true }
on(Components.BLAST_FURNACE_BAR_STOCK_28){ player, _, _, buttonID, _, _ -> on(Components.BLAST_FURNACE_BAR_STOCK_28){ player, _, _, buttonID, _, _ ->
val bar = BFBars.forId(buttonID) ?: return@on false val (isAll, bar) = getBarForButton(buttonID)
val isAll = buttonID == bar.allButtonId val state = BlastFurnace.getPlayerState(player)
val playerAmt = getVarbit(player, bar.varbit) state.claimBars(bar, if (isAll) state.container.getBarAmount(bar) else 1)
if(playerAmt == 0) return@on true
var amtToWithdraw = if(isAll) playerAmt else 1
if(freeSlots(player) < amtToWithdraw){
amtToWithdraw = freeSlots(player)
}
val barItemId = when(bar){
BFBars.BRONZE -> Items.BRONZE_BAR_2349
BFBars.IRON -> Items.IRON_BAR_2351
BFBars.STEEL -> Items.STEEL_BAR_2353
BFBars.MITHRIL -> Items.MITHRIL_BAR_2359
BFBars.ADAMANT -> Items.ADAMANTITE_BAR_2361
BFBars.RUNITE -> Items.RUNITE_BAR_2363
BFBars.SILVER -> Items.SILVER_BAR_2355
BFBars.GOLD -> Items.GOLD_BAR_2357
}
setVarbit(player, bar.varbit, playerAmt - amtToWithdraw)
while(amtToWithdraw > 0){
if(addItem(player, barItemId, 1) && player.blastBars.remove(Item(barItemId))) {
amtToWithdraw--
} else break
}
if(player.blastBars.isEmpty)
setVarp(player, 543, 0)
return@on true return@on true
} }
} }
internal enum class BFBars(val buttonId: Int, val varbit: Int, val allButtonId: Int = buttonId + 2){ private fun getBarForButton (id: Int) : Pair<Boolean, Bar> {
BRONZE(43, 941, 44), return when (id) {
IRON(40, 942, 41), 43,44 -> Pair(id == 44, Bar.BRONZE)
STEEL(36, 943), 40,41 -> Pair(id == 41, Bar.IRON)
MITHRIL(33, 944), 36,38 -> Pair(id == 38, Bar.STEEL)
ADAMANT(30, 945), 33,35 -> Pair(id == 35, Bar.MITHRIL)
RUNITE(27, 946), 30,32 -> Pair(id == 32, Bar.ADAMANT)
SILVER(24, 948), 27,29 -> Pair(id == 29, Bar.RUNITE)
GOLD(21, 947); 24,26 -> Pair(id == 26, Bar.SILVER)
21,23 -> Pair(id == 23, Bar.GOLD)
companion object { else -> Pair(false, Bar.BRONZE)
private val idMap = values().associateBy { it.buttonId }
fun forId(buttonId: Int): BFBars? {
return idMap[buttonId] ?: idMap[buttonId - 2] ?: idMap[buttonId - 1]
} }
} }
} }
}
@@ -1,36 +1,31 @@
package content.minigame.blastfurnace package content.minigame.blastfurnace
import content.global.skill.smithing.smelting.Bar import content.region.misc.keldagrim.handlers.BlastFurnaceDoorDialogue
import core.api.* import core.api.*
import core.game.dialogue.DialogueFile
import core.game.dialogue.Topic
import core.game.interaction.IntType
import core.game.interaction.InteractionListener import core.game.interaction.InteractionListener
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import core.game.system.task.Pulse import core.game.system.task.Pulse
import core.game.world.map.Location import core.game.world.map.Location
import core.tools.END_DIALOGUE
import org.rs09.consts.Items import org.rs09.consts.Items
/**"Most" of the listeners for blast furnace live in this file, handles
* listeners for most things from interacting with the temp gauge to putting ore on the
* conveyor belt. The only thing that's not in here as far as listeners goes is Ordan's unnoting.
* That lives in OrdanDialogue.kt
* @author phil, bushtail*/
class BlastFurnaceListeners : InteractionListener { class BlastFurnaceListeners : InteractionListener {
companion object {
val disLoc = getScenery(1941, 4963, 0) var initialized = false
val brokenPotPipe = 9117 val PUMP_LOC = Location.create(1950, 4961, 0)
val brokenPumpPipe = 9121 val PUMP_FACELOC = Location.create(1949, 4961, 0)
val pump = 9090 val PEDAL_LOC = Location.create(1947, 4966, 0)
val stove = intArrayOf(9085, 9086, 9087) val PEDAL_DISMOUNT = Location.create(1947, 4967, 0)
val coke = 9088 val PEDAL_FACELOC = Location.create(1946, 4966, 0)
val pedals = 9097 val PUMP_ANIM = 2432
val conveyorLoad = 9100 val PEDAL_ANIM = 2433
val dispenser = intArrayOf(9093, 9094, 9095, 9096) val PEDAL_SCENERY = getScenery(PEDAL_LOC)!!
val brokenBelt = 9103 val PUMP_SCENERY = getScenery(PUMP_LOC)!!
val brokenCog = 9105 val validOreIds = intArrayOf(
val tGauge = 9089
val sink = 9143
val playerOre = intArrayOf(
Items.IRON_ORE_440, Items.IRON_ORE_440,
Items.COPPER_ORE_436, Items.COPPER_ORE_436,
Items.TIN_ORE_438, Items.TIN_ORE_438,
@@ -41,81 +36,93 @@ class BlastFurnaceListeners : InteractionListener {
Items.GOLD_ORE_444, Items.GOLD_ORE_444,
Items.RUNITE_ORE_451 Items.RUNITE_ORE_451
) )
val notedOre = intArrayOf( }
Items.IRON_ORE_441,
Items.COPPER_ORE_437,
Items.TIN_ORE_439,
Items.COAL_454,
Items.MITHRIL_ORE_448,
Items.ADAMANTITE_ORE_450,
Items.SILVER_ORE_443,
Items.GOLD_ORE_445,
Items.RUNITE_ORE_452
)
override fun defineListeners() { override fun defineListeners() {
if (initialized) return
initialized = true
/**Handles operating the pump*/ on (BlastConsts.STAIR_ENTRANCE_ID, IntType.SCENERY, "climb-down") { p, _ ->
val hasCharos = inEquipment(p, Items.RING_OF_CHAROS_4202) || inEquipment(p, Items.RING_OF_CHAROSA_6465)
val fee = BlastFurnace.getEntranceFee(hasCharos, getStatLevel(p, Skills.SMITHING))
if (fee > 0 && !hasTimerActive<BFTempEntranceTimer>(p)) {
openDialogue(p, BlastFurnaceDoorDialogue(fee))
return@on true
}
BlastFurnace.enter(p, false)
return@on true
}
on(pump, SCENERY, "operate") { player, node -> on (BlastConsts.STAIR_EXIT_ID, IntType.SCENERY, "climb-up") { p, _ ->
if(player.getSkills().getLevel(Skills.STRENGTH) >= 30) { teleport(p, BlastConsts.EXIT_LOC)
val pumpL = Location(1950, 4961, 0) return@on true
val pumpF = Location(1949, 4961, 0) }
player.properties.teleportLocation = pumpL
submitIndividualPulse(player, object : Pulse() { on (BlastConsts.PUMP, IntType.SCENERY, "operate") { p, _ ->
if (getDynLevel(p, Skills.STRENGTH) >= 30 && BlastFurnace.pumper == null) {
forceMove(p, p.location, PUMP_LOC, 0, 30) {
animate(p, PUMP_ANIM)
removeScenery(PUMP_SCENERY)
submitIndividualPulse(p, object : Pulse() {
override fun pulse(): Boolean { override fun pulse(): Boolean {
face(player, pumpF,) face(p, PUMP_FACELOC)
animate(player, 2432) animate(p, PUMP_ANIM)
BlastFurnace.pumping = true BlastFurnace.pumper = p
if (!BlastFurnace.pumpPipeBroken || !BlastFurnace.potPipeBroken) rewardXP(
player,
Skills.STRENGTH,
4.0
)
return false return false
} }
override fun stop() { override fun stop() {
BlastFurnace.pumping = false resetAnimator(p)
BlastFurnace.pumper = null
addScenery(PUMP_SCENERY)
super.stop() super.stop()
} }
}) })
}else{
sendDialogue(player,"I need 30 Strength to do this!")
} }
}
else if (BlastFurnace.pumper != null)
sendMessage(p, "Someone else is already doing that.")
else
sendMessage(p, "You need a Strength level of 30 to do that.")
return@on true return@on true
} }
setDest(IntType.SCENERY, BlastConsts.PUMP) { _, _ -> Location.create(1951, 4961, 0) }
/**Logic for the pedals that run the conveyor, rewards Agility XP every tick that they're being on (BlastConsts.PEDALS, IntType.SCENERY, "pedal") { p, n ->
* pedaled but will stop once the conveyor breaks.*/ if (getDynLevel(p, Skills.AGILITY) > 30 && BlastFurnace.pedaler == null) {
forceMove(p, p.location, PEDAL_LOC, 0, 30) {
on(pedals, SCENERY, "pedal") { player, node -> animate(p, PEDAL_ANIM)
if(player.getSkills().getLevel(Skills.AGILITY) >= 30) { removeScenery(PEDAL_SCENERY)
val pedalL = Location(1947, 4966, 0) submitIndividualPulse(p, object : Pulse() {
val pedalF = Location(1946, 4966, 0)
player.properties.teleportLocation = pedalL
submitIndividualPulse(player, object : Pulse() {
override fun pulse(): Boolean { override fun pulse(): Boolean {
face(player, pedalF,) if (p.settings.runEnergy < 1.0) {
animate(player, 2433) teleport(p, PEDAL_DISMOUNT)
BlastFurnace.pedaling = true return true
if (BlastFurnace.beltRunning) rewardXP(player, Skills.AGILITY, 2.0) }
face(p, PEDAL_FACELOC)
animate(p, PEDAL_ANIM)
BlastFurnace.pedaler = p
return false return false
} }
override fun stop() { override fun stop() {
BlastFurnace.pedaling = false resetAnimator(p)
BlastFurnace.pedaler = null
addScenery(PEDAL_SCENERY)
super.stop() super.stop()
} }
}) })
}else{
sendDialogue(player,"I need 30 Agility to do this!")
} }
}
else if (BlastFurnace.pedaler != null)
sendMessage(p, "Someone else is already doing that.")
else
sendMessage(p, "You need an Agility level of 30 to do that.")
return@on true return@on true
} }
setDest(IntType.SCENERY, BlastConsts.PEDALS) { _, _ -> Location.create(1948, 4966, 0) }
/**Lets players use a spade to take coke from the pile of coke*/ on (BlastConsts.COKE, SCENERY, "collect") { player, _ ->
on(coke, SCENERY, "collect") { player, _ ->
if (inInventory(player, Items.SPADE_952, 1)) { if (inInventory(player, Items.SPADE_952, 1)) {
if(removeItem(player, Items.SPADE_952, Container.INVENTORY) && addItem(player, Items.SPADEFUL_OF_COKE_6448, 1)) { if(removeItem(player, Items.SPADE_952, Container.INVENTORY) && addItem(player, Items.SPADEFUL_OF_COKE_6448, 1)) {
lockInteractions(player,1) lockInteractions(player,1)
@@ -127,214 +134,107 @@ class BlastFurnaceListeners : InteractionListener {
return@on true return@on true
} }
/**This code is for handling the coke stove, lets players interact with it with the spadeful of coke on (Items.SPADEFUL_OF_COKE_6448, ITEM, "empty") {p, n ->
* and rewards firemaking XP when shoving coke in there, it will not allow players to shovel more if (removeItem(p, n)) addItem(p, Items.SPADE_952)
* coke in if it is already full.*/ return@on true
}
on(stove, SCENERY,"refuel") { player, _ -> on (BlastConsts.STOVE, SCENERY, "refuel") { player, _ ->
if (inInventory(player, Items.SPADEFUL_OF_COKE_6448, 1) && BlastFurnace.stoveCoke < 30 && player.getSkills().getLevel(Skills.FIREMAKING) >= 30) { if (inInventory(player, Items.SPADEFUL_OF_COKE_6448, 1)) {
if (BlastFurnace.state.cokeInStove >= BlastConsts.COKE_LIMIT) {
sendMessage(player, "The stove is already full of coke!")
return@on true
}
if (getDynLevel(player, Skills.FIREMAKING) < 30) {
sendMessage(player, "You need a Firemaking level of 30 to do this.")
return@on true
}
animate(player, 2442) animate(player, 2442)
lockInteractions(player,2) lock(player,2)
submitIndividualPulse(player, object : Pulse() { submitIndividualPulse(player, object : Pulse() {
override fun pulse(): Boolean { override fun pulse(): Boolean {
return if(removeItem(player, Items.SPADEFUL_OF_COKE_6448, Container.INVENTORY) && addItem(player, Items.SPADE_952, 1)) { return if(removeItem(player, Items.SPADEFUL_OF_COKE_6448, Container.INVENTORY) && addItem(player, Items.SPADE_952, 1)) {
animate(player, 2443) animate(player, 2443)
rewardXP(player, Skills.FIREMAKING, 5.0)
BlastFurnace.state.addCoke(1)
true true
} else { } else {
false false
} }
} }
}) })
rewardXP(player, Skills.FIREMAKING, 5.0)
BlastFurnace.stoveCoke++
}else if(inInventory(player, Items.SPADEFUL_OF_COKE_6448,1) && BlastFurnace.stoveCoke >= 30 && player.getSkills().getLevel(Skills.FIREMAKING) >= 30){
sendDialogue(player,"The coke stove is already full!")
} else if(player.getSkills().getLevel(Skills.FIREMAKING) < 30){
sendDialogue(player,"I need 30 Firemaking to do this")
} else { } else {
sendMessage(player,"You need some coke to do that!") sendMessage(player,"You need some coke to do that!")
} }
return@on true return@on true
} }
/**This block of code handles the logic for putting coal and ores on (BlastConsts.BELT, IntType.SCENERY, "put-ore-on") { p, _ ->
* on the conveyor belt. It won't let you put anything on there if there's no room openDialogue(p, object : DialogueFile() {
* in your blast ore or blast coal player containers, the way it works is that it override fun handle(componentID: Int, buttonID: Int) {
* puts your coal and ore into the blast furnace containers however, the blast furnace will NOT when (stage) {
* smelt your ores because it's waiting for a player attribute to be set by the ore NPCs that this spawns*/ 0 -> sendDialogue(p, "Really place all your ore on the belt?").also { stage++ }
1 -> showTopics(
on(conveyorLoad, SCENERY, "put-ore-on") { player, node -> Topic("Yes.", 100, true),
val rocksInInventory = playerOre.filter {inInventory(player, it)} Topic("Nevermind.", END_DIALOGUE, true)
var oreToActuallyAdd = 0 )
var coalToActuallyAdd = 0 100 -> {
if(player.blastCoal.freeSlots() > 0 || player.blastOre.freeSlots() > 0) { end()
player.dialogueInterpreter.sendOptions("Add all your ore to the furnace?", "Yes", "No") BlastFurnace.placeAllOre(p, accountForSkill = true)
player.dialogueInterpreter.addAction { player, button ->
if (button == 2 && rocksInInventory.isNotEmpty()) {
rocksInInventory.forEach { oreID ->
val oreAmount = amountInInventory(player, oreID)
val copperInPot = player.blastOre.getAmount(436)
val tinInPot = player.blastOre.getAmount(438)
if(oreID == Items.COAL_453 && oreAmount <= player.blastCoal.freeSlots()){
coalToActuallyAdd = oreAmount
}else if(oreID == Items.COAL_453 && oreAmount > player.blastCoal.freeSlots() && player.blastCoal.freeSlots() > 0){
coalToActuallyAdd = player.blastCoal.freeSlots()
}
if(oreID != Items.COAL_453 && oreAmount <= player.blastOre.freeSlots()){
oreToActuallyAdd = oreAmount
}else if(oreID != Items.COAL_453 && oreAmount > player.blastOre.freeSlots() && player.blastOre.freeSlots() > 0){
oreToActuallyAdd = player.blastOre.freeSlots()
}
if(oreID == Items.COPPER_ORE_436 && (oreToActuallyAdd + copperInPot) > 14){
while(oreToActuallyAdd + copperInPot > 14){
oreToActuallyAdd--
}
}
if(oreID == Items.TIN_ORE_438 && (oreToActuallyAdd + tinInPot) > 14){
while(oreToActuallyAdd + tinInPot > 14){
oreToActuallyAdd--
}
}
val bar = Bar.forOre(oreID)
if(oreID == Items.COAL_453) {
if (coalToActuallyAdd > 0 && getStatLevel(player, Skills.SMITHING) >= 30) {
if(removeItem(player, Item(oreID, coalToActuallyAdd), Container.INVENTORY) && player.blastCoal.add(Item(Items.COAL_453,coalToActuallyAdd))) {
BlastFurnaceOre(player, BFOreVariant.values()[playerOre.indexOf(oreID)],coalToActuallyAdd).init()
}
}
else if(getStatLevel(player, Skills.SMITHING) < 30){
sendDialogue(player, "My Smithing level is not high enough to use Coal!")
}
else sendDialogue(player, "It looks like the melting pot is already full of coal!")
} else if (bar != null) {
if (oreToActuallyAdd > 0 && getStatLevel(player, Skills.SMITHING) >= bar.level) {
if(removeItem(player, Item(oreID, oreToActuallyAdd), Container.INVENTORY) && player.blastOre.add(Item(oreID,oreToActuallyAdd))) {
BlastFurnaceOre(
player,
BFOreVariant.values()[playerOre.indexOf(oreID)],
oreToActuallyAdd
).init()
}
}
else if(getStatLevel(player, Skills.SMITHING) < bar.level){
sendDialogue(player, "My Smithing level is not high enough to smelt ${bar.name.lowercase()}!")
}
else sendDialogue(player, "It looks like the melting pot is already full of ore!")
}
}
} else if (button == 2 && rocksInInventory.isEmpty()){
sendDialogue(player,"I should make sure that I have some ore before doing this.")
} }
} }
} }
})
return@on true return@on true
} }
/**Added this because clicking on ore then the belt and getting "Nothing interesting happens" is annoying onUseWith (IntType.SCENERY, validOreIds, BlastConsts.BELT) { p, used, _ ->
* it's essentially the same thing as above except without the dialogue because if you're clicking on ore BlastFurnace.placeAllOre(p, used.id, accountForSkill = true)
* and trying to use it on the conveyor belt then you know what you're trying to do.*/ return@onUseWith true
onUseWith(SCENERY,playerOre,conveyorLoad){ player, oreType, _ ->
val amountInInventory = player.inventory.getAmount(oreType.id)
val spaceForCoal = player.blastCoal.freeSlots()
val spaceForOre = player.blastOre.freeSlots()
val copperInPot = player.blastOre.getAmount(436)
val tinInPot = player.blastOre.getAmount(438)
var amountToAdd = 0
if(oreType.id == Items.COAL_453 && amountInInventory <= spaceForCoal){
amountToAdd = amountInInventory
}else if(oreType.id == Items.COAL_453 && amountInInventory > spaceForCoal){
amountToAdd = spaceForOre
} }
if(oreType.id != Items.COAL_453 && amountInInventory <= spaceForOre){
amountToAdd = amountInInventory on(BlastConsts.TEMP_GAUGE, IntType.SCENERY, "read") { p, _ ->
}else if(oreType.id != Items.COAL_453 && amountInInventory > spaceForOre){ openInterface(p, 30)
amountToAdd = spaceForOre return@on true
} }
if(oreType.id == Items.COPPER_ORE_436 && (amountToAdd + copperInPot) > 14){
while(amountToAdd + copperInPot > 14){ on(BlastConsts.DISPENSER, IntType.SCENERY, "search", "take") { p, _ ->
amountToAdd-- openInterface(p, 28)
return@on true
}
onUseWith(SCENERY, Items.BUCKET_OF_WATER_1929, *BlastConsts.DISPENSER){ p, _, _ ->
when (getVarbit(p, BFPlayerState.DISPENSER_STATE)) {
1,2 -> {
var barsCooled = false
for (player in BlastFurnace.playersInArea) {
val state = BlastFurnace.getPlayerState(player)
if (state.barsNeedCooled) {
state.coolBars()
barsCooled = true
} }
} }
if(oreType.id == Items.TIN_ORE_438 && (amountToAdd + tinInPot) > 14){ if (barsCooled) {
while(amountToAdd + tinInPot > 14){ removeItem(p, Items.BUCKET_OF_WATER_1929, Container.INVENTORY)
amountToAdd-- addItem(p, Items.BUCKET_1925)
} }
} }
val bar = Bar.forOre(oreType.id) 0 -> sendDialogue(p,"There's nothing to cool off!")
if(oreType.id == Items.COAL_453) { 3 -> sendDialogue(p,"These bars have already cooled off!")
if (amountToAdd > 0 && getStatLevel(player, Skills.SMITHING) >= 30) {
if(removeItem(player, Item(oreType.id, amountToAdd), Container.INVENTORY) && player.blastCoal.add(Item(oreType.id,amountToAdd))) {
BlastFurnaceOre(
player,
BFOreVariant.values()[playerOre.indexOf(oreType.id)],
amountToAdd
).init()
}
}else if(getStatLevel(player,Skills.SMITHING) < 30){
sendDialogue(player,"My Smithing level is not high enough to use Coal!")
}
else sendDialogue(player, "It looks like the melting pot is already full of ore!")
} else if(bar != null) {
if (amountToAdd > 0 && getStatLevel(player, Skills.SMITHING) >= bar.level) {
player.blastOre.add(Item(oreType.id,amountToAdd))
removeItem(player, Item(oreType.id, amountToAdd), Container.INVENTORY)
BlastFurnaceOre(player, BFOreVariant.values()[playerOre.indexOf(oreType.id)],amountToAdd).init()
}else if(getStatLevel(player,Skills.SMITHING) < bar.level){
sendDialogue(player,"My Smithing level is not high enough to smelt Iron!")
}
else sendDialogue(player, "It looks like the melting pot is already full of ore!")
} }
return@onUseWith true return@onUseWith true
} }
/**This handles interacting with the temperature gauge on the furnace*/ on(BFSceneryController.BROKEN_POT_PIPE, SCENERY, "repair"){ player, _ ->
on(tGauge, SCENERY, "read") { player, _ ->
player.interfaceManager.openComponent(30)
return@on true
}
/**This handles taking bars from the dispenser*/
on(dispenser,SCENERY,"search", "take"){ player, _ ->
if (getVarbit(player, 936) == 0 || getVarbit(player, 936) == 3) {
player.interfaceManager.openComponent(28)
}
return@on true
}
/**Handles using a bucket of water on the bar dispenser*/
onUseWith(SCENERY,Items.BUCKET_OF_WATER_1929,*dispenser){ player, _, _ ->
when (getVarbit(player, 936)) {
2 -> {
removeItem(player,Items.BUCKET_OF_WATER_1929,Container.INVENTORY)
addItem(player,Items.BUCKET_1925)
BlastFurnace.barsHot = false
setVarbit(player, 936, 3)
}
0 -> {
sendDialogue(player,"There's nothing to cool off!")
}
1 -> {
sendDialogue(player,"I should wait until the machine is finished")
}
3 -> {
sendDialogue(player,"These bars have already cooled off!")
}
}
return@onUseWith true
}
/**Handles pipe repairing*/
on(brokenPotPipe,SCENERY,"repair"){ player, _ ->
if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){ if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){
if (!BlastFurnace.state.potPipeBroken) {
sendMessage(player, "You can't fix something that isn't broken.")
return@on true
}
if(inInventory(player,Items.HAMMER_2347,1)) { if(inInventory(player,Items.HAMMER_2347,1)) {
rewardXP(player, Skills.CRAFTING, 50.0) rewardXP(player, Skills.CRAFTING, 50.0)
BlastFurnace.potPipeBroken = false BlastFurnace.state.potPipeBroken = false
} } else {
else {
sendMessage(player, "I need a hammer to do this!") sendMessage(player, "I need a hammer to do this!")
} }
} else { } else {
@@ -343,13 +243,16 @@ class BlastFurnaceListeners : InteractionListener {
return@on true return@on true
} }
on(brokenPumpPipe,SCENERY, "repair"){ player, _ -> on(BFSceneryController.BROKEN_PUMP_PIPE, SCENERY, "repair"){ player, _ ->
if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){ if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){
if (!BlastFurnace.state.pumpPipeBroken) {
sendMessage(player, "You can't fix something that isn't broken.")
return@on true
}
if(inInventory(player,Items.HAMMER_2347,1)) { if(inInventory(player,Items.HAMMER_2347,1)) {
rewardXP(player, Skills.CRAFTING, 50.0) rewardXP(player, Skills.CRAFTING, 50.0)
BlastFurnace.pumpPipeBroken = false BlastFurnace.state.pumpPipeBroken = false
} } else {
else {
sendMessage(player, "I need a hammer to do this!") sendMessage(player, "I need a hammer to do this!")
} }
} else { } else {
@@ -358,13 +261,16 @@ class BlastFurnaceListeners : InteractionListener {
return@on true return@on true
} }
on(brokenBelt,SCENERY,"repair"){ player, _ -> on(BFSceneryController.BROKEN_BELT, SCENERY, "repair"){ player, _ ->
if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){ if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){
if (!BlastFurnace.state.beltBroken) {
sendMessage(player, "You can't fix something that isn't broken.")
return@on true
}
if(inInventory(player,Items.HAMMER_2347,1)) { if(inInventory(player,Items.HAMMER_2347,1)) {
rewardXP(player, Skills.CRAFTING, 50.0) rewardXP(player, Skills.CRAFTING, 50.0)
BlastFurnace.beltBroken = false BlastFurnace.state.beltBroken = false
} } else {
else {
sendMessage(player, "I need a hammer to do this!") sendMessage(player, "I need a hammer to do this!")
} }
} else { } else {
@@ -373,13 +279,16 @@ class BlastFurnaceListeners : InteractionListener {
return@on true return@on true
} }
on(brokenCog,SCENERY,"repair"){ player, _ -> on(BFSceneryController.BROKEN_COG, SCENERY, "repair"){ player, _ ->
if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){ if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){
if (!BlastFurnace.state.cogBroken) {
sendMessage(player, "You can't fix something that isn't broken.")
return@on true
}
if(inInventory(player,Items.HAMMER_2347,1)) { if(inInventory(player,Items.HAMMER_2347,1)) {
rewardXP(player, Skills.CRAFTING, 50.0) rewardXP(player, Skills.CRAFTING, 50.0)
BlastFurnace.cogBroken = false BlastFurnace.state.cogBroken = false
} } else {
else {
sendMessage(player, "I need a hammer to do this!") sendMessage(player, "I need a hammer to do this!")
} }
} else { } else {
@@ -388,9 +297,7 @@ class BlastFurnaceListeners : InteractionListener {
return@on true return@on true
} }
/**Handles filling buckets from the sink*/ on(BlastConsts.SINK, SCENERY,"fill-bucket"){ player, _ ->
on(sink, SCENERY,"fill-bucket"){ player, _ ->
player.pulseManager.run(object : Pulse(1){ player.pulseManager.run(object : Pulse(1){
override fun pulse(): Boolean { override fun pulse(): Boolean {
if(removeItem(player, Items.BUCKET_1925)) if(removeItem(player, Items.BUCKET_1925))
@@ -1,91 +0,0 @@
package content.minigame.blastfurnace
import core.api.*
import core.game.node.entity.npc.AbstractNPC
import core.game.node.entity.player.Player
import core.game.system.task.Pulse
import core.game.world.map.Location
import core.game.world.update.flag.context.Animation
import core.plugin.Initializable
import org.rs09.consts.NPCs
/**On god this is like 95% ceikry and 5% me adding a setAttribute to the clear code
* @author funny alium man*/
@Initializable
class BlastFurnaceOre : AbstractNPC {
//Arios constructor spaghetti
constructor() : super(NPCs.COAL_2562, null, true) {}
private constructor(id: Int, location: Location) : super(id, location) {}
constructor(owner: Player, variant: BFOreVariant, amount: Int) : super(
when(variant){
BFOreVariant.IRON -> NPCs.IRON_ORE_2556
BFOreVariant.COPPER -> NPCs.COPPER_ORE_2555
BFOreVariant.TIN -> NPCs.TIN_ORE_2554
BFOreVariant.COAL -> NPCs.COAL_2562
BFOreVariant.MITHRIL -> NPCs.MITHRIL_ORE_2557
BFOreVariant.ADAMANT -> NPCs.ADAMANTITE_ORE_2558
BFOreVariant.SILVER -> NPCs.SILVER_ORE_2560
BFOreVariant.GOLD -> NPCs.GOLD_ORE_2561
BFOreVariant.RUNITE -> NPCs.RUNITE_ORE_2559
}, Location.create(1942, 4966, 0)) {this.owner = owner; isRespawn = false; }
var owner: Player? = null
var delay = 2
var counter = 3
override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC {
return BlastFurnaceOre(id, location)
}
override fun getIds(): IntArray {
return intArrayOf(
NPCs.IRON_ORE_2556,
NPCs.COPPER_ORE_2555,
NPCs.TIN_ORE_2554,
NPCs.COAL_2562,
NPCs.MITHRIL_ORE_2557,
NPCs.ADAMANTITE_ORE_2558,
NPCs.SILVER_ORE_2560,
NPCs.GOLD_ORE_2561,
NPCs.PERFECT_GOLD_ORE_2563,
NPCs.RUNITE_ORE_2559)
}
override fun handleTickActions() {
if (BlastFurnace.beltRunning) {
delay--
if (delay <= 0 && getWorldTicks() % 2 == 0) { //run every other tick
if (counter > 0) {
properties.teleportLocation = location.transform(0, -1, 0)
counter--
if (counter == 0) {
val animation = Animation(2434)
animate(animation)
owner?.setAttribute("/save:OreInPot",true)
submitIndividualPulse(this, object : Pulse(animationDuration(animation)) {
override fun pulse(): Boolean {
clear()
return true
}
})
}
}
}
}
}
}
enum class BFOreVariant {
IRON,
COPPER,
TIN,
COAL,
MITHRIL,
ADAMANT,
SILVER,
GOLD,
RUNITE
}
@@ -1,58 +0,0 @@
package content.minigame.blastfurnace
import core.api.*
import core.game.node.entity.Entity
import core.game.node.item.Item
import core.game.shops.Shops
import core.game.world.map.zone.ZoneBorders
import core.tools.secondsToTicks
import org.rs09.consts.NPCs
import kotlin.math.min
/**Code for defining the Blast Furnace zone, Blast Furnace will only
* operate and run its logic if there are actual players in this zone
* @author phil lips, ceikry*/
class BlastFurnaceZone : MapArea, TickListener {
var pulseStarted = false
var lastShopRestock = 0
override fun defineAreaBorders(): Array<ZoneBorders> {
return arrayOf(ZoneBorders(1935,4956,1956,4974))
}
override fun areaEnter(entity: Entity) {
if (!pulseStarted){
submitWorldPulse(BlastFurnace.blastPulse)
pulseStarted = true
}
if (entity.isPlayer) {
BlastFurnace.blastFurnacePlayerList.add(entity.asPlayer())
}
}
override fun areaLeave(entity: Entity, logout: Boolean) {
if (entity.isPlayer) {
BlastFurnace.blastFurnacePlayerList.remove(entity.asPlayer())
}
}
override fun tick() {
if (getWorldTicks() - lastShopRestock > secondsToTicks(600)) {
lastShopRestock = getWorldTicks()
restockRandomOre()
}
}
private fun restockRandomOre() {
val shop = Shops.shopsByNpc[NPCs.ORDAN_2564] ?: return
val stockContainer = shop.stockInstances.values.firstOrNull() ?: return
val restockThisTick = shop.stock.random()
stockContainer.add(
Item(
restockThisTick.itemId,
min(30, 100 - stockContainer.getAmount(restockThisTick.itemId))
)
)
}
}
@@ -0,0 +1,76 @@
package content.minigame.blastfurnace
import core.tools.RandomFunction
class BlastState {
var disableBreaking = false
var forceBreaking = false
var potPipeBroken = false
var pumpPipeBroken = false
var beltBroken = false
var cogBroken = false
var ticksElapsed = 0
var stoveTemp = 0
private set
var furnaceTemp = 0
private set
var cokeInStove = 0
private set
fun tick(pumping: Boolean, pedaling: Boolean) {
ticksElapsed++
adjustStoveTemperature()
adjustFurnaceTemperature(pumping)
checkForCokeDecrement()
checkForBreakage(pedaling, pumping)
}
private fun adjustStoveTemperature() {
if (cokeInStove > 0)
stoveTemp = (stoveTemp + 1).coerceAtMost(100)
else stoveTemp = (stoveTemp - 1).coerceAtLeast(0)
}
private fun adjustFurnaceTemperature(pumping: Boolean) {
if (pumping && !pumpPipeBroken && !potPipeBroken && !beltBroken && !cogBroken) {
when (stoveTemp) {
in 1..32 -> furnaceTemp += 1
in 32..66 -> furnaceTemp += 2
in 67..100 -> furnaceTemp += 3
}
} else furnaceTemp--
furnaceTemp = furnaceTemp
.coerceAtLeast(0)
.coerceAtMost(100)
}
private fun checkForBreakage(pedaling: Boolean, pumping: Boolean) {
if (disableBreaking) return
if (pumping && (!potPipeBroken || !pumpPipeBroken)) {
if (RandomFunction.roll(50) || forceBreaking) {
if (RandomFunction.nextBool()) potPipeBroken = true
else pumpPipeBroken = true
}
}
if (pedaling && (!beltBroken || !cogBroken)) {
if (RandomFunction.roll(50) || forceBreaking)
beltBroken = true
else if (RandomFunction.roll(50) || forceBreaking)
cogBroken = true
}
}
private fun checkForCokeDecrement() {
if (ticksElapsed % 10 == 0)
cokeInStove = (cokeInStove - 1).coerceAtLeast(0)
}
fun addCoke (amount: Int) {
cokeInStove += amount.coerceAtMost(BlastConsts.COKE_LIMIT - cokeInStove)
}
}
@@ -1,24 +0,0 @@
package content.minigame.blastfurnace
import core.game.interaction.InterfaceListener
/**Handles adding and removing players to the temp gauge viewing list.
* Only updates the gauge if people are actually looking at it
* @author phil lips*/
class PhunnyGaugeTempInterfaceListener : InterfaceListener {
override fun defineInterfaceListeners() {
onOpen(30) {player, component ->
BlastFurnace.gaugeViewList.add(player)
return@onOpen true
}
onClose(30) {player, component ->
BlastFurnace.gaugeViewList.remove(player)
return@onClose true
}
}
}
@@ -1,18 +1,19 @@
package content.region.misc.keldagrim.handlers package content.region.misc.keldagrim.handlers
import content.minigame.blastfurnace.BlastFurnace
import core.api.* import core.api.*
import core.cache.def.impl.SceneryDefinition import core.cache.def.impl.SceneryDefinition
import core.game.dialogue.DialoguePlugin
import core.game.interaction.OptionHandler import core.game.interaction.OptionHandler
import core.game.node.Node import core.game.node.Node
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.item.Item import core.game.node.item.Item
import core.game.node.scenery.Scenery
import core.game.world.map.Location import core.game.world.map.Location
import core.plugin.Initializable import core.plugin.Initializable
import core.plugin.Plugin import core.plugin.Plugin
import core.game.dialogue.DialogueFile import core.game.dialogue.DialogueFile
import core.game.node.entity.skill.Skills import core.game.dialogue.DialoguePlugin
import core.tools.END_DIALOGUE
import org.rs09.consts.Items
/** /**
* File that contains several plugins relating to Keldagrim, * File that contains several plugins relating to Keldagrim,
@@ -40,16 +41,6 @@ class KeldagrimOptionHandlers : OptionHandler() {
5998 -> player.properties.teleportLocation = Location.create(2780, 10161) 5998 -> player.properties.teleportLocation = Location.create(2780, 10161)
} }
} }
"climb-up" -> {
when(node.id){
9138 -> player.properties.teleportLocation = Location.create(2930, 10197, 0)
}
}
"climb-down" -> {
when(node.id){
9084 -> player.dialogueInterpreter.open(BlastFurnaceDoorDialogue(),Scenery(9084, location(2929,10196,0)))
}
}
"open" -> { "open" -> {
when(node.id){ when(node.id){
28094 -> player.dialogueInterpreter.open(GETrapdoorDialogueID) 28094 -> player.dialogueInterpreter.open(GETrapdoorDialogueID)
@@ -113,38 +104,23 @@ class GETrapdoorDialogue(player: Player? = null) : DialoguePlugin(player){
} }
class BlastFurnaceDoorDialogue : DialogueFile(){ class BlastFurnaceDoorDialogue(val fee: Int) : DialogueFile(){
var init = true var init = true
override fun handle(componentID: Int, buttonID: Int) { override fun handle(componentID: Int, buttonID: Int) {
if (init) {
stage = if (player!!.getSkills().getLevel(Skills.SMITHING) >= 60) {
100
} else {
5
}
init = false
}
when(stage){ when(stage){
5 -> sendDialogue(player!!,"You must be Smithing Level 60 or higher in order to enter the Blast Furnace").also { stage = 10 } 0 -> sendDialogue(player!!,"You must be Smithing Level 60 or higher in order to enter the Blast Furnace").also { stage++ }
10 -> sendDialogue(player!!,"However, you may enter if you pay the entrance fee").also { stage = 20 } 1 -> sendDialogue(player!!,"However, you may enter for 10 minutes if you pay the entrance fee.<br>($fee gp)").also { stage++ }
20 -> options("Yes","No").also { stage = 30 } 2 -> options("Yes","No").also { stage++ }
30 -> when(buttonID){ 3 -> when(buttonID){
1 -> { 1 -> {
if(player!!.equipment.contains(6465,1) && player!!.inventory.contains(995, 1250)){ if (removeItem(player!!, Item(Items.COINS_995, fee)))
removeItem(player!!, Item(995, 1250), Container.INVENTORY) BlastFurnace.enter(player!!, true)
player!!.setAttribute("BlastTimer",1000) else
player?.properties?.teleportLocation = Location.create(1940, 4958, 0).also { end() } sendDialogue(player!!, "You don't have enough gold to cover the entrance fee!")
stage = END_DIALOGUE
}
20 -> sendDialogue(player!!,"Then get out of here!").also { stage = END_DIALOGUE }
} }
else if (player!!.inventory.contains(995, 2500)) {
removeItem(player!!, Item(995, 2500), Container.INVENTORY)
player!!.setAttribute("BlastTimer",1000)
player?.properties?.teleportLocation = Location.create(1940, 4958, 0).also { end() }
} else sendDialogue(player!!, "You don't have enough gold to pay the fee!").also { stage = 40 }
}
20 -> sendDialogue(player!!,"Then get out of here!").also { stage = 40 }
}
40 -> end()
100 -> player?.properties?.teleportLocation = Location.create(1940, 4958, 0).also { stage = 40 }
} }
} }
} }
+5 -1
View File
@@ -472,6 +472,10 @@ fun addScenery (sceneryId: Int, location: Location, rotation: Int = 0, type: Int
return scenery return scenery
} }
fun addScenery (scenery: Scenery) {
SceneryBuilder.add(scenery)
}
/** /**
* Remove a scenery from the world * Remove a scenery from the world
* @param scenery the Scenery object to remove. * @param scenery the Scenery object to remove.
@@ -1271,7 +1275,7 @@ fun truncateLoc (mover: Entity, destination: Location) : Pair<Boolean,Location>
* @param callback (optional) a callback called when the forced movement completes * @param callback (optional) a callback called when the forced movement completes
* @see NOTE: There are 30 client cycles per second. * @see NOTE: There are 30 client cycles per second.
*/ */
fun forceMove (player: Player, start: Location, dest: Location, startArrive: Int, destArrive: Int, dir: Direction? = null, anim: Int = -1, callback: (()->Unit)? = null) { fun forceMove (player: Player, start: Location, dest: Location, startArrive: Int, destArrive: Int, dir: Direction? = null, anim: Int = 819, callback: (()->Unit)? = null) {
var direction: Direction var direction: Direction
if (dir == null) { if (dir == null) {
@@ -0,0 +1,44 @@
package content.activity.blastfurnace
import TestUtils
import content.minigame.blastfurnace.BFBeltOre
import content.minigame.blastfurnace.BlastFurnace
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.rs09.consts.Items
class BFBeltOreTests {
init {TestUtils.preTestSetup()}
@Test fun oreShouldMoveCloserToPotWhenTicked() {
TestUtils.getMockPlayer("bf-oremove-test").use { p ->
p.location = BFBeltOre.ORE_START_LOC
val ore = BlastFurnace.addOreToBelt(p, Items.IRON_ORE_440, 5)
Assertions.assertEquals(BFBeltOre.ORE_START_LOC, ore.location)
Assertions.assertEquals(BFBeltOre.ORE_START_LOC, ore.npcInstance?.location)
ore.tick()
val expectedLoc = BFBeltOre.ORE_START_LOC.transform(0, -1, 0)
Assertions.assertEquals(expectedLoc, ore.location)
Assertions.assertEquals(expectedLoc, ore.npcInstance?.properties?.teleportLocation)
TestUtils.advanceTicks(1, false)
ore.tick()
Assertions.assertEquals(expectedLoc, ore.npcInstance?.location)
}
}
@Test fun oreShouldBeAddedToOreContainerAfterReachingEnd() {
TestUtils.getMockPlayer("bf-oremoveadd-test").use { p ->
p.location = BFBeltOre.ORE_START_LOC
val ore = BlastFurnace.addOreToBelt(p, Items.IRON_ORE_440, 5)
for (i in 0 until 4)
if (ore.tick()) BlastFurnace.getPlayerState(p).oresOnBelt.remove(ore)
val container = BlastFurnace.getOreContainer(p)
Assertions.assertEquals(5, container.getOreAmount(Items.IRON_ORE_440))
Assertions.assertEquals(0, BlastFurnace.getAmountOnBelt(p, Items.IRON_ORE_440))
}
}
}
@@ -0,0 +1,101 @@
package content.activity.blastfurnace
import TestUtils
import content.global.skill.smithing.smelting.Bar
import content.minigame.blastfurnace.BFPlayerState
import content.minigame.blastfurnace.BlastFurnace
import core.api.addItem
import core.api.amountInInventory
import core.api.setVarbit
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.rs09.consts.Items
class BFPlayerStateTests {
init { TestUtils.preTestSetup() }
@Test fun processOreIntoBarsShouldDoNothingIfBarsNotCooled() {
TestUtils.getMockPlayer("bf-barsnotcooled").use { p ->
val state = BlastFurnace.getPlayerState(p)
state.container.addOre(Items.IRON_ORE_440, 28)
Assertions.assertEquals(true, state.processOresIntoBars())
Assertions.assertEquals(28, state.container.getBarAmount(Bar.IRON))
state.container.addCoal(40)
state.container.addOre(Items.RUNITE_ORE_451, 10)
Assertions.assertEquals(false, state.processOresIntoBars())
Assertions.assertEquals(0, state.container.getBarAmount(Bar.RUNITE))
}
}
@Test fun processOreIntoBarsShouldDoNothingIfBarsUnchecked() {
TestUtils.getMockPlayer("bf-barsnotchecked").use { p ->
val state = BlastFurnace.getPlayerState(p)
state.container.addOre(Items.IRON_ORE_440, 28)
Assertions.assertEquals(true, state.processOresIntoBars())
state.coolBars()
state.container.addCoal(40)
state.container.addOre(Items.RUNITE_ORE_451, 10)
Assertions.assertEquals(false, state.processOresIntoBars())
Assertions.assertEquals(0, state.container.getBarAmount(Bar.RUNITE))
state.checkBars()
Assertions.assertEquals(true, state.processOresIntoBars())
Assertions.assertEquals(10, state.container.getBarAmount(Bar.RUNITE))
}
}
@Test fun shouldBeAbleToClaimBars() {
TestUtils.getMockPlayer("bf-barclaiminig").use { p ->
val state = BlastFurnace.getPlayerState(p)
state.container.addOre(Items.IRON_ORE_440, 28)
Assertions.assertEquals(true, state.processOresIntoBars())
state.coolBars()
Assertions.assertEquals(true, state.claimBars(Bar.IRON, 5))
Assertions.assertEquals(5, amountInInventory(p, Items.IRON_BAR_2351))
Assertions.assertEquals(23, state.container.getBarAmount(Bar.IRON))
}
}
@Test fun shouldNotBeAbleToClaimMoreBarsThanFreeInventorySlots() {
TestUtils.getMockPlayer("bf-claimbarslessslots").use { p ->
val state = BlastFurnace.getPlayerState(p)
state.container.addOre(Items.IRON_ORE_440, 28)
Assertions.assertEquals(true, state.processOresIntoBars())
state.coolBars()
addItem(p, Items.ABYSSAL_WHIP_4151, 27)
Assertions.assertEquals(true, state.claimBars(Bar.IRON, 5))
Assertions.assertEquals(1, amountInInventory(p, Items.IRON_BAR_2351))
Assertions.assertEquals(27, state.container.getBarAmount(Bar.IRON))
}
}
@Test fun claimBarsShouldDoNothingAndGrantNoItemIfInventoryFull() {
TestUtils.getMockPlayer("bf-claimbarsnoslots").use { p ->
val state = BlastFurnace.getPlayerState(p)
state.container.addOre(Items.IRON_ORE_440, 28)
Assertions.assertEquals(true, state.processOresIntoBars())
state.coolBars()
addItem(p, Items.ABYSSAL_WHIP_4151, 28)
Assertions.assertEquals(false, state.claimBars(Bar.IRON, 5))
Assertions.assertEquals(0, amountInInventory(p, Items.IRON_BAR_2351))
Assertions.assertEquals(28, state.container.getBarAmount(Bar.IRON))
}
}
@Test fun claimBarsShouldDoNothingIfBarsNotCooled() {
TestUtils.getMockPlayer("bf-claimbarsnotcooled").use { p ->
val state = BlastFurnace.getPlayerState(p)
state.container.addOre(Items.IRON_ORE_440, 28)
Assertions.assertEquals(true, state.processOresIntoBars())
Assertions.assertEquals(false, state.claimBars(Bar.IRON, 5))
}
}
}
@@ -0,0 +1,42 @@
package content.activity.blastfurnace
import content.minigame.blastfurnace.BFSceneryController
import core.api.getScenery
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
class BFSceneryControllerTests {
init { TestUtils.preTestSetup(); BFSceneryController().resetAllScenery() }
@Test
fun updateBrokenShouldReplaceWithCorrectVariants() {
val scenCont = BFSceneryController()
scenCont.updateBreakable(true, true, true, true)
Assertions.assertEquals(BFSceneryController.BROKEN_BELT, getScenery(BFSceneryController.beltGearRight)?.id)
Assertions.assertEquals(BFSceneryController.BROKEN_COG, getScenery(BFSceneryController.cogRightLoc)?.id)
Assertions.assertEquals(BFSceneryController.BROKEN_POT_PIPE, getScenery(BFSceneryController.potPipeLoc)?.id)
Assertions.assertEquals(BFSceneryController.BROKEN_PUMP_PIPE, getScenery(BFSceneryController.pumpPipeLoc)?.id)
scenCont.updateBreakable(false, false, false, false)
Assertions.assertEquals(BFSceneryController.DEFAULT_BELT, getScenery(BFSceneryController.beltGearRight)?.id)
Assertions.assertEquals(BFSceneryController.DEFAULT_COG, getScenery(BFSceneryController.cogRightLoc)?.id)
Assertions.assertEquals(BFSceneryController.DEFAULT_POT_PIPE, getScenery(BFSceneryController.potPipeLoc)?.id)
Assertions.assertEquals(BFSceneryController.DEFAULT_PUMP_PIPE, getScenery(BFSceneryController.pumpPipeLoc)?.id)
}
@Test
fun stoveIdShouldCorrespondToTemperature() {
val testData = arrayOf(
Pair(0, BFSceneryController.STOVE_COLD),
Pair(40, BFSceneryController.STOVE_WARM),
Pair(80, BFSceneryController.STOVE_HOT)
)
for ((temp, expectedId) in testData) {
val cont = BFSceneryController()
cont.resetAllScenery()
cont.updateStove(temp)
Assertions.assertEquals(expectedId, getScenery(BFSceneryController.stoveLoc)!!.id)
}
}
}
@@ -0,0 +1,245 @@
package content.activity.blastfurnace
import TestUtils
import content.minigame.blastfurnace.BlastFurnace
import content.minigame.blastfurnace.BlastFurnaceListeners
import content.minigame.blastfurnace.BlastConsts
import core.ServerConstants
import core.api.addItem
import core.api.amountInInventory
import core.game.node.entity.skill.Skills
import core.game.world.map.Location
import core.game.world.map.RegionManager
import org.json.simple.JSONObject
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.rs09.consts.Items
class BlastFurnaceAreaTests {
@Test fun shouldBeAbleToEnterBfArea() {
TestUtils.getMockPlayer("bf-enterable").use { p ->
p.skills.setStaticLevel(Skills.SMITHING, 60)
p.location = Location(2932, 10195, 0)
TestUtils.simulateInteraction(p, RegionManager.getObject(BlastConsts.STAIRLOC_ENTRANCE)!!, 0)
TestUtils.advanceTicks(5, false)
Assertions.assertEquals(BlastConsts.ENTRANCE_LOC, p.location)
}
}
@Test fun shouldBeAbleToLeaveBfArea() {
TestUtils.getMockPlayer("bf-leavable").use { p ->
p.location = BlastConsts.ENTRANCE_LOC
TestUtils.simulateInteraction(p, RegionManager.getObject(BlastConsts.STAIRLOC_EXIT)!!, 0)
TestUtils.advanceTicks(5, false)
Assertions.assertEquals(BlastConsts.EXIT_LOC, p.location)
}
}
@Test fun shouldNotBeAbleToEnterWithLessThan60Smithing() {
TestUtils.getMockPlayer("bf-60smith").use { p ->
p.location = Location(2932, 10195, 0)
TestUtils.simulateInteraction(p, RegionManager.getObject(BlastConsts.STAIRLOC_ENTRANCE)!!, 0)
TestUtils.advanceTicks(5, false)
Assertions.assertNotEquals(BlastConsts.ENTRANCE_LOC, p.location)
}
}
@Test fun getFeePriceReturnsExpectedValues() {
val testData = arrayOf(
Triple(false, 10, 2500),
Triple(true, 10, 1250),
Triple(false, 60, 0),
Triple(true, 60, 0),
Triple(false, 59, 2500),
Triple(true, 59, 1250)
)
for ((hasCharos, smithLevel, expected) in testData)
Assertions.assertEquals(expected, BlastFurnace.getEntranceFee(hasCharos, smithLevel))
}
@Test fun enterWithFeeShouldKickPlayerOutAfter10Minutes() {
TestUtils.getMockPlayer("bf-fee-kickout").use { p ->
BlastFurnace.enter(p, true)
TestUtils.advanceTicks(BlastConsts.FEE_ENTRANCE_DURATION + 2, false)
Assertions.assertNotEquals(BlastConsts.ENTRANCE_LOC, p.location)
}
}
@Test fun shouldBeAbleToLeaveAndEnterFreelyWhileTimerActive() {
TestUtils.getMockPlayer("bf-fee-reentry").use { p ->
BlastFurnace.enter(p, true)
TestUtils.advanceTicks(2, false)
Assertions.assertEquals(BlastConsts.ENTRANCE_LOC, p.location)
TestUtils.simulateInteraction(p, RegionManager.getObject(BlastConsts.STAIRLOC_EXIT)!!, 0)
TestUtils.advanceTicks(2, false)
Assertions.assertEquals(BlastConsts.EXIT_LOC, p.location)
TestUtils.simulateInteraction(p, RegionManager.getObject(BlastConsts.STAIRLOC_ENTRANCE)!!, 0)
TestUtils.advanceTicks(2, false)
Assertions.assertEquals(BlastConsts.ENTRANCE_LOC, p.location)
p.location = BlastConsts.EXIT_LOC
TestUtils.advanceTicks(BlastConsts.FEE_ENTRANCE_DURATION, false)
//should not allow free reentry if timer has run out
TestUtils.simulateInteraction(p, RegionManager.getObject(BlastConsts.STAIRLOC_ENTRANCE)!!, 0)
TestUtils.advanceTicks(2, false)
Assertions.assertEquals(BlastConsts.EXIT_LOC, p.location)
}
}
@Test fun playerShouldOnlyBeTeleportedIfInsideBFArea() {
TestUtils.getMockPlayer("bf-fee-timeout-notele-outside-bf").use { p ->
BlastFurnace.enter(p, true)
TestUtils.advanceTicks(2, false)
Assertions.assertEquals(BlastConsts.ENTRANCE_LOC, p.location)
p.location = ServerConstants.HOME_LOCATION
TestUtils.advanceTicks(BlastConsts.FEE_ENTRANCE_DURATION, false)
Assertions.assertEquals(ServerConstants.HOME_LOCATION, p.location)
}
}
@Test fun playerShouldBeAbleToPlaceOreOnBelt() {
TestUtils.getMockPlayer("bf-placeoreonbelt").use { p ->
addItem(p, Items.COAL_453, 2)
BlastFurnace.placeAllOre(p)
Assertions.assertEquals(2, BlastFurnace.getAmountOnBelt(p, Items.COAL_453))
Assertions.assertEquals(0, amountInInventory(p, Items.COAL_453))
}
}
@Test fun playerShouldNotBeAbleToPlaceMoreOreThanCanFitOnBelt() {
TestUtils.getMockPlayer("bf-toomuchoreonbelt").use { p ->
val cont = BlastFurnace.getOreContainer(p)
cont.addCoal(BlastConsts.COAL_LIMIT - 15)
cont.addOre(Items.IRON_ORE_440, BlastConsts.ORE_LIMIT - 13)
addItem(p, Items.COAL_453, 15)
addItem(p, Items.IRON_ORE_440, 13)
BlastFurnace.placeAllOre(p)
addItem(p, Items.COAL_453, 15)
addItem(p, Items.IRON_ORE_440, 13)
BlastFurnace.placeAllOre(p)
Assertions.assertEquals(15, amountInInventory(p, Items.COAL_453))
Assertions.assertEquals(13, amountInInventory(p, Items.IRON_ORE_440))
}
}
@Test fun beltOreLimitsShouldAccountForCreatedBars() {
TestUtils.getMockPlayer("bf-baraccountedfor").use { p ->
val cont = BlastFurnace.getOreContainer(p)
cont.addOre(Items.IRON_ORE_440, BlastConsts.ORE_LIMIT - 15)
cont.convertToBars()
addItem(p, Items.IRON_ORE_440, 20)
BlastFurnace.placeAllOre(p)
Assertions.assertEquals(5, amountInInventory(p, Items.IRON_ORE_440))
}
}
@Test fun playerShouldBeAbleToOnlyPlaceSpecificOreOnBelt() {
TestUtils.getMockPlayer("bf-specific-oreplace").use { p ->
addItem(p, Items.IRON_ORE_440, 5)
addItem(p, Items.COAL_453, 3)
addItem(p, Items.RUNITE_ORE_451, 10)
BlastFurnace.placeAllOre(p, Items.RUNITE_ORE_451)
Assertions.assertEquals(0, amountInInventory(p, Items.RUNITE_ORE_451))
Assertions.assertEquals(5, amountInInventory(p, Items.IRON_ORE_440))
Assertions.assertEquals(3, amountInInventory(p, Items.COAL_453))
}
}
@Test fun playerShouldNotBeAbleToPlaceOresTheyLackTheLevelFor() {
TestUtils.getMockPlayer("bf-levelgate-oreplace").use { p ->
addItem(p, Items.RUNITE_ORE_451, 10)
BlastFurnace.placeAllOre(p, Items.RUNITE_ORE_451, accountForSkill = true)
Assertions.assertEquals(10, amountInInventory(p, Items.RUNITE_ORE_451))
}
}
@Test fun shouldNotBeAbleToOccupyExtraBronzeSlotsWithMoreThan28TinOrCopper() {
TestUtils.getMockPlayer("bf-bronze-orelimit").use { p ->
//Edge case - bronze bars have an edge case that allows 28 of both {copper, tin}, so this needs to make sure you can't, for example, add 56 copper.
addItem(p, Items.COPPER_ORE_436, 28)
BlastFurnace.placeAllOre(p)
addItem(p, Items.COPPER_ORE_436, 28)
BlastFurnace.placeAllOre(p)
Assertions.assertEquals(28, BlastFurnace.getAmountOnBelt(p, Items.COPPER_ORE_436))
Assertions.assertEquals(28, amountInInventory(p, Items.COPPER_ORE_436))
p.inventory.clear()
addItem(p, Items.TIN_ORE_438, 28)
BlastFurnace.placeAllOre(p)
addItem(p, Items.TIN_ORE_438, 28)
BlastFurnace.placeAllOre(p)
Assertions.assertEquals(28, BlastFurnace.getAmountOnBelt(p, Items.TIN_ORE_438))
Assertions.assertEquals(28, amountInInventory(p, Items.TIN_ORE_438))
}
}
@Test fun shouldNotBeAbleToPlaceMoreThan28TotalNonCoalOreOnTheBelt() {
TestUtils.getMockPlayer("bf-orelimit").use { p ->
addItem(p, Items.GOLD_ORE_444, 28)
BlastFurnace.placeAllOre(p)
addItem(p, Items.RUNITE_ORE_451, 28)
BlastFurnace.placeAllOre(p)
Assertions.assertEquals(28, BlastFurnace.getAmountOnBelt(p, Items.GOLD_ORE_444))
Assertions.assertEquals(0, amountInInventory(p, Items.GOLD_ORE_444))
Assertions.assertEquals(0, BlastFurnace.getAmountOnBelt(p, Items.RUNITE_ORE_451))
Assertions.assertEquals(28, amountInInventory(p, Items.RUNITE_ORE_451))
}
}
@Test fun shouldBeAbleToPlace28CopperAndTinOreOnTheBelt() {
TestUtils.getMockPlayer("bf-orelimit").use { p ->
addItem(p, Items.COPPER_ORE_436, 28)
BlastFurnace.placeAllOre(p)
addItem(p, Items.TIN_ORE_438, 28)
BlastFurnace.placeAllOre(p)
Assertions.assertEquals(28, BlastFurnace.getAmountOnBelt(p, Items.COPPER_ORE_436))
Assertions.assertEquals(0, amountInInventory(p, Items.COPPER_ORE_436))
Assertions.assertEquals(28, BlastFurnace.getAmountOnBelt(p, Items.TIN_ORE_438))
Assertions.assertEquals(0, amountInInventory(p, Items.TIN_ORE_438))
}
}
@Test fun BFAreaShouldPersistInfoAcrossPlayerRelogs() {
TestUtils.getMockPlayer("bf-persistence").use { p ->
var container = BlastFurnace.getOreContainer(p)
container.addOre(Items.IRON_ORE_440, 15)
container.addCoal(40)
BlastFurnace.addOreToBelt(p, Items.IRON_ORE_440, 4)
BlastFurnace.addOreToBelt(p, Items.RUNITE_ORE_451, 10)
BlastFurnace.addOreToBelt(p, Items.COAL_453, 20)
val area = BlastFurnace()
val saveObj = JSONObject()
area.savePlayer(p, saveObj)
BlastFurnace.playerStates.clear()
area.parsePlayer(p, saveObj)
container = BlastFurnace.getOreContainer(p)
Assertions.assertEquals(15, container.getOreAmount(Items.IRON_ORE_440))
Assertions.assertEquals(40, container.coalAmount())
Assertions.assertEquals(4, BlastFurnace.getAmountOnBelt(p, Items.IRON_ORE_440))
Assertions.assertEquals(10, BlastFurnace.getAmountOnBelt(p, Items.RUNITE_ORE_451))
Assertions.assertEquals(20, BlastFurnace.getAmountOnBelt(p, Items.COAL_453))
}
}
companion object {
init {
TestUtils.preTestSetup()
BlastFurnaceListeners().defineListeners()
}
}
}
@@ -0,0 +1,169 @@
package content.activity.blastfurnace
import content.global.skill.smithing.smelting.Bar
import content.minigame.blastfurnace.BFOreContainer
import content.minigame.blastfurnace.BlastConsts
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.rs09.consts.Items
class BlastFurnaceOreContainerTests {
@Test fun shouldBeAbleToAddCoal() {
val cont = BFOreContainer()
cont.addCoal(15)
Assertions.assertEquals(15, cont.coalAmount())
}
@Test fun addCoalShouldReturnExtraAmountIfAddingMoreThanPossible() {
val cont = BFOreContainer()
cont.addCoal(BlastConsts.COAL_LIMIT - 26)
Assertions.assertEquals(2, cont.addCoal(28))
}
@Test fun shouldBeAbleToAddOres() {
val cont = BFOreContainer()
cont.addOre (Items.IRON_ORE_440, 20)
Assertions.assertEquals(20, cont.getOreAmount(Items.IRON_ORE_440))
}
@Test fun addOreShouldReturnExtraAmountIfAddingMoreThanPossible() {
val cont = BFOreContainer()
cont.addOre(Items.IRON_ORE_440, BlastConsts.ORE_LIMIT - 2)
Assertions.assertEquals(3, cont.addOre(Items.IRON_ORE_440, 5))
}
@Test fun addOreShouldReturnExtraAmountWhenAddingMoreCopperOrTinThanPossible() {
val contTin = BFOreContainer()
Assertions.assertEquals(28, contTin.addOre(Items.TIN_ORE_438, 56))
val contCopper = BFOreContainer()
Assertions.assertEquals(28, contCopper.addOre(Items.COPPER_ORE_436, 56))
}
@Test fun convertToBarsShouldYieldExpectedResults() {
data class Data (val coalAmount: Int, val oreAmount: Array<Pair<Int,Int>>, val expectedOreAmounts: Array<Pair<Int,Int>>, val expectedCoalAmount: Int, val expectedBarResult: Array<Pair<Bar,Int>>)
val testData = arrayOf(
Data( //standard case - 20 iron with no coal creates 20 iron bars
0, arrayOf(Pair(Items.IRON_ORE_440, 20)),
arrayOf(Pair(Items.IRON_ORE_440, 0)), 0, arrayOf(Pair(Bar.IRON, 20))
),
Data ( //edge case - 20 iron with 10 coal produces 10 steel and 10 iron
10, arrayOf(Pair(Items.IRON_ORE_440, 20)),
arrayOf(Pair(Items.IRON_ORE_440, 0)), 0, arrayOf(Pair(Bar.STEEL, 10), Pair(Bar.IRON, 10))
),
Data ( //standard case - 20 coal with 10 mithril produces 10 mithril bars
20, arrayOf(Pair(Items.MITHRIL_ORE_447, 10)),
arrayOf(Pair(Items.MITHRIL_ORE_447, 0)), 0, arrayOf(Pair(Bar.MITHRIL, 10))
),
Data ( //standard case - 30 coal with 10 adamantite produces 10 addy bars
30, arrayOf(Pair(Items.ADAMANTITE_ORE_449, 10)),
arrayOf(Pair(Items.ADAMANTITE_ORE_449, 0)), 0, arrayOf(Pair(Bar.ADAMANT, 10))
),
Data ( //standard case - 40 coal with 10 runite produces 10 runite bars
40, arrayOf(Pair(Items.RUNITE_ORE_451, 10)),
arrayOf(Pair(Items.RUNITE_ORE_451, 0)), 0, arrayOf(Pair(Bar.RUNITE, 10))
),
Data ( //semi-edge case - 28 gold with 150 coal produces 28 gold bars with 150 coal remaining (doesn't use any coal when not needed)
150, arrayOf(Pair(Items.GOLD_ORE_444, 28)),
arrayOf(Pair(Items.GOLD_ORE_444, 0)), 150, arrayOf(Pair(Bar.GOLD, 28))
),
Data ( //edge case - 18 silver and 10 runite with 58 coal produces 18 silver bars and 10 runite bars with 18 coal remaining
58, arrayOf(Pair(Items.SILVER_ORE_442, 18), Pair(Items.RUNITE_ORE_451, 10)),
arrayOf(Pair(Items.SILVER_ORE_442, 0), Pair(Items.RUNITE_ORE_451, 0)), 18, arrayOf(Pair(Bar.SILVER, 18), Pair(Bar.RUNITE, 10))
),
Data ( //edge case - only 20 coal but 10 runite (half of what's needed) produces 5 runite bars, with 5 runite ore and 0 coal remaining.
20, arrayOf(Pair(Items.RUNITE_ORE_451, 10)),
arrayOf(Pair(Items.RUNITE_ORE_451, 5)), 0, arrayOf(Pair(Bar.RUNITE, 5))
),
Data (//technically an edge case - 28 copper and 28 tin makes 28 bronze with nothing leftover.
0, arrayOf(Pair(Items.COPPER_ORE_436, 28), Pair(Items.TIN_ORE_438, 28)),
arrayOf(Pair(Items.COPPER_ORE_436, 0), Pair(Items.TIN_ORE_438, 0)), 0, arrayOf(Pair(Bar.BRONZE, 28))
),
Data (//edge case - 10 copper and no tin makes nothing with 10 copper leftover
0, arrayOf(Pair(Items.COPPER_ORE_436, 10)),
arrayOf(Pair(Items.COPPER_ORE_436, 10)), 0, arrayOf(Pair(Bar.BRONZE, 0))
),
Data (//edge case - 14 copper and 5 tin make 5 bronze bars with 9 copper leftover
0, arrayOf(Pair(Items.COPPER_ORE_436, 14), Pair(Items.TIN_ORE_438, 5)),
arrayOf(Pair(Items.COPPER_ORE_436, 9), Pair(Items.TIN_ORE_438, 0)), 0, arrayOf(Pair(Bar.BRONZE, 5))
)
)
var index = 0
for ((initialCoal, initialOres, expectedOres, expectedCoal, expectedBars) in testData) {
val cont = BFOreContainer()
cont.addCoal(initialCoal)
for ((ore, amount) in initialOres) cont.addOre(ore, amount)
cont.convertToBars()
for ((ore, amount) in expectedOres)
Assertions.assertEquals(amount, cont.getOreAmount(ore), "Problem testcase was $index - Missing $ore")
for ((bar, amount) in expectedBars)
Assertions.assertEquals(amount, cont.getBarAmount(bar), "Problem testcase was $index - Missing ${bar.name}")
Assertions.assertEquals(expectedCoal, cont.coalAmount(), "Problem testcase was $index")
index++
}
}
@Test fun convertToBarsShouldNotConsumeMaterialsForAlreadyFilledBarType() {
val cont = BFOreContainer()
cont.addOre(Items.IRON_ORE_440, 28)
cont.convertToBars()
Assertions.assertEquals(28, cont.getBarAmount(Bar.IRON))
cont.addOre(Items.IRON_ORE_440, 28)
cont.convertToBars()
Assertions.assertEquals(28, cont.getBarAmount(Bar.IRON))
Assertions.assertEquals(28, cont.getOreAmount(Items.IRON_ORE_440))
}
@Test fun oreContainerShouldCleanlySerializeAndDeserializeFromJson() {
val cont = BFOreContainer()
cont.addOre(Items.IRON_ORE_440, 28)
cont.convertToBars()
cont.addOre(Items.RUNITE_ORE_451, 15)
cont.addOre(Items.MITHRIL_ORE_447, 13)
cont.addCoal(150)
val json = cont.toJson()
val deserialized = BFOreContainer.fromJson(json)
Assertions.assertEquals(28, deserialized.getBarAmount(Bar.IRON))
Assertions.assertEquals(15, cont.getOreAmount(Items.RUNITE_ORE_451))
Assertions.assertEquals(13, cont.getOreAmount(Items.MITHRIL_ORE_447))
Assertions.assertEquals(150, cont.coalAmount())
}
@Test fun shouldBeAbleToRemoveBars() {
val cont = BFOreContainer()
cont.addOre(Items.IRON_ORE_440, 28)
cont.convertToBars()
val bars = cont.takeBars(Bar.IRON, 15)
Assertions.assertEquals(15, bars?.amount)
Assertions.assertEquals(13, cont.getBarAmount(Bar.IRON))
}
@Test fun shouldNotBeAbleToRemoveMoreBarsThanPossible() {
val cont = BFOreContainer()
cont.addOre(Items.IRON_ORE_440, 28)
cont.convertToBars()
val bars = cont.takeBars(Bar.IRON, 50)
Assertions.assertEquals(28, bars?.amount)
Assertions.assertEquals(0, cont.getBarAmount(Bar.IRON))
}
@Test fun convertToBarsShouldReturnXPReward() {
val cont = BFOreContainer()
cont.addOre(Items.IRON_ORE_440, 28)
Assertions.assertEquals(350.0, cont.convertToBars())
}
@Test fun removingBarsWithNoStockReturnsNull() {
val cont = BFOreContainer()
Assertions.assertEquals(null, cont.takeBars(Bar.RUNITE, 1))
}
}
@@ -0,0 +1,95 @@
package content.activity.blastfurnace
import content.minigame.blastfurnace.BlastState
import content.minigame.blastfurnace.BlastConsts
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
class BlastStateTests {
@Test
fun tickPassedWithCokeInStoveShouldIncreaseStoveTemp() {
val state = BlastState(); state.disableBreaking = true
state.addCoke(1)
Assertions.assertEquals(0, state.stoveTemp)
state.tick(true, false)
Assertions.assertEquals(1, state.stoveTemp)
}
@Test
fun stoveTempShouldNeverExceed100() {
val state = BlastState(); state.disableBreaking = true
state.addCoke(BlastConsts.COKE_LIMIT)
for (i in 0 until 150)
state.tick(true, false)
Assertions.assertEquals(100, state.stoveTemp)
}
@Test fun cokeShouldDisappearFromStove() {
val state = BlastState(); state.disableBreaking = true
state.addCoke(1)
for (i in 0 until 10)
state.tick(true, false)
Assertions.assertEquals(0, state.cokeInStove)
}
@Test fun stoveTempShouldLowerWithoutCoke() {
val state = BlastState(); state.disableBreaking = true
state.addCoke(1)
for (i in 0 until 10)
state.tick(true, false)
Assertions.assertEquals(10, state.stoveTemp)
for (i in 0 until 10)
state.tick(true, false)
Assertions.assertEquals(0, state.stoveTemp)
}
@Test
fun stoveTempShouldNeverGoBelow0() {
val state = BlastState(); state.disableBreaking = true
for (i in 0 until 100)
state.tick(true, false)
Assertions.assertEquals(0, state.stoveTemp)
}
@Test fun furnaceTempShouldIncreaseProportionallyToStoveTempWhilePumping() {
val testData = arrayOf(
Pair(0, 0),
Pair(1, 1),
Pair(4, 2),
Pair(7, 3)
)
for ((cokeToAdd, expectedTempRise) in testData) {
val state = BlastState(); state.disableBreaking = true
state.addCoke(cokeToAdd)
for (i in 0 until state.cokeInStove * 10)
state.tick(false, false)
state.tick(true, false)
Assertions.assertEquals(expectedTempRise, state.furnaceTemp)
}
}
@Test fun pumpingShouldNotTransferHeatIfPipesBroken() {
val state1 = BlastState(); state1.disableBreaking = true
state1.addCoke(BlastConsts.COKE_LIMIT)
state1.pumpPipeBroken = true
for (i in 0 until 20) state1.tick(true, false)
Assertions.assertEquals(0, state1.furnaceTemp)
val state2 = BlastState(); state2.disableBreaking = true
state2.addCoke(BlastConsts.COKE_LIMIT)
state2.potPipeBroken = true
for (i in 0 until 20) state2.tick(true, false)
Assertions.assertEquals(0, state2.furnaceTemp)
}
@Test fun pumpingShouldNotTransferHeatIfBeltBroken() {
val state = BlastState(); state.disableBreaking = true
state.addCoke(BlastConsts.COKE_LIMIT)
state.beltBroken = true
for (i in 0 until 20) state.tick(true, false)
Assertions.assertEquals(0, state.furnaceTemp)
}
}