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:
@@ -12,7 +12,6 @@ import core.plugin.Plugin;
|
||||
* Handles the searching of a bird nest item.
|
||||
* @author Vexia
|
||||
*/
|
||||
@Initializable
|
||||
public final class BirdNestPlugin extends OptionHandler {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package content.global.handlers.item
|
||||
|
||||
import content.data.tables.BirdNest
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
|
||||
class BirdNestScript : InteractionListener {
|
||||
val nestIds = BirdNest.values().map { it.nest.id }.toIntArray()
|
||||
|
||||
override fun defineListeners() {
|
||||
on(nestIds, IntType.ITEM, "search", handler = ::handleNest)
|
||||
}
|
||||
|
||||
private fun handleNest(player: Player, node: Node) : Boolean {
|
||||
val nest = BirdNest.forNest(node as? Item ?: return false)
|
||||
nest.search(player, node as? Item ?: return false)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package content.global.handlers.item
|
||||
|
||||
import content.data.consumables.Consumables
|
||||
import core.api.delayAttack
|
||||
import core.api.getUsedOption
|
||||
import core.api.getWorldTicks
|
||||
import core.api.stopExecuting
|
||||
import core.game.interaction.Clocks
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.GameWorld
|
||||
|
||||
class ConsumableListener : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
on(IntType.ITEM, "eat", "drink", handler = ::handleConsumable)
|
||||
}
|
||||
|
||||
private fun handleConsumable(player: Player, node: Node) : Boolean {
|
||||
val consumable = Consumables.getConsumableById(node.id) ?: return stopExecuting(player)
|
||||
|
||||
val food = getUsedOption(player) == "eat"
|
||||
val isIgnoreMainClock = consumable.isIgnoreMainClock
|
||||
|
||||
if (food) {
|
||||
if (isIgnoreMainClock && player.clocks[Clocks.NEXT_CONSUME] < GameWorld.ticks) {
|
||||
consumable.consumable.consume(node as? Item ?: return stopExecuting(player), player)
|
||||
player.clocks[Clocks.NEXT_CONSUME] = getWorldTicks() + 2
|
||||
player.clocks[Clocks.NEXT_EAT] = getWorldTicks() + 2
|
||||
delayAttack(player, 3)
|
||||
} else if (player.clocks[Clocks.NEXT_CONSUME] < getWorldTicks() && player.clocks[Clocks.NEXT_EAT] < getWorldTicks()) {
|
||||
consumable.consumable.consume(node as? Item ?: return stopExecuting(player), player)
|
||||
player.clocks[Clocks.NEXT_EAT] = getWorldTicks() + 2
|
||||
delayAttack(player, 3)
|
||||
}
|
||||
} else {
|
||||
if (isIgnoreMainClock && player.clocks[Clocks.NEXT_CONSUME] < getWorldTicks()) {
|
||||
consumable.consumable.consume(node as? Item ?: return stopExecuting(player), player)
|
||||
player.clocks[Clocks.NEXT_CONSUME] = getWorldTicks() + 3
|
||||
player.clocks[Clocks.NEXT_DRINK] = getWorldTicks() + 3
|
||||
} else if (player.clocks[Clocks.NEXT_CONSUME] < getWorldTicks() && player.clocks[Clocks.NEXT_DRINK] < getWorldTicks()) {
|
||||
consumable.consumable.consume(node as? Item ?: return stopExecuting(player), player)
|
||||
player.clocks[Clocks.NEXT_DRINK] = getWorldTicks() + 3
|
||||
}
|
||||
}
|
||||
|
||||
return stopExecuting(player)
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import core.plugin.Plugin;
|
||||
* @author Emperor
|
||||
* @version 1.0
|
||||
*/
|
||||
@Initializable
|
||||
public final class ConsumableOptionPlugin extends OptionHandler {
|
||||
|
||||
@Override
|
||||
@@ -35,7 +34,7 @@ public final class ConsumableOptionPlugin extends OptionHandler {
|
||||
|
||||
@Override
|
||||
public boolean handle(final Player player, final Node node, final String option) {
|
||||
if (player.getLocks().isLocked(option)) {
|
||||
/* if (player.getLocks().isLocked(option)) {
|
||||
return true;
|
||||
}
|
||||
boolean food = option.equals("eat");
|
||||
@@ -61,7 +60,7 @@ public final class ConsumableOptionPlugin extends OptionHandler {
|
||||
if (food) {
|
||||
player.getProperties().getCombatPulse().delayNextAttack(3);
|
||||
}
|
||||
lastEaten = node.asItem().getId();
|
||||
lastEaten = node.asItem().getId();*/
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,18 @@ package content.global.handlers.item
|
||||
|
||||
import content.global.travel.glider.GliderPulse
|
||||
import content.global.travel.glider.Gliders
|
||||
import core.api.*
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.QueueStrength
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.Location
|
||||
import core.net.packet.PacketRepository
|
||||
import core.net.packet.context.MinimapStateContext
|
||||
import core.net.packet.out.MinimapState
|
||||
import org.rs09.consts.Items
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.IntType
|
||||
import core.api.*
|
||||
|
||||
private const val SQUASH_GRAPHICS_BEGIN = 767
|
||||
private const val SQUASH_GRAPHICS_END = 769
|
||||
@@ -25,41 +29,68 @@ private const val LAUNCH_ANIMATION = 4547
|
||||
class GrandSeedPodHandler : InteractionListener {
|
||||
|
||||
override fun defineListeners() {
|
||||
on(Items.GRAND_SEED_POD_9469, IntType.ITEM, "squash", "launch"){ player, _ ->
|
||||
when(getUsedOption(player)){
|
||||
"launch" -> submitWorldPulse(LaunchPulse(player))
|
||||
"squash" -> submitWorldPulse(SquashPulse(player))
|
||||
on(intArrayOf(Items.GRAND_SEED_POD_9469), IntType.ITEM, "squash", "launch") { player, _ ->
|
||||
val opt = getUsedOption(player)
|
||||
if (!removeItem(player, Items.GRAND_SEED_POD_9469)) return@on false
|
||||
if (opt == "launch") {
|
||||
visualize(player, LAUNCH_ANIMATION, LAUNCH_GRAPHICS)
|
||||
delayEntity(player, 7)
|
||||
queueScript(player, 3, QueueStrength.SOFT) {stage: Int ->
|
||||
if (stage == 0) {
|
||||
rewardXP(player, Skills.FARMING, 100.0)
|
||||
openOverlay(player, 115)
|
||||
return@queueScript keepRunning(player)
|
||||
}
|
||||
|
||||
if (stage == 1) {
|
||||
PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2))
|
||||
return@queueScript delayScript(player, 3)
|
||||
}
|
||||
|
||||
if (stage == 2) {
|
||||
teleport(player, Gliders.TA_QUIR_PRIW.location)
|
||||
return@queueScript delayScript(player, 2)
|
||||
}
|
||||
|
||||
if (stage == 3) {
|
||||
closeOverlay(player)
|
||||
PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0))
|
||||
}
|
||||
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
}
|
||||
removeItem(player, Items.GRAND_SEED_POD_9469, Container.INVENTORY)
|
||||
lock(player, 50)
|
||||
|
||||
if (opt == "squash") {
|
||||
visualize(player, SQUASH_ANIM_BEGIN, SQUASH_GRAPHICS_BEGIN)
|
||||
delayEntity(player, 12)
|
||||
queueScript(player, 3, QueueStrength.SOFT) {stage: Int ->
|
||||
if (stage == 0) {
|
||||
animate(player, 1241, true)
|
||||
return@queueScript keepRunning(player)
|
||||
}
|
||||
|
||||
if (stage == 1) {
|
||||
teleport(player, Location.create(2464, 3494, 0))
|
||||
return@queueScript keepRunning(player)
|
||||
}
|
||||
|
||||
if (stage == 2) {
|
||||
visualize(player, 1241, SQUASH_GRAPHICS_END)
|
||||
return@queueScript delayScript(player, 2)
|
||||
}
|
||||
|
||||
if (stage == 3) {
|
||||
animate(player, SQUASH_ANIM_END, true)
|
||||
adjustLevel(player, Skills.FARMING, -5)
|
||||
return@queueScript keepRunning(player)
|
||||
}
|
||||
|
||||
return@queueScript stopExecuting(player)
|
||||
}
|
||||
}
|
||||
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
|
||||
class LaunchPulse(val player: Player): Pulse(){
|
||||
var counter = 0
|
||||
override fun pulse(): Boolean {
|
||||
when(counter++){
|
||||
1 -> visualize(player, LAUNCH_ANIMATION, LAUNCH_GRAPHICS)
|
||||
3 -> rewardXP(player, Skills.FARMING, 100.0)
|
||||
4 -> submitWorldPulse(GliderPulse(2, player, Gliders.TA_QUIR_PRIW)).also { return true }
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
class SquashPulse(val player: Player) : Pulse(){
|
||||
var counter = 0
|
||||
override fun pulse(): Boolean {
|
||||
when(counter++){
|
||||
1 -> visualize(player, SQUASH_ANIM_BEGIN, SQUASH_GRAPHICS_BEGIN)
|
||||
4 -> animate(player, 1241, true)
|
||||
5 -> teleport(player, Location.create(2464, 3494, 0))
|
||||
6 -> visualize(player, anim = 1241, gfx = SQUASH_GRAPHICS_END)
|
||||
8 -> animate(player, SQUASH_ANIM_END, true).also { adjustLevel(player, Skills.FARMING, -5) }
|
||||
9 -> unlock(player).also { return true }
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
package content.global.handlers.item
|
||||
|
||||
import core.api.*
|
||||
import core.game.interaction.Interaction
|
||||
import core.game.interaction.Option
|
||||
import core.game.interaction.*
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.impl.Projectile
|
||||
import core.game.node.entity.player.Player
|
||||
@@ -11,8 +10,6 @@ import core.game.system.task.Pulse
|
||||
import core.game.world.map.path.Pathfinder
|
||||
import core.game.world.update.flag.context.Graphics
|
||||
import org.rs09.consts.Items
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.IntType
|
||||
|
||||
|
||||
class PlayerPeltables : InteractionListener {
|
||||
@@ -40,7 +37,7 @@ class PlayerPeltables : InteractionListener {
|
||||
}
|
||||
|
||||
private fun removePlayerOps(player: Player, _node: Node) : Boolean {
|
||||
Interaction.sendOption(player, 0, "null")
|
||||
InteractPlugin.sendOption(player, 0, "null")
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -16,6 +16,8 @@ import core.game.world.update.flag.context.Graphics;
|
||||
import core.plugin.Initializable;
|
||||
import core.plugin.Plugin;
|
||||
|
||||
import static core.api.ContentAPIKt.stun;
|
||||
|
||||
/**
|
||||
* Handles the dragon spear special attack.
|
||||
* @author Emperor
|
||||
@@ -95,8 +97,7 @@ public final class ShoveSpecialHandler extends MeleeSwingHandler implements Plug
|
||||
}
|
||||
victim.getWalkingQueue().reset();
|
||||
victim.getPulseManager().clear();
|
||||
victim.animate(STUN_ANIM);
|
||||
victim.getStateManager().set(EntityState.STUNNED, 5);
|
||||
stun(victim, 5);
|
||||
if (dir != null) {
|
||||
Point p = Direction.getWalkPoint(dir);
|
||||
Location dest = victim.getLocation().transform(p.getX(), p.getY(), 0);
|
||||
|
||||
@@ -93,7 +93,7 @@ class BankBoothListener : InteractionListener {
|
||||
}
|
||||
}
|
||||
|
||||
private fun quickBankBoothUse(player: Player, node: Node): Boolean {
|
||||
private fun quickBankBoothUse(player: Player, node: Node, state: Int): Boolean {
|
||||
if (player.ironmanManager.checkRestriction(IronmanMode.ULTIMATE)) {
|
||||
return true
|
||||
}
|
||||
@@ -107,20 +107,20 @@ class BankBoothListener : InteractionListener {
|
||||
return true
|
||||
}
|
||||
|
||||
private fun regularBankBoothUse(player: Player, node: Node): Boolean {
|
||||
private fun regularBankBoothUse(player: Player, node: Node, state: Int): Boolean {
|
||||
if (player.ironmanManager.checkRestriction(IronmanMode.ULTIMATE)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (ServerConstants.BANK_BOOTH_QUICK_OPEN) {
|
||||
return quickBankBoothUse(player, node)
|
||||
return quickBankBoothUse(player, node, state)
|
||||
}
|
||||
|
||||
tryInvokeBankerDialogue(player, node)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun collectBankBoothUse(player: Player, node: Node): Boolean {
|
||||
private fun collectBankBoothUse(player: Player, node: Node, state: Int): Boolean {
|
||||
if (BankerNPC.checkLunarIsleRestriction(player, node)) {
|
||||
tryInvokeBankerDialogue(player, node)
|
||||
return true
|
||||
@@ -176,9 +176,9 @@ class BankBoothListener : InteractionListener {
|
||||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(BANK_BOOTHS, IntType.SCENERY, "use-quickly", "bank", handler = ::quickBankBoothUse)
|
||||
on(BANK_BOOTHS, IntType.SCENERY, "use", handler = ::regularBankBoothUse)
|
||||
on(BANK_BOOTHS, IntType.SCENERY, "collect", handler = ::collectBankBoothUse)
|
||||
defineInteraction(IntType.SCENERY, BANK_BOOTHS, "use-quickly", "bank", handler = ::quickBankBoothUse)
|
||||
defineInteraction(IntType.SCENERY, BANK_BOOTHS, "use", handler = ::regularBankBoothUse)
|
||||
defineInteraction(IntType.SCENERY, BANK_BOOTHS, "collect", handler = ::collectBankBoothUse)
|
||||
|
||||
if (ServerConstants.BANK_BOOTH_NOTE_ENABLED) {
|
||||
onUseAnyWith(IntType.SCENERY, *BANK_BOOTHS, handler = ::attemptToConvertItems)
|
||||
|
||||
@@ -20,9 +20,9 @@ private val BANK_CHESTS = intArrayOf(
|
||||
*/
|
||||
class BankChestListener : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
on(BANK_CHESTS, IntType.SCENERY, "bank", "use") { player, node ->
|
||||
defineInteraction(IntType.SCENERY, BANK_CHESTS, "bank", "use") {player, node, state ->
|
||||
openBankAccount(player)
|
||||
return@on true
|
||||
return@defineInteraction true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ private val BANK_DEPOSIT_BOXES = intArrayOf(
|
||||
*/
|
||||
class BankDepositBoxListener : InteractionListener {
|
||||
|
||||
private fun openDepositBox(player: Player, node: Node) : Boolean {
|
||||
private fun openDepositBox(player: Player, node: Node, state: Int) : Boolean {
|
||||
restrictForIronman(player, IronmanMode.ULTIMATE) {
|
||||
player.interfaceManager.open(Component(Components.BANK_DEPOSIT_BOX_11)).closeEvent = CloseEvent { p, _ ->
|
||||
p.interfaceManager.openDefaultTabs()
|
||||
@@ -55,6 +55,6 @@ class BankDepositBoxListener : InteractionListener {
|
||||
}
|
||||
|
||||
override fun defineListeners() {
|
||||
on(BANK_DEPOSIT_BOXES, IntType.SCENERY, "deposit", handler = ::openDepositBox)
|
||||
defineInteraction(IntType.SCENERY, BANK_DEPOSIT_BOXES, "deposit", handler = ::openDepositBox)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package content.global.handlers.scenery
|
||||
|
||||
import core.api.addItem
|
||||
import core.api.sendMessage
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.player.Player
|
||||
import org.rs09.consts.Items
|
||||
|
||||
class DoogleLeafInteraction : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
defineInteraction(IntType.SCENERY, intArrayOf(31155), "pick-leaf", handler = ::handleDoogle)
|
||||
}
|
||||
|
||||
fun handleDoogle(player: Player, node: Node, state: Int) : Boolean {
|
||||
if (!addItem(player, Items.DOOGLE_LEAVES_1573)) {
|
||||
sendMessage(player, "You don't have enough space in your inventory.")
|
||||
return true
|
||||
}
|
||||
sendMessage(player, "You pick some doogle leaves.")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import core.plugin.Plugin;
|
||||
* @author 'Vexia
|
||||
* @version 1.0
|
||||
*/
|
||||
@Initializable
|
||||
public class DoogleLeafPlugin extends OptionHandler {
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package content.global.handlers.scenery
|
||||
|
||||
import core.api.*
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.audio.Audio
|
||||
@@ -27,13 +28,13 @@ private val SOUND = Audio(3189)
|
||||
*/
|
||||
class MillingListener : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
on(HOPPER_CONTROLS, SCENERY, "operate", "pull") { player, _ ->
|
||||
defineInteraction(IntType.SCENERY, HOPPER_CONTROLS, "operate", "pull") { player, _, _ ->
|
||||
useHopperControl(player)
|
||||
return@on true
|
||||
return@defineInteraction true
|
||||
}
|
||||
on(FLOUR_BINS, SCENERY, "empty") { player, _ ->
|
||||
defineInteraction(IntType.SCENERY, FLOUR_BINS, "empty") {player, _, _ ->
|
||||
fillPot(player)
|
||||
return@on true
|
||||
return@defineInteraction true
|
||||
}
|
||||
onUseWith(SCENERY, intArrayOf(GRAIN, SWEETCORN), *HOPPERS) { player, used, _ ->
|
||||
fillHopper(player, used.asItem())
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user