Implement Authentic Interaction Subsystem

Implemented authentic script/interaction queues
This does now mean we have a total of 3 interaction systems, but this additional system is necessary to fix certain categories of bug and implement some authentic features
Converted mining to new system
Converted fishing to new system
Converted woodcutting to new system
Provided an example of soft-queued scripts with GrandTreePodListener
Implemented tick-eating (it is now possible to eat a shark, drink a potion, and eat a karambwan all on the same tick)
Can now eat and drop items while stunned
This commit is contained in:
Ceikry
2023-02-28 23:41:14 +00:00
committed by Ryan
parent 5206c99151
commit 124eeab893
61 changed files with 1466 additions and 293 deletions
@@ -1,42 +1,20 @@
package content.global.skill.gather
import core.game.node.Node
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import content.global.skill.fishing.FishingSpot
import content.global.skill.gather.fishing.FishingPulse
import content.global.skill.gather.mining.MiningSkillPulse
import content.global.skill.gather.woodcutting.WoodcuttingSkillPulse
import org.rs09.consts.NPCs
import content.region.misc.miscellania.dialogue.KjallakOnChopDialogue
import core.game.interaction.InteractionListener
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.Node
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
class GatheringSkillOptionListeners : InteractionListener {
val ETCETERIA_REGION = 10300
override fun defineListeners() {
on(IntType.SCENERY,"chop-down","chop down","cut down","chop"){ player, node ->
if(player.location.regionId == ETCETERIA_REGION){
player.dialogueInterpreter.open(KjallakOnChopDialogue(), NPC(NPCs.CARPENTER_KJALLAK_3916))
return@on true
}
player.pulseManager.run(WoodcuttingSkillPulse(player, node.asScenery()))
return@on true
}
on(IntType.SCENERY,"mine"){ player, node ->
player.pulseManager.run(MiningSkillPulse(player, node.asScenery()))
return@on true
}
on(IntType.NPC,"net"){ player, node -> return@on fish(player,node,"net")}
on(IntType.NPC,"lure"){ player, node -> return@on fish(player,node,"lure")}
on(IntType.NPC,"bait"){ player, node -> return@on fish(player,node,"bait")}
on(IntType.NPC,"harpoon"){ player, node -> return@on fish(player,node,"harpoon")}
on(IntType.NPC,"cage"){ player, node -> return@on fish(player,node,"cage")}
on(IntType.NPC,"fish"){ player, node -> return@on fish(player,node,"fish") }
}
fun fish(player: Player, node: Node, opt: String): Boolean{
@@ -0,0 +1,118 @@
package content.global.skill.gather.fishing
import content.global.skill.fishing.Fish
import content.global.skill.fishing.FishingOption
import content.global.skill.fishing.FishingSpot
import content.global.skill.skillcapeperks.SkillcapePerks
import content.global.skill.skillcapeperks.SkillcapePerks.Companion.isActive
import content.global.skill.summoning.familiar.Forager
import core.api.*
import core.game.event.ResourceProducedEvent
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.Node
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.system.command.sets.STATS_BASE
import core.game.system.command.sets.STATS_FISH
import core.game.world.map.path.Pathfinder
import core.tools.RandomFunction
import core.tools.colorize
class FishingListener : InteractionListener{
override fun defineListeners() {
val SPOT_IDS = FishingSpot.values().flatMap { it.ids.toList() }.toIntArray()
defineInteraction(
IntType.NPC,
SPOT_IDS,
"net", "lure", "bait", "harpoon", "cage", "fish",
persistent = true,
allowedDistance = 1,
handler = ::handleFishing
)
}
private fun handleFishing(player: Player, node: Node, state: Int) : Boolean {
val npc = node as? NPC ?: return clearScripts(player)
val spot = FishingSpot.forId(npc.id) ?: return clearScripts(player)
val op = spot.getOptionByName(getUsedOption(player))
var forager: Forager? = null
if (player.familiarManager.hasFamiliar() && player.familiarManager.familiar is Forager) {
forager = player.familiarManager.familiar as Forager
}
if (!finishedMoving(player))
return restartScript(player)
if (state == 0) {
if (!checkRequirements(player, op, node))
return clearScripts(player)
forager?.let {
val dest = player.location.transform(player.direction)
Pathfinder.find(it, dest).walk(it)
}
anim(player, op)
return delayScript(player, 5)
}
anim(player, op)
forager?.handlePassiveAction()
val fish = op.rollFish(player) ?: return delayScript(player, 5)
if (!hasSpaceFor(player, fish.item)) return restartScript(player)
if (!op.removeBait(player.inventory)) return restartScript(player)
player.dispatch(ResourceProducedEvent(fish.item.id, fish.item.amount, node))
val item = fish.item
if (isActive(SkillcapePerks.GREAT_AIM, player) && RandomFunction.roll(20)) {
addItem(player, item.id, item.amount)
sendMessage(player, colorize("%RYour expert aim catches you a second fish."))
}
addItemOrDrop(player, item.id, item.amount)
player.incrementAttribute("$STATS_BASE:$STATS_FISH")
rewardXP(player, Skills.FISHING, fish.experience)
return restartScript(player)
}
private fun anim(player: Player, option: FishingOption) {
if (animationFinished(player))
animate(player, option.animation)
}
private fun checkRequirements(player: Player, option: FishingOption, node: Node) : Boolean {
if (!player.inventory.containsItem(option.tool) && !hasBarbTail(player, option)) {
player.dialogueInterpreter.sendDialogue("You need a " + option.tool.name.toLowerCase() + " to catch these fish.")
return false
}
if (!option.hasBait(player.inventory)) {
player.dialogueInterpreter.sendDialogue("You don't have any " + option.getBaitName().toLowerCase() + "s left.")
return false
}
if (player.skills.getLevel(Skills.FISHING) < option!!.level) {
val f = option!!.fish[option!!.fish.size - 1]
player.dialogueInterpreter.sendDialogue("You need a fishing level of " + f.level + " to catch " + (if (f == Fish.SHRIMP || f == Fish.ANCHOVIE) "" else "a") + " " + f.item.name.toLowerCase() + ".".trim { it <= ' ' })
return false
}
if (player.inventory.freeSlots() == 0) {
player.dialogueInterpreter.sendDialogue("You don't have enough space in your inventory.")
return false
}
return node.isActive && node.location.withinDistance(player.location, 1)
}
private fun hasBarbTail(player: Player, option: FishingOption): Boolean {
if (option == FishingOption.HARPOON || option == FishingOption.N_HARPOON) {
if (player.inventory.containsItem(FishingOption.BARB_HARPOON.tool) || player.equipment.containsItem(
FishingOption.BARB_HARPOON.tool
)
) {
return true
}
}
return false
}
}
@@ -0,0 +1,225 @@
package content.global.skill.gather.mining
import content.data.skill.SkillingPets
import content.data.skill.SkillingTool
import content.global.skill.skillcapeperks.SkillcapePerks
import core.api.*
import core.cache.def.impl.ItemDefinition
import core.game.event.ResourceProducedEvent
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.Node
import core.game.node.entity.npc.drop.DropFrequency
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.diary.DiaryType
import core.game.node.entity.skill.Skills
import core.game.node.item.ChanceItem
import core.game.node.scenery.Scenery
import core.game.node.scenery.SceneryBuilder
import core.game.system.command.sets.STATS_BASE
import core.game.system.command.sets.STATS_ROCKS
import core.tools.RandomFunction
import core.tools.prependArticle
import org.rs09.consts.Items
class MiningListener : InteractionListener {
override fun defineListeners() {
defineInteraction(
IntType.SCENERY,
MiningNode.values().map { it.id }.toIntArray(),
"mine",
persistent = true, allowedDistance = 1,
handler = ::handleMining
)
}
private val GEM_REWARDS = arrayOf(ChanceItem(1623, 1, DropFrequency.COMMON), ChanceItem(1621, 1, DropFrequency.COMMON), ChanceItem(1619, 1, DropFrequency.UNCOMMON), ChanceItem(1617, 1, DropFrequency.RARE))
private fun handleMining(player: Player, node: Node, state: Int) : Boolean {
val resource = MiningNode.forId(node.id)
val tool = SkillingTool.getPickaxe(player)
val isEssence = resource.id == 2491
val isGems = resource.identifier == MiningNode.GEM_ROCK_0.identifier
if (!finishedMoving(player))
return true
if (state == 0) {
if (!checkRequirements(player, resource, node)) {
player.scripts.reset()
return true
}
anim(player, tool)
sendMessage(player, "You swing your pickaxe at the rock...")
return delayScript(player, getDelay(resource))
}
anim(player, tool)
if (!checkReward(player, resource, tool))
return delayScript(player, getDelay(resource))
// Reward logic
var reward = resource!!.reward
var rewardAmount : Int
if (reward > 0) {
reward = calculateReward(player, resource, isEssence, isGems, reward) // calculate rewards
rewardAmount = calculateRewardAmount(player, isEssence, reward) // calculate amount
player.dispatch(ResourceProducedEvent(reward, rewardAmount, node))
SkillingPets.checkPetDrop(player, SkillingPets.GOLEM) // roll for pet
// Reward mining experience
val experience = resource!!.experience * rewardAmount
rewardXP(player, Skills.MINING, experience)
// If player is wearing Bracelet of Clay, soften
if(reward == Items.CLAY_434){
val bracelet = getItemFromEquipment(player, EquipmentSlot.HANDS)
if(bracelet != null && bracelet.id == Items.BRACELET_OF_CLAY_11074){
var charges = player.getAttribute("jewellery-charges:bracelet-of-clay", 28);
charges--
reward = Items.SOFT_CLAY_1761
sendMessage(player, "Your bracelet of clay softens the clay for you.")
if(charges <= 0) {
if(removeItem(player, bracelet, Container.EQUIPMENT)) {
sendMessage(player, "Your bracelet of clay crumbles to dust.")
charges = 28
}
}
player.setAttribute("/save:jewellery-charges:bracelet-of-clay", charges)
}
}
val rewardName = getItemName(reward).lowercase()
// Send the message for the resource reward
if (isGems) {
sendMessage(player, "You get ${prependArticle(rewardName)}.")
} else {
sendMessage(player, "You get some ${rewardName.lowercase()}.")
}
// Give the mining reward, increment 'rocks mined' attribute
if(addItem(player, reward, rewardAmount)) {
var rocksMined = getAttribute(player, "$STATS_BASE:$STATS_ROCKS", 0)
setAttribute(player, "/save:$STATS_BASE:$STATS_ROCKS", ++rocksMined)
}
// Calculate bonus gem chance while mining
if (!isEssence) {
var chance = 282
var altered = false
val ring = getItemFromEquipment(player, EquipmentSlot.RING)
if (ring != null && ring.name.lowercase().contains("ring of wealth") || inEquipment(player, Items.RING_OF_THE_STAR_SPRITE_14652)) {
chance = (chance / 1.5).toInt()
altered = true
}
val necklace = getItemFromEquipment(player, EquipmentSlot.AMULET)
if (necklace != null && necklace.id in 1705..1713) {
chance = (chance / 1.5).toInt()
altered = true
}
if (RandomFunction.roll(chance)) {
val gem = GEM_REWARDS.random()
sendMessage(player,"You find a ${gem.name}!")
if (freeSlots(player) == 0) {
sendMessage(player,"You do not have enough space in your inventory, so you drop the gem on the floor.")
}
addItemOrDrop(player, gem.id)
}
}
// Transform ore to depleted version
if (!isEssence && resource!!.respawnRate != 0) {
SceneryBuilder.replace(node as Scenery, Scenery(resource!!.emptyId, node.getLocation(), node.type, node.rotation), resource!!.respawnDuration)
node.setActive(false)
return true
}
}
return true
}
private fun calculateRewardAmount(player: Player, isMiningEssence: Boolean, reward: Int): Int {
var amount = 1
// If player is wearing Varrock armour from diary, roll chance at extra ore
if (!isMiningEssence && player.achievementDiaryManager.getDiary(DiaryType.VARROCK).level != -1) {
when (reward) {
Items.CLAY_434, Items.COPPER_ORE_436, Items.TIN_ORE_438, Items.LIMESTONE_3211, Items.BLURITE_ORE_668, Items.IRON_ORE_440, Items.ELEMENTAL_ORE_2892, Items.SILVER_ORE_442, Items.COAL_453 -> if (player.achievementDiaryManager.armour >= 0 && RandomFunction.random(100) <= 10) {
amount += 1
sendMessage(player,"The Varrock armour allows you to mine an additional ore.")
}
Items.GOLD_ORE_444, Items.GRANITE_500G_6979, Items.GRANITE_2KG_6981, Items.GRANITE_5KG_6983, Items.MITHRIL_ORE_447 -> if (player.achievementDiaryManager.armour >= 1 && RandomFunction.random(100) <= 10) {
amount += 1
sendMessage(player, "The Varrock armour allows you to mine an additional ore.")
}
Items.ADAMANTITE_ORE_449 -> if (player.achievementDiaryManager.armour >= 2 && RandomFunction.random(100) <= 10) {
amount += 1
sendMessage(player, "The Varrock armour allows you to mine an additional ore.")
}
}
}
// If player has mining boost from Shooting Star, roll chance at extra ore
if (player.hasActiveState("shooting-star")) {
if (RandomFunction.getRandom(5) == 3) {
sendMessage(player, "...you manage to mine a second ore thanks to the Star Sprite.")
amount += 1
}
}
return amount
}
private fun calculateReward(player: Player, resource: MiningNode, isMiningEssence: Boolean, isMiningGems: Boolean, reward: Int): Int {
// If the player is mining sandstone or granite, then get size of sandstone/granite and xp reward for that size
var reward = reward
if (resource == MiningNode.SANDSTONE || resource == MiningNode.GRANITE) {
val value = RandomFunction.randomize(if (resource == MiningNode.GRANITE) 3 else 4)
reward += value shl 1
rewardXP(player, Skills.MINING, value * 10.toDouble())
} else if (isMiningEssence && getDynLevel(player, Skills.MINING) >= 30) {
reward = 7936
} else if (isMiningGems) {
reward = RandomFunction.rollWeightedChanceTable(MiningNode.gemRockGems).id
}
return reward
}
private fun checkReward(player: Player, resource: MiningNode?, tool: SkillingTool): Boolean {
val level = 1 + getDynLevel(player, Skills.MINING) + getFamiliarBoost(player, Skills.MINING)
val hostRatio = Math.random() * (100.0 * resource!!.rate)
var toolRatio = tool.ratio
if(SkillcapePerks.isActive(SkillcapePerks.PRECISION_MINER,player)){
toolRatio += 0.075
}
val clientRatio = Math.random() * ((level - resource.level) * (1.0 + toolRatio))
return hostRatio < clientRatio
}
fun getDelay(resource: MiningNode) : Int {
return if (resource.id == 2491) 3 else 4
}
fun anim(player: Player, tool: SkillingTool) {
if (animationFinished(player))
animate(player, tool.animation)
}
fun checkRequirements(player: Player, resource: MiningNode, node: Node): Boolean {
if (getDynLevel(player, Skills.MINING) < resource.level) {
sendMessage(player, "You need a mining level of ${resource.level} to mine this rock.")
return false
}
if (SkillingTool.getPickaxe(player) == null) {
sendMessage(player, "You do not have a pickaxe to use.")
return false
}
if (freeSlots(player) == 0) {
if(resource.identifier == 13.toByte()) {
sendDialogue(player,"Your inventory is too full to hold any more gems.")
return false
}
sendDialogue(player,"Your inventory is too full to hold any more ${ItemDefinition.forId(resource!!.reward).name.lowercase()}.")
return false
}
return node.isActive
}
}
@@ -0,0 +1,247 @@
package content.global.skill.gather.woodcutting
import content.data.skill.SkillingPets
import content.data.skill.SkillingTool
import content.data.tables.BirdNest
import content.global.skill.farming.FarmingPatch.Companion.forObject
import content.global.skill.skillcapeperks.SkillcapePerks
import content.global.skill.skillcapeperks.SkillcapePerks.Companion.isActive
import core.api.delayScript
import core.api.finishedMoving
import core.api.sendMessage
import core.cache.def.impl.ItemDefinition
import core.game.container.impl.EquipmentContainer
import core.game.event.ResourceProducedEvent
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.Node
import core.game.node.entity.impl.Projectile
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.audio.Audio
import core.game.node.entity.player.link.diary.DiaryType
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import core.game.node.scenery.Scenery
import core.game.node.scenery.SceneryBuilder
import core.game.system.command.sets.STATS_BASE
import core.game.system.command.sets.STATS_LOGS
import core.game.world.map.RegionManager
import core.tools.RandomFunction
import org.rs09.consts.Items
import org.rs09.consts.Sounds
import org.rs09.consts.Sounds.TREE_FALLING_2734
import java.util.*
import kotlin.streams.toList
class WoodcuttingListener : InteractionListener {
private val woodcuttingSounds = intArrayOf(
Sounds.WOODCUTTING_HIT_3038,
Sounds.WOODCUTTING_HIT_3039,
Sounds.WOODCUTTING_HIT_3040,
Sounds.WOODCUTTING_HIT_3041,
Sounds.WOODCUTTING_HIT_3042
)
override fun defineListeners() {
defineInteraction(
IntType.SCENERY,
ids = WoodcuttingNode.values().map { it.id }.toIntArray(),
"chop-down", "chop", "chop down", "cut down",
persistent = true,
allowedDistance = 1,
handler = ::handleWoodcutting
)
}
private fun handleWoodcutting(player: Player, node: Node, state: Int) : Boolean {
val resource = WoodcuttingNode.forId(node.id)
val tool = SkillingTool.getHatchet(player)
if (!finishedMoving(player))
return true
if (state == 0) {
if (!checkWoodcuttingRequirements(player, resource, node)) {
player.scripts.reset()
return true
}
animateWoodcutting(player)
sendMessage(player, "You swing your axe at the tree...")
return delayScript(player, 3)
}
animateWoodcutting(player)
if (!checkReward(player, resource, tool))
return delayScript(player, 3)
if (tool.id == Items.INFERNO_ADZE_13661 && RandomFunction.roll(4)) {
sendMessage(player, "You chop some logs. The heat of the inferno adze incinerates them.")
Projectile.create(
player, null,
1776,
35, 30,
20, 25
).transform(
player,
player.location.transform(2, 0, 0),
true,
25, 25
).send()
delayScript(player, 3)
return rollDepletion(player, node.asScenery(), resource)
}
val reward = resource.getReward()
val rewardAmount: Int
if (reward > 0) {
rewardAmount = calculateRewardAmount(player, reward) // calculate amount
SkillingPets.checkPetDrop(player, SkillingPets.BEAVER) // roll for pet
//add experience
val experience: Double = calculateExperience(player, resource, rewardAmount)
player.getSkills().addExperience(Skills.WOODCUTTING, experience, true)
//send the message for the resource reward
if (resource == WoodcuttingNode.DRAMEN_TREE) {
player.packetDispatch.sendMessage("You cut a branch from the Dramen tree.")
} else {
player.packetDispatch.sendMessage("You get some " + ItemDefinition.forId(reward).name.lowercase(Locale.getDefault()) + ".")
}
//give the reward
player.inventory.add(Item(reward, rewardAmount))
player.dispatch(ResourceProducedEvent(reward, rewardAmount, node, -1))
var cutLogs = player.getAttribute("$STATS_BASE:$STATS_LOGS", 0)
player.setAttribute("/save:$STATS_BASE:$STATS_LOGS", ++cutLogs)
//calculate bonus bird nest for mining
val chance = 282
if (RandomFunction.random(chance) == chance / 2) {
if (isActive(SkillcapePerks.NEST_HUNTER, player)) {
if (!player.inventory.add(BirdNest.getRandomNest(false).nest)) {
BirdNest.drop(player)
}
} else {
BirdNest.drop(player)
}
}
}
delayScript(player, 3)
rollDepletion(player, node.asScenery(), resource)
return true
}
private fun rollDepletion(player: Player, node: Scenery, resource: WoodcuttingNode): Boolean {
//transform to depleted version
//OSRS and RS3 Wikis both agree: All trees present in 2009 are a 1/8 fell chance, aside from normal trees/dead trees which are 100%
//OSRS: https://oldschool.runescape.wiki/w/Woodcutting scroll down to the mechanics section
//RS3 : https://runescape.wiki/w/Woodcutting scroll down to the mechanics section, and expand the tree felling chances table
if (resource.getRespawnRate() > 0) {
if (RandomFunction.roll(8) || resource.identifier.toInt() == 1 || resource.identifier.toInt() == 2 || resource.identifier.toInt() == 3 || resource.identifier.toInt() == 6) {
if (resource.isFarming()) {
val fPatch = forObject(node.asScenery())
if (fPatch != null) {
val patch = fPatch.getPatchFor(player)
patch.setCurrentState(patch.getCurrentState() + 1)
}
return true
}
if (resource.getEmptyId() > -1) {
SceneryBuilder.replace(node, node.transform(resource.getEmptyId()), resource.getRespawnDuration())
} else {
SceneryBuilder.replace(node, node.transform(0), resource.getRespawnDuration())
}
node.setActive(false)
player.getAudioManager().send(TREE_FALLING_2734)
return true
}
}
return false
}
private fun checkReward(player: Player, resource: WoodcuttingNode, tool: SkillingTool): Boolean {
val skill = Skills.WOODCUTTING
val level: Int = player.getSkills().getLevel(skill) + player.getFamiliarManager().getBoost(skill)
val hostRatio = RandomFunction.randomDouble(100.0)
val lowMod: Double = if (tool == SkillingTool.BLACK_AXE) resource.tierModLow / 2 else resource.tierModLow
val low: Double = resource.baseLow + tool.ordinal * lowMod
val highMod: Double = if (tool == SkillingTool.BLACK_AXE) resource.tierModHigh / 2 else resource.tierModHigh
val high: Double = resource.baseHigh + tool.ordinal * highMod
val clientRatio = RandomFunction.getSkillSuccessChance(low, high, level)
return hostRatio < clientRatio
}
fun animateWoodcutting(player: Player) {
if (!player.animator.isAnimating) {
player.animate(SkillingTool.getHatchet(player).animation)
val playersAroundMe: List<Player> = RegionManager.getLocalPlayers(player, 2)
.stream()
.filter { p: Player -> p.username != player.username }
.toList()
val soundIndex = RandomFunction.random(0, woodcuttingSounds.size)
player.audioManager.send(
Audio(woodcuttingSounds[soundIndex]),
playersAroundMe,
player.location
)
}
}
fun checkWoodcuttingRequirements(player: Player, resource: WoodcuttingNode, node: Node): Boolean {
if (player.getSkills().getLevel(Skills.WOODCUTTING) < resource.getLevel()) {
player.getPacketDispatch().sendMessage("You need a woodcutting level of " + resource.getLevel() + " to chop this tree.")
return false
}
if (SkillingTool.getHatchet(player) == null) {
player.packetDispatch.sendMessage("You do not have an axe to use.")
return false
}
if (player.inventory.freeSlots() < 1) {
player.dialogueInterpreter.sendDialogue("Your inventory is too full to hold any more " + ItemDefinition.forId(resource.getReward()).name.lowercase(Locale.getDefault()) + ".")
return false
}
return node.isActive
}
private fun calculateRewardAmount(player: Player, reward: Int): Int {
var amount = 1
// 3239: Hollow tree (bark) 10% chance of obtaining
if (reward == 3239 && RandomFunction.random(100) >= 10) {
amount = 0
}
// Seers village medium reward - extra normal log while in seer's village
if (reward == 1511 && player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).isComplete(1) && player.getViewport().getRegion().getId() == 10806) {
amount = 2
}
return amount
}
private fun calculateExperience(player: Player, resource: WoodcuttingNode, amount: Int): Double {
var amount = amount
var experience: Double = resource.getExperience()
val reward = resource.reward
if (player.getLocation().getRegionId() == 10300) {
return 1.0
}
// Bark
if (reward == 3239) {
// If we receive the item, give the full experience points otherwise give the base amount
if (amount >= 1) {
experience = 275.2
} else {
amount = 1
}
}
// Seers village medium reward - extra 10% xp from maples while wearing headband
if (reward == 1517 && player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).isComplete(1) && player.getEquipment().get(EquipmentContainer.SLOT_HAT) != null && player.getEquipment().get(EquipmentContainer.SLOT_HAT).getId() == 14631) {
experience *= 1.10
}
return experience * amount
}
}
@@ -141,10 +141,10 @@ public enum WoodcuttingNode {
double experience,rate;
public byte identifier;
boolean farming;
double baseLow = 64;
double baseHigh = 200;
double tierModLow = 32;
double tierModHigh = 100;
public double baseLow = 64;
public double baseHigh = 200;
public double tierModLow = 32;
public double tierModHigh = 100;
WoodcuttingNode(int full, int empty,byte identifier){
this.full = full;
this.empty = empty;
@@ -40,7 +40,7 @@ public final class StatBoostSpell extends MagicSpell {
public boolean cast(Entity entity, Node target) {
final Player player = ((Player) entity);
Item item = ((Item) target);
final Potion potion = (Potion) Consumables.getConsumableById(item.getId());
final Potion potion = (Potion) Consumables.getConsumableById(item.getId()).getConsumable();
player.getInterfaceManager().setViewedTab(6);
if (potion == null) {
player.getPacketDispatch().sendMessage("You can only cast this spell on a potion.");
@@ -42,7 +42,7 @@ public class StatRestoreSpell extends MagicSpell {
public boolean cast(Entity entity, Node target) {
final Player player = ((Player) entity);
Item item = ((Item) target);
final Potion potion = (Potion) Consumables.getConsumableById(item.getId());
final Potion potion = (Potion) Consumables.getConsumableById(item.getId()).getConsumable();
player.getInterfaceManager().setViewedTab(6);
if (potion == null) {
player.getPacketDispatch().sendMessage("You can only cast this spell on a potion.");
@@ -1,6 +1,6 @@
package content.global.skill.runecrafting;
import core.game.global.action.DropItemHandler;
import core.game.global.action.DropListener;
import core.game.node.entity.skill.Skills;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
@@ -195,7 +195,7 @@ public enum RunePouch {
*/
private void drop(Player player, Item item) {
onDrop(player, item);
DropItemHandler.drop(player, item);
DropListener.drop(player, item);
}
/**
@@ -94,7 +94,7 @@ public class BunyipNPC extends Familiar {
player.sendMessage("You can't use this special on an object like that.");
return false;
}
Consumable consumable = Consumables.getConsumableById(special.getItem().getId() + 2);
Consumable consumable = Consumables.getConsumableById(special.getItem().getId() + 2).getConsumable();
if (consumable == null) {
player.sendMessage("Error: Report to admin.");
return false;
@@ -138,7 +138,7 @@ public class BunyipNPC extends Familiar {
public boolean handle(NodeUsageEvent event) {
Player player = event.getPlayer();
Fish fish = Fish.forItem(event.getUsedItem());
Consumable consumable = Consumables.getConsumableById(fish.getItem().getId() + 2);
Consumable consumable = Consumables.getConsumableById(fish.getItem().getId() + 2).getConsumable();
if (consumable == null) {
return true;
}
@@ -15,6 +15,8 @@ import core.plugin.ClassScanner;
import core.plugin.Initializable;
import core.tools.RandomFunction;
import static core.api.ContentAPIKt.stun;
/**
* The plugin used to load the minotaur familiar npcs.
* @author Vexia
@@ -58,7 +60,7 @@ public final class MinotaurFamiliarNPC implements Plugin<Object> {
GameWorld.getPulser().submit(new Pulse(ticks) {
@Override
public boolean pulse() {
target.getStateManager().set(EntityState.STUNNED, 4);
stun(target, 4);
return true;
}
});
@@ -52,7 +52,7 @@ public class RavenousLocustNPC extends Familiar {
if (item == null) {
continue;
}
Consumable consumable = Consumables.getConsumableById(item.getId());
Consumable consumable = Consumables.getConsumableById(item.getId()).getConsumable();
if (consumable != null) {
p.getInventory().remove(item);
break;
@@ -1,5 +1,6 @@
package content.global.skill.thieving
import core.api.stun
import core.game.node.entity.combat.ImpactHandler
import core.game.node.entity.impl.Animator
import core.game.node.entity.player.Player
@@ -59,8 +60,7 @@ class ThievingListeners : InteractionListener {
val hitSoundId = 518 + RandomFunction.random(4) // choose 1 of 4 possible hit noises
player.audioManager.send(hitSoundId, 1, 20) // OSRS defines a delay of 20
player.stateManager.set(EntityState.STUNNED, secondsToTicks(pickpocketData.stunTime))
player.lock(secondsToTicks(pickpocketData.stunTime))
stun(player, pickpocketData.stunTime)
player.impactHandler.manualHit(node.asNpc(),RandomFunction.random(pickpocketData.stunDamageMin,pickpocketData.stunDamageMax),ImpactHandler.HitsplatType.NORMAL)