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 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.skill.Skills
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
import core.tools.RandomFunction
import org.json.simple.JSONObject
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:
* Handles most of the Blast Furnace's operating logic, there is some crosstalk between
* this file and BlastFurnaceListeners.kt as well as the BlastFurnaceZone.kt.
* 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*/
class BlastFurnace : MapArea, PersistPlayer, TickListener {
override fun defineAreaBorders(): Array<ZoneBorders> {
return arrayOf(bfArea)
}
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){
it.incrementAttribute("BlastTimer",-1)
} else if(it.getAttribute("BlastTimer",0) <= 0 && it.getSkills().getLevel(Skills.SMITHING) < 60){
it.removeAttribute("BlastTimer")
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
override fun savePlayer(player: Player, save: JSONObject) {
val state = playerStates[player.details.uid]
if (state != null) {
save["bf-state"] = state.toJson()
}
}
/**This handles the coke stove temperature, if there is coke in the
* coke oven the temperature will raise, otherwise the temperature
* will lower, 0-33 is cold, 34-66 is warm, 67-100 is hot*/
fun stoveCokeTemperature() {
if (getWorldTicks() % 10 == 0 && stoveCoke > 0) {
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)
override fun parsePlayer(player: Player, data: JSONObject) {
playerStates.remove(player.details.uid)
if (data.containsKey("bf-state")) {
val stateObj = data["bf-state"] as JSONObject
getPlayerState(player).readJson(stateObj)
}
}
/**This handles the raising and lowering of the furnace temperature
* depending on whether or not the coke stove is hot and if the
* furnace pump is being operated. A hot coke stove will raise the
* temperature the fastest, a warm coke stove will raise it slowly,
* and a cold stove will raise it the slowest*/
fun furnaceTemperature() {
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
override fun areaEnter(entity: Entity) {
if (entity is Player) {
playersInArea.add(entity)
val state = getPlayerState(entity)
for (ore in state.oresOnBelt)
ore.createNpc()
}
}
/**Tells the furnace whether or not to smelt bars depending on if
* the furnace temperature is in the right range. Also contains the
* logic for smelting the players ore given the fact that they actually
* have ore in their ore container and/or have coal in their coal container*/
fun operateFurnace() {
makeBars = furnaceTemp in 51..66
blastFurnacePlayerList.forEach { player ->
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")
override fun areaLeave(entity: Entity, logout: Boolean) {
if (entity is Player) {
playersInArea.remove(entity)
val state = getPlayerState(entity)
for (ore in state.oresOnBelt) {
ore.npcInstance?.clear()
ore.npcInstance = null
}
}
}
/**This handles the conveyor belt, if the pedals are running and the belt
* isn't broken then the conveyor belt will run*/
fun runConveyor() {
if (pedaling && (!beltBroken && !cogBroken)) {
animateScenery(belt1!!, 2435)
animateScenery(belt2!!, 2435)
animateScenery(belt3!!, 2435)
animateScenery(beltLoc1!!, 2436)
animateScenery(beltLoc2!!, 2436)
animateScenery(cogs1!!, 2436)
animateScenery(cogs2!!, 2436)
animateScenery(gearsLoc!!, 2436)
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
override fun tick() {
if (state.beltBroken || state.cogBroken) pedaler = null
if (state.potPipeBroken || state.pumpPipeBroken) pumper = null
state.tick(pumper != null, pedaler != null)
if (playersInArea.size > 0) {
updateVisuals()
if (getWorldTicks() % 2 == 0) {
updatePedaler()
updatePumper()
}
processBars()
}
//Reset each tick
pumper = null
pedaler = null
}
/**Its just one of those days*/
fun breakStuff() {
if (pumping && furnaceTemp > 76) {
if (RandomFunction.random(1, 100) > 20 && (!potPipeBroken || !pumpPipeBroken)) {
if (RandomFunction.random(1, 100) > 50 && !potPipeBroken) {
pipe1 = getScenery(1943, 4961, 0)
potPipeBroken = true
}
if (RandomFunction.random(1, 100) <= 50 && !pumpPipeBroken) {
pipe2 = getScenery(1947, 4961, 0)
pumpPipeBroken = true
}
private fun updatePumper() {
if (pumper != null) {
if (state.stoveTemp == 0) return
if (state.furnaceTemp == 100 && RandomFunction.roll(5)) {
impact(pumper!!, (0.2 * pumper!!.skills.maximumLifepoints).toInt())
sendMessage(pumper!!, "A blast of hot air cooks you a bit.")
pumper!!.pulseManager.clear()
return
}
}
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)
rewardXP(pumper!!, Skills.STRENGTH, 4.0)
}
}
/**This replaces the breakable objects IDs back to their unbroken state*/
fun fixStuff() {
if (!potPipeBroken && pipe1!!.id != 9116) {
pipe1 = getScenery(1943, 4961, 0)
replaceScenery(pipe1!!, 9116, -1)
private fun updatePedaler() {
if (pedaler == null) return
var oresPedaled = false
for (state in playerStates.values) {
if (state.oresOnBelt.isEmpty()) continue
state.updateOres()
oresPedaled = true
}
if (!pumpPipeBroken && pipe2!!.id != 9120) {
pipe2 = getScenery(1947, 4961, 0)
replaceScenery(pipe2!!, 9120, -1)
if (oresPedaled) {
rewardXP(pedaler!!, Skills.AGILITY, 2.0)
pedaler!!.settings.runEnergy -= 2
}
if (!beltBroken && beltLoc2!!.id != 9102) {
beltLoc2 = getScenery(1944, 4967, 0)
replaceScenery(beltLoc2!!, 9102, -1)
}
private fun updateVisuals() {
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 (!cogBroken && cogs2!!.id != 9104) {
cogs2 = getScenery(1945, 4967, 0)
replaceScenery(cogs2!!, 9104, -1)
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
import content.global.skill.smithing.smelting.Bar
import core.api.*
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.Items
/**
* Handles the blast furnace bar stock interface.
* @author phil, bushtail
* @version 2.0
*/
class BlastFurnaceInterfaceListener : InterfaceListener {
override fun defineInterfaceListeners() {
onOpen(28,) { player, component ->
var playerBars = player.blastBars.toArray().filterNotNull()
var bronzeBit = 1
var ironBit = 1
var steelBit = 1
var mithrilBit = 1
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
}
onOpen(Components.BLAST_FURNACE_BAR_STOCK_28) { player, component ->
val state = BlastFurnace.getPlayerState(player)
state.setBarClaimVarbits()
state.checkBars()
return@onOpen true
}
onOpen(Components.BLAST_FURNACE_TEMP_GAUGE_30) {player, _ ->
submitIndividualPulse(player, object : Pulse() {
override fun pulse(): Boolean {
val anim = BlastFurnace.state.furnaceTemp + 2452
animateInterface(player, 30, 4, anim)
return false
}
})
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, _, _ ->
val bar = BFBars.forId(buttonID) ?: return@on false
val isAll = buttonID == bar.allButtonId
val playerAmt = getVarbit(player, bar.varbit)
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)
val (isAll, bar) = getBarForButton(buttonID)
val state = BlastFurnace.getPlayerState(player)
state.claimBars(bar, if (isAll) state.container.getBarAmount(bar) else 1)
return@on true
}
}
internal enum class BFBars(val buttonId: Int, val varbit: Int, val allButtonId: Int = buttonId + 2){
BRONZE(43, 941, 44),
IRON(40, 942, 41),
STEEL(36, 943),
MITHRIL(33, 944),
ADAMANT(30, 945),
RUNITE(27, 946),
SILVER(24, 948),
GOLD(21, 947);
companion object {
private val idMap = values().associateBy { it.buttonId }
fun forId(buttonId: Int): BFBars? {
return idMap[buttonId] ?: idMap[buttonId - 2] ?: idMap[buttonId - 1]
}
private fun getBarForButton (id: Int) : Pair<Boolean, Bar> {
return when (id) {
43,44 -> Pair(id == 44, Bar.BRONZE)
40,41 -> Pair(id == 41, Bar.IRON)
36,38 -> Pair(id == 38, Bar.STEEL)
33,35 -> Pair(id == 35, Bar.MITHRIL)
30,32 -> Pair(id == 32, Bar.ADAMANT)
27,29 -> Pair(id == 29, Bar.RUNITE)
24,26 -> Pair(id == 26, Bar.SILVER)
21,23 -> Pair(id == 23, Bar.GOLD)
else -> Pair(false, Bar.BRONZE)
}
}
}
@@ -1,121 +1,128 @@
package content.minigame.blastfurnace
import content.global.skill.smithing.smelting.Bar
import content.region.misc.keldagrim.handlers.BlastFurnaceDoorDialogue
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.node.entity.skill.Skills
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.map.Location
import core.tools.END_DIALOGUE
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 {
val disLoc = getScenery(1941, 4963, 0)
val brokenPotPipe = 9117
val brokenPumpPipe = 9121
val pump = 9090
val stove = intArrayOf(9085, 9086, 9087)
val coke = 9088
val pedals = 9097
val conveyorLoad = 9100
val dispenser = intArrayOf(9093, 9094, 9095, 9096)
val brokenBelt = 9103
val brokenCog = 9105
val tGauge = 9089
val sink = 9143
val playerOre = intArrayOf(
Items.IRON_ORE_440,
Items.COPPER_ORE_436,
Items.TIN_ORE_438,
Items.COAL_453,
Items.MITHRIL_ORE_447,
Items.ADAMANTITE_ORE_449,
Items.SILVER_ORE_442,
Items.GOLD_ORE_444,
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
)
companion object {
var initialized = false
val PUMP_LOC = Location.create(1950, 4961, 0)
val PUMP_FACELOC = Location.create(1949, 4961, 0)
val PEDAL_LOC = Location.create(1947, 4966, 0)
val PEDAL_DISMOUNT = Location.create(1947, 4967, 0)
val PEDAL_FACELOC = Location.create(1946, 4966, 0)
val PUMP_ANIM = 2432
val PEDAL_ANIM = 2433
val PEDAL_SCENERY = getScenery(PEDAL_LOC)!!
val PUMP_SCENERY = getScenery(PUMP_LOC)!!
val validOreIds = intArrayOf(
Items.IRON_ORE_440,
Items.COPPER_ORE_436,
Items.TIN_ORE_438,
Items.COAL_453,
Items.MITHRIL_ORE_447,
Items.ADAMANTITE_ORE_449,
Items.SILVER_ORE_442,
Items.GOLD_ORE_444,
Items.RUNITE_ORE_451
)
}
override fun defineListeners() {
if (initialized) return
initialized = true
/**Handles operating the pump*/
on(pump, SCENERY, "operate") { player, node ->
if(player.getSkills().getLevel(Skills.STRENGTH) >= 30) {
val pumpL = Location(1950, 4961, 0)
val pumpF = Location(1949, 4961, 0)
player.properties.teleportLocation = pumpL
submitIndividualPulse(player, object : Pulse() {
override fun pulse(): Boolean {
face(player, pumpF,)
animate(player, 2432)
BlastFurnace.pumping = true
if (!BlastFurnace.pumpPipeBroken || !BlastFurnace.potPipeBroken) rewardXP(
player,
Skills.STRENGTH,
4.0
)
return false
}
override fun stop() {
BlastFurnace.pumping = false
super.stop()
}
})
}else{
sendDialogue(player,"I need 30 Strength to do this!")
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
}
/**Logic for the pedals that run the conveyor, rewards Agility XP every tick that they're being
* pedaled but will stop once the conveyor breaks.*/
on(pedals, SCENERY, "pedal") { player, node ->
if(player.getSkills().getLevel(Skills.AGILITY) >= 30) {
val pedalL = Location(1947, 4966, 0)
val pedalF = Location(1946, 4966, 0)
player.properties.teleportLocation = pedalL
submitIndividualPulse(player, object : Pulse() {
override fun pulse(): Boolean {
face(player, pedalF,)
animate(player, 2433)
BlastFurnace.pedaling = true
if (BlastFurnace.beltRunning) rewardXP(player, Skills.AGILITY, 2.0)
return false
}
override fun stop() {
BlastFurnace.pedaling = false
super.stop()
}
})
}else{
sendDialogue(player,"I need 30 Agility to do this!")
}
on (BlastConsts.STAIR_EXIT_ID, IntType.SCENERY, "climb-up") { p, _ ->
teleport(p, BlastConsts.EXIT_LOC)
return@on true
}
/**Lets players use a spade to take coke from the pile of coke*/
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 {
face(p, PUMP_FACELOC)
animate(p, PUMP_ANIM)
BlastFurnace.pumper = p
return false
}
on(coke, SCENERY, "collect") { player, _ ->
override fun stop() {
resetAnimator(p)
BlastFurnace.pumper = null
addScenery(PUMP_SCENERY)
super.stop()
}
})
}
}
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
}
setDest(IntType.SCENERY, BlastConsts.PUMP) { _, _ -> Location.create(1951, 4961, 0) }
on (BlastConsts.PEDALS, IntType.SCENERY, "pedal") { p, n ->
if (getDynLevel(p, Skills.AGILITY) > 30 && BlastFurnace.pedaler == null) {
forceMove(p, p.location, PEDAL_LOC, 0, 30) {
animate(p, PEDAL_ANIM)
removeScenery(PEDAL_SCENERY)
submitIndividualPulse(p, object : Pulse() {
override fun pulse(): Boolean {
if (p.settings.runEnergy < 1.0) {
teleport(p, PEDAL_DISMOUNT)
return true
}
face(p, PEDAL_FACELOC)
animate(p, PEDAL_ANIM)
BlastFurnace.pedaler = p
return false
}
override fun stop() {
resetAnimator(p)
BlastFurnace.pedaler = null
addScenery(PEDAL_SCENERY)
super.stop()
}
})
}
}
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
}
setDest(IntType.SCENERY, BlastConsts.PEDALS) { _, _ -> Location.create(1948, 4966, 0) }
on (BlastConsts.COKE, SCENERY, "collect") { player, _ ->
if (inInventory(player, Items.SPADE_952, 1)) {
if(removeItem(player, Items.SPADE_952, Container.INVENTORY) && addItem(player, Items.SPADEFUL_OF_COKE_6448, 1)) {
lockInteractions(player,1)
@@ -124,273 +131,173 @@ class BlastFurnaceListeners : InteractionListener {
} else {
sendMessage(player, "You need a spade to do this!")
}
return@on true
return@on true
}
/**This code is for handling the coke stove, lets players interact with it with the spadeful of coke
* and rewards firemaking XP when shoving coke in there, it will not allow players to shovel more
* coke in if it is already full.*/
on (Items.SPADEFUL_OF_COKE_6448, ITEM, "empty") {p, n ->
if (removeItem(p, n)) addItem(p, Items.SPADE_952)
return@on true
}
on(stove, SCENERY,"refuel") { player, _ ->
if (inInventory(player, Items.SPADEFUL_OF_COKE_6448, 1) && BlastFurnace.stoveCoke < 30 && player.getSkills().getLevel(Skills.FIREMAKING) >= 30) {
on (BlastConsts.STOVE, SCENERY, "refuel") { player, _ ->
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)
lockInteractions(player,2)
lock(player,2)
submitIndividualPulse(player, object : Pulse() {
override fun pulse(): Boolean {
return if(removeItem(player, Items.SPADEFUL_OF_COKE_6448, Container.INVENTORY) && addItem(player, Items.SPADE_952, 1)) {
animate(player, 2443)
rewardXP(player, Skills.FIREMAKING, 5.0)
BlastFurnace.state.addCoke(1)
true
} else {
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 {
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 the conveyor belt. It won't let you put anything on there if there's no room
* in your blast ore or blast coal player containers, the way it works is that it
* puts your coal and ore into the blast furnace containers however, the blast furnace will NOT
* smelt your ores because it's waiting for a player attribute to be set by the ore NPCs that this spawns*/
on(conveyorLoad, SCENERY, "put-ore-on") { player, node ->
val rocksInInventory = playerOre.filter {inInventory(player, it)}
var oreToActuallyAdd = 0
var coalToActuallyAdd = 0
if(player.blastCoal.freeSlots() > 0 || player.blastOre.freeSlots() > 0) {
player.dialogueInterpreter.sendOptions("Add all your ore to the furnace?", "Yes", "No")
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!")
}
on (BlastConsts.BELT, IntType.SCENERY, "put-ore-on") { p, _ ->
openDialogue(p, object : DialogueFile() {
override fun handle(componentID: Int, buttonID: Int) {
when (stage) {
0 -> sendDialogue(p, "Really place all your ore on the belt?").also { stage++ }
1 -> showTopics(
Topic("Yes.", 100, true),
Topic("Nevermind.", END_DIALOGUE, true)
)
100 -> {
end()
BlastFurnace.placeAllOre(p, accountForSkill = true)
}
} 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
* it's essentially the same thing as above except without the dialogue because if you're clicking on ore
* and trying to use it on the conveyor belt then you know what you're trying to do.*/
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
}else if(oreType.id != Items.COAL_453 && amountInInventory > spaceForOre){
amountToAdd = spaceForOre
}
if(oreType.id == Items.COPPER_ORE_436 && (amountToAdd + copperInPot) > 14){
while(amountToAdd + copperInPot > 14){
amountToAdd--
}
}
if(oreType.id == Items.TIN_ORE_438 && (amountToAdd + tinInPot) > 14){
while(amountToAdd + tinInPot > 14){
amountToAdd--
}
}
val bar = Bar.forOre(oreType.id)
if(oreType.id == Items.COAL_453) {
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()
onUseWith (IntType.SCENERY, validOreIds, BlastConsts.BELT) { p, used, _ ->
BlastFurnace.placeAllOre(p, used.id, accountForSkill = true)
return@onUseWith true
}
on(BlastConsts.TEMP_GAUGE, IntType.SCENERY, "read") { p, _ ->
openInterface(p, 30)
return@on true
}
on(BlastConsts.DISPENSER, IntType.SCENERY, "search", "take") { p, _ ->
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 (barsCooled) {
removeItem(p, Items.BUCKET_OF_WATER_1929, Container.INVENTORY)
addItem(p, Items.BUCKET_1925)
}
}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!")
0 -> sendDialogue(p,"There's nothing to cool off!")
3 -> sendDialogue(p,"These bars have already cooled off!")
}
return@onUseWith true
}
/**This handles interacting with the temperature gauge on the furnace*/
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, _ ->
on(BFSceneryController.BROKEN_POT_PIPE, SCENERY, "repair"){ player, _ ->
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)) {
rewardXP(player, Skills.CRAFTING, 50.0)
BlastFurnace.potPipeBroken = false
}
else {
BlastFurnace.state.potPipeBroken = false
} else {
sendMessage(player, "I need a hammer to do this!")
}
}else{
} else {
sendDialogue(player,"I need 30 Crafting in order to do this")
}
return@on true
}
on(brokenPumpPipe,SCENERY, "repair"){ player, _ ->
on(BFSceneryController.BROKEN_PUMP_PIPE, SCENERY, "repair"){ player, _ ->
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)) {
rewardXP(player, Skills.CRAFTING, 50.0)
BlastFurnace.pumpPipeBroken = false
}
else {
BlastFurnace.state.pumpPipeBroken = false
} else {
sendMessage(player, "I need a hammer to do this!")
}
}else{
} else {
sendDialogue(player,"I need 30 Crafting in order to do this")
}
return@on true
}
on(brokenBelt,SCENERY,"repair"){ player, _ ->
on(BFSceneryController.BROKEN_BELT, SCENERY, "repair"){ player, _ ->
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)) {
rewardXP(player, Skills.CRAFTING, 50.0)
BlastFurnace.beltBroken = false
}
else {
BlastFurnace.state.beltBroken = false
} else {
sendMessage(player, "I need a hammer to do this!")
}
}else{
} else {
sendDialogue(player,"I need 30 Crafting in order to do this")
}
return@on true
}
on(brokenCog,SCENERY,"repair"){ player, _ ->
on(BFSceneryController.BROKEN_COG, SCENERY, "repair"){ player, _ ->
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)) {
rewardXP(player, Skills.CRAFTING, 50.0)
BlastFurnace.cogBroken = false
}
else {
BlastFurnace.state.cogBroken = false
} else {
sendMessage(player, "I need a hammer to do this!")
}
}else{
} else {
sendDialogue(player,"I need 30 Craft in order to do this")
}
return@on true
}
/**Handles filling buckets from the sink*/
on(sink, SCENERY,"fill-bucket"){ player, _ ->
on(BlastConsts.SINK, SCENERY,"fill-bucket"){ player, _ ->
player.pulseManager.run(object : Pulse(1){
override fun pulse(): Boolean {
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
import content.minigame.blastfurnace.BlastFurnace
import core.api.*
import core.cache.def.impl.SceneryDefinition
import core.game.dialogue.DialoguePlugin
import core.game.interaction.OptionHandler
import core.game.node.Node
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.node.scenery.Scenery
import core.game.world.map.Location
import core.plugin.Initializable
import core.plugin.Plugin
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,
@@ -40,16 +41,6 @@ class KeldagrimOptionHandlers : OptionHandler() {
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" -> {
when(node.id){
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
override fun handle(componentID: Int, buttonID: Int) {
if (init) {
stage = if (player!!.getSkills().getLevel(Skills.SMITHING) >= 60) {
100
} else {
5
}
init = false
}
when(stage){
5 -> sendDialogue(player!!,"You must be Smithing Level 60 or higher in order to enter the Blast Furnace").also { stage = 10 }
10 -> sendDialogue(player!!,"However, you may enter if you pay the entrance fee").also { stage = 20 }
20 -> options("Yes","No").also { stage = 30 }
30 -> when(buttonID){
0 -> sendDialogue(player!!,"You must be Smithing Level 60 or higher in order to enter the Blast Furnace").also { stage++ }
1 -> sendDialogue(player!!,"However, you may enter for 10 minutes if you pay the entrance fee.<br>($fee gp)").also { stage++ }
2 -> options("Yes","No").also { stage++ }
3 -> when(buttonID){
1 -> {
if(player!!.equipment.contains(6465,1) && player!!.inventory.contains(995, 1250)){
removeItem(player!!, Item(995, 1250), Container.INVENTORY)
player!!.setAttribute("BlastTimer",1000)
player?.properties?.teleportLocation = Location.create(1940, 4958, 0).also { end() }
}
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 }
if (removeItem(player!!, Item(Items.COINS_995, fee)))
BlastFurnace.enter(player!!, true)
else
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 = 40 }
20 -> sendDialogue(player!!,"Then get out of here!").also { stage = END_DIALOGUE }
}
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
}
fun addScenery (scenery: Scenery) {
SceneryBuilder.add(scenery)
}
/**
* Remove a scenery from the world
* @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
* @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
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)
}
}