mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-12 01:21:20 -07:00
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:
parent
5206c99151
commit
124eeab893
61 changed files with 1466 additions and 293 deletions
|
|
@ -42,7 +42,7 @@ public enum Consumables {
|
||||||
SALMON(new Food(new int[] {329}, new HealingEffect(9))),
|
SALMON(new Food(new int[] {329}, new HealingEffect(9))),
|
||||||
SLIMY_EEL(new Food(new int[] {3381}, new HealingEffect(6))),
|
SLIMY_EEL(new Food(new int[] {3381}, new HealingEffect(6))),
|
||||||
TUNA(new Food(new int[] {361}, new HealingEffect(10))),
|
TUNA(new Food(new int[] {361}, new HealingEffect(10))),
|
||||||
COOKED_KARAMBWAN(new Food(new int[] {3144}, new HealingEffect(18))),
|
COOKED_KARAMBWAN(new Food(new int[] {3144}, new HealingEffect(18)), true),
|
||||||
COOKED_CHOMPY(new Food(new int[] {2878}, new HealingEffect(10))),
|
COOKED_CHOMPY(new Food(new int[] {2878}, new HealingEffect(10))),
|
||||||
RAINBOW_FISH(new Food(new int[] {10136}, new HealingEffect(11))),
|
RAINBOW_FISH(new Food(new int[] {10136}, new HealingEffect(11))),
|
||||||
CAVE_EEL(new Food(new int[] {5003}, new HealingEffect(7))),
|
CAVE_EEL(new Food(new int[] {5003}, new HealingEffect(7))),
|
||||||
|
|
@ -364,24 +364,26 @@ public enum Consumables {
|
||||||
SC_MAGIC(new Potion(new int[] {14267, 14269, 14271, 14273, 14275}, new SkillEffect(Skills.MAGIC, 3, 0.1))),
|
SC_MAGIC(new Potion(new int[] {14267, 14269, 14271, 14273, 14275}, new SkillEffect(Skills.MAGIC, 3, 0.1))),
|
||||||
SC_SUMMONING(new Potion(new int[] {14277, 14279, 14281, 14283, 14285}, new SummoningEffect(7, 0.25)));
|
SC_SUMMONING(new Potion(new int[] {14277, 14279, 14281, 14283, 14285}, new SummoningEffect(7, 0.25)));
|
||||||
|
|
||||||
public static HashMap<Integer,Consumable> consumables = new HashMap<>();
|
public static HashMap<Integer,Consumables> consumables = new HashMap<>();
|
||||||
|
|
||||||
private final Consumable consumable;
|
private final Consumable consumable;
|
||||||
|
public boolean isIgnoreMainClock = false;
|
||||||
|
|
||||||
Consumables(Consumable consumable) {
|
Consumables(Consumable consumable) {
|
||||||
this.consumable = consumable;
|
this.consumable = consumable;
|
||||||
}
|
}
|
||||||
|
Consumables(Consumable consumable, boolean isIgnoreMainClock) {this.consumable = consumable; this.isIgnoreMainClock = isIgnoreMainClock;}
|
||||||
|
|
||||||
public Consumable getConsumable() {
|
public Consumable getConsumable() {
|
||||||
return consumable;
|
return consumable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Consumable getConsumableById(final int itemId) {
|
public static Consumables getConsumableById(final int itemId) {
|
||||||
return consumables.get(itemId);
|
return consumables.get(itemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void add(final Consumable consumable) {
|
public static void add(final Consumables consumable) {
|
||||||
for (int id : consumable.getIds()) {
|
for (int id : consumable.consumable.getIds()) {
|
||||||
consumables.putIfAbsent(id, consumable);
|
consumables.putIfAbsent(id, consumable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -391,7 +393,7 @@ public enum Consumables {
|
||||||
*/
|
*/
|
||||||
static {
|
static {
|
||||||
for (Consumables consumable : Consumables.values()) {
|
for (Consumables consumable : Consumables.values()) {
|
||||||
add(consumable.consumable);
|
add(consumable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import core.plugin.Plugin;
|
||||||
* Handles the searching of a bird nest item.
|
* Handles the searching of a bird nest item.
|
||||||
* @author Vexia
|
* @author Vexia
|
||||||
*/
|
*/
|
||||||
@Initializable
|
|
||||||
public final class BirdNestPlugin extends OptionHandler {
|
public final class BirdNestPlugin extends OptionHandler {
|
||||||
|
|
||||||
@Override
|
@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
|
* @author Emperor
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
*/
|
*/
|
||||||
@Initializable
|
|
||||||
public final class ConsumableOptionPlugin extends OptionHandler {
|
public final class ConsumableOptionPlugin extends OptionHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -35,7 +34,7 @@ public final class ConsumableOptionPlugin extends OptionHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(final Player player, final Node node, final String option) {
|
public boolean handle(final Player player, final Node node, final String option) {
|
||||||
if (player.getLocks().isLocked(option)) {
|
/* if (player.getLocks().isLocked(option)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
boolean food = option.equals("eat");
|
boolean food = option.equals("eat");
|
||||||
|
|
@ -61,7 +60,7 @@ public final class ConsumableOptionPlugin extends OptionHandler {
|
||||||
if (food) {
|
if (food) {
|
||||||
player.getProperties().getCombatPulse().delayNextAttack(3);
|
player.getProperties().getCombatPulse().delayNextAttack(3);
|
||||||
}
|
}
|
||||||
lastEaten = node.asItem().getId();
|
lastEaten = node.asItem().getId();*/
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,18 @@ package content.global.handlers.item
|
||||||
|
|
||||||
import content.global.travel.glider.GliderPulse
|
import content.global.travel.glider.GliderPulse
|
||||||
import content.global.travel.glider.Gliders
|
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.player.Player
|
||||||
import core.game.node.entity.skill.Skills
|
import core.game.node.entity.skill.Skills
|
||||||
import core.game.system.task.Pulse
|
import core.game.system.task.Pulse
|
||||||
import core.game.world.map.Location
|
import core.game.world.map.Location
|
||||||
|
import core.net.packet.PacketRepository
|
||||||
|
import core.net.packet.context.MinimapStateContext
|
||||||
|
import core.net.packet.out.MinimapState
|
||||||
import org.rs09.consts.Items
|
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_BEGIN = 767
|
||||||
private const val SQUASH_GRAPHICS_END = 769
|
private const val SQUASH_GRAPHICS_END = 769
|
||||||
|
|
@ -25,41 +29,68 @@ private const val LAUNCH_ANIMATION = 4547
|
||||||
class GrandSeedPodHandler : InteractionListener {
|
class GrandSeedPodHandler : InteractionListener {
|
||||||
|
|
||||||
override fun defineListeners() {
|
override fun defineListeners() {
|
||||||
on(Items.GRAND_SEED_POD_9469, IntType.ITEM, "squash", "launch"){ player, _ ->
|
on(intArrayOf(Items.GRAND_SEED_POD_9469), IntType.ITEM, "squash", "launch") { player, _ ->
|
||||||
when(getUsedOption(player)){
|
val opt = getUsedOption(player)
|
||||||
"launch" -> submitWorldPulse(LaunchPulse(player))
|
if (!removeItem(player, Items.GRAND_SEED_POD_9469)) return@on false
|
||||||
"squash" -> submitWorldPulse(SquashPulse(player))
|
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)
|
||||||
}
|
}
|
||||||
removeItem(player, Items.GRAND_SEED_POD_9469, Container.INVENTORY)
|
|
||||||
lock(player, 50)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
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
|
package content.global.handlers.item
|
||||||
|
|
||||||
import core.api.*
|
import core.api.*
|
||||||
import core.game.interaction.Interaction
|
import core.game.interaction.*
|
||||||
import core.game.interaction.Option
|
|
||||||
import core.game.node.Node
|
import core.game.node.Node
|
||||||
import core.game.node.entity.impl.Projectile
|
import core.game.node.entity.impl.Projectile
|
||||||
import core.game.node.entity.player.Player
|
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.map.path.Pathfinder
|
||||||
import core.game.world.update.flag.context.Graphics
|
import core.game.world.update.flag.context.Graphics
|
||||||
import org.rs09.consts.Items
|
import org.rs09.consts.Items
|
||||||
import core.game.interaction.InteractionListener
|
|
||||||
import core.game.interaction.IntType
|
|
||||||
|
|
||||||
|
|
||||||
class PlayerPeltables : InteractionListener {
|
class PlayerPeltables : InteractionListener {
|
||||||
|
|
@ -40,7 +37,7 @@ class PlayerPeltables : InteractionListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removePlayerOps(player: Player, _node: Node) : Boolean {
|
private fun removePlayerOps(player: Player, _node: Node) : Boolean {
|
||||||
Interaction.sendOption(player, 0, "null")
|
InteractPlugin.sendOption(player, 0, "null")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ import core.game.world.update.flag.context.Graphics;
|
||||||
import core.plugin.Initializable;
|
import core.plugin.Initializable;
|
||||||
import core.plugin.Plugin;
|
import core.plugin.Plugin;
|
||||||
|
|
||||||
|
import static core.api.ContentAPIKt.stun;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the dragon spear special attack.
|
* Handles the dragon spear special attack.
|
||||||
* @author Emperor
|
* @author Emperor
|
||||||
|
|
@ -95,8 +97,7 @@ public final class ShoveSpecialHandler extends MeleeSwingHandler implements Plug
|
||||||
}
|
}
|
||||||
victim.getWalkingQueue().reset();
|
victim.getWalkingQueue().reset();
|
||||||
victim.getPulseManager().clear();
|
victim.getPulseManager().clear();
|
||||||
victim.animate(STUN_ANIM);
|
stun(victim, 5);
|
||||||
victim.getStateManager().set(EntityState.STUNNED, 5);
|
|
||||||
if (dir != null) {
|
if (dir != null) {
|
||||||
Point p = Direction.getWalkPoint(dir);
|
Point p = Direction.getWalkPoint(dir);
|
||||||
Location dest = victim.getLocation().transform(p.getX(), p.getY(), 0);
|
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)) {
|
if (player.ironmanManager.checkRestriction(IronmanMode.ULTIMATE)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -107,20 +107,20 @@ class BankBoothListener : InteractionListener {
|
||||||
return true
|
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)) {
|
if (player.ironmanManager.checkRestriction(IronmanMode.ULTIMATE)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ServerConstants.BANK_BOOTH_QUICK_OPEN) {
|
if (ServerConstants.BANK_BOOTH_QUICK_OPEN) {
|
||||||
return quickBankBoothUse(player, node)
|
return quickBankBoothUse(player, node, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
tryInvokeBankerDialogue(player, node)
|
tryInvokeBankerDialogue(player, node)
|
||||||
return true
|
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)) {
|
if (BankerNPC.checkLunarIsleRestriction(player, node)) {
|
||||||
tryInvokeBankerDialogue(player, node)
|
tryInvokeBankerDialogue(player, node)
|
||||||
return true
|
return true
|
||||||
|
|
@ -176,9 +176,9 @@ class BankBoothListener : InteractionListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun defineListeners() {
|
override fun defineListeners() {
|
||||||
on(BANK_BOOTHS, IntType.SCENERY, "use-quickly", "bank", handler = ::quickBankBoothUse)
|
defineInteraction(IntType.SCENERY, BANK_BOOTHS, "use-quickly", "bank", handler = ::quickBankBoothUse)
|
||||||
on(BANK_BOOTHS, IntType.SCENERY, "use", handler = ::regularBankBoothUse)
|
defineInteraction(IntType.SCENERY, BANK_BOOTHS, "use", handler = ::regularBankBoothUse)
|
||||||
on(BANK_BOOTHS, IntType.SCENERY, "collect", handler = ::collectBankBoothUse)
|
defineInteraction(IntType.SCENERY, BANK_BOOTHS, "collect", handler = ::collectBankBoothUse)
|
||||||
|
|
||||||
if (ServerConstants.BANK_BOOTH_NOTE_ENABLED) {
|
if (ServerConstants.BANK_BOOTH_NOTE_ENABLED) {
|
||||||
onUseAnyWith(IntType.SCENERY, *BANK_BOOTHS, handler = ::attemptToConvertItems)
|
onUseAnyWith(IntType.SCENERY, *BANK_BOOTHS, handler = ::attemptToConvertItems)
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ private val BANK_CHESTS = intArrayOf(
|
||||||
*/
|
*/
|
||||||
class BankChestListener : InteractionListener {
|
class BankChestListener : InteractionListener {
|
||||||
override fun defineListeners() {
|
override fun defineListeners() {
|
||||||
on(BANK_CHESTS, IntType.SCENERY, "bank", "use") { player, node ->
|
defineInteraction(IntType.SCENERY, BANK_CHESTS, "bank", "use") {player, node, state ->
|
||||||
openBankAccount(player)
|
openBankAccount(player)
|
||||||
return@on true
|
return@defineInteraction true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +29,7 @@ private val BANK_DEPOSIT_BOXES = intArrayOf(
|
||||||
*/
|
*/
|
||||||
class BankDepositBoxListener : InteractionListener {
|
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) {
|
restrictForIronman(player, IronmanMode.ULTIMATE) {
|
||||||
player.interfaceManager.open(Component(Components.BANK_DEPOSIT_BOX_11)).closeEvent = CloseEvent { p, _ ->
|
player.interfaceManager.open(Component(Components.BANK_DEPOSIT_BOX_11)).closeEvent = CloseEvent { p, _ ->
|
||||||
p.interfaceManager.openDefaultTabs()
|
p.interfaceManager.openDefaultTabs()
|
||||||
|
|
@ -55,6 +55,6 @@ class BankDepositBoxListener : InteractionListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun defineListeners() {
|
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
|
* @author 'Vexia
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
*/
|
*/
|
||||||
@Initializable
|
|
||||||
public class DoogleLeafPlugin extends OptionHandler {
|
public class DoogleLeafPlugin extends OptionHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package content.global.handlers.scenery
|
package content.global.handlers.scenery
|
||||||
|
|
||||||
import core.api.*
|
import core.api.*
|
||||||
|
import core.game.interaction.IntType
|
||||||
import core.game.interaction.InteractionListener
|
import core.game.interaction.InteractionListener
|
||||||
import core.game.node.entity.player.Player
|
import core.game.node.entity.player.Player
|
||||||
import core.game.node.entity.player.link.audio.Audio
|
import core.game.node.entity.player.link.audio.Audio
|
||||||
|
|
@ -27,13 +28,13 @@ private val SOUND = Audio(3189)
|
||||||
*/
|
*/
|
||||||
class MillingListener : InteractionListener {
|
class MillingListener : InteractionListener {
|
||||||
override fun defineListeners() {
|
override fun defineListeners() {
|
||||||
on(HOPPER_CONTROLS, SCENERY, "operate", "pull") { player, _ ->
|
defineInteraction(IntType.SCENERY, HOPPER_CONTROLS, "operate", "pull") { player, _, _ ->
|
||||||
useHopperControl(player)
|
useHopperControl(player)
|
||||||
return@on true
|
return@defineInteraction true
|
||||||
}
|
}
|
||||||
on(FLOUR_BINS, SCENERY, "empty") { player, _ ->
|
defineInteraction(IntType.SCENERY, FLOUR_BINS, "empty") {player, _, _ ->
|
||||||
fillPot(player)
|
fillPot(player)
|
||||||
return@on true
|
return@defineInteraction true
|
||||||
}
|
}
|
||||||
onUseWith(SCENERY, intArrayOf(GRAIN, SWEETCORN), *HOPPERS) { player, used, _ ->
|
onUseWith(SCENERY, intArrayOf(GRAIN, SWEETCORN), *HOPPERS) { player, used, _ ->
|
||||||
fillHopper(player, used.asItem())
|
fillHopper(player, used.asItem())
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,20 @@
|
||||||
package content.global.skill.gather
|
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.fishing.FishingSpot
|
||||||
import content.global.skill.gather.fishing.FishingPulse
|
import content.global.skill.gather.fishing.FishingPulse
|
||||||
import content.global.skill.gather.mining.MiningSkillPulse
|
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.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 {
|
class GatheringSkillOptionListeners : InteractionListener {
|
||||||
|
|
||||||
val ETCETERIA_REGION = 10300
|
val ETCETERIA_REGION = 10300
|
||||||
|
|
||||||
override fun defineListeners() {
|
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{
|
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;
|
double experience,rate;
|
||||||
public byte identifier;
|
public byte identifier;
|
||||||
boolean farming;
|
boolean farming;
|
||||||
double baseLow = 64;
|
public double baseLow = 64;
|
||||||
double baseHigh = 200;
|
public double baseHigh = 200;
|
||||||
double tierModLow = 32;
|
public double tierModLow = 32;
|
||||||
double tierModHigh = 100;
|
public double tierModHigh = 100;
|
||||||
WoodcuttingNode(int full, int empty,byte identifier){
|
WoodcuttingNode(int full, int empty,byte identifier){
|
||||||
this.full = full;
|
this.full = full;
|
||||||
this.empty = empty;
|
this.empty = empty;
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ public final class StatBoostSpell extends MagicSpell {
|
||||||
public boolean cast(Entity entity, Node target) {
|
public boolean cast(Entity entity, Node target) {
|
||||||
final Player player = ((Player) entity);
|
final Player player = ((Player) entity);
|
||||||
Item item = ((Item) target);
|
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);
|
player.getInterfaceManager().setViewedTab(6);
|
||||||
if (potion == null) {
|
if (potion == null) {
|
||||||
player.getPacketDispatch().sendMessage("You can only cast this spell on a potion.");
|
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) {
|
public boolean cast(Entity entity, Node target) {
|
||||||
final Player player = ((Player) entity);
|
final Player player = ((Player) entity);
|
||||||
Item item = ((Item) target);
|
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);
|
player.getInterfaceManager().setViewedTab(6);
|
||||||
if (potion == null) {
|
if (potion == null) {
|
||||||
player.getPacketDispatch().sendMessage("You can only cast this spell on a potion.");
|
player.getPacketDispatch().sendMessage("You can only cast this spell on a potion.");
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package content.global.skill.runecrafting;
|
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.skill.Skills;
|
||||||
import core.game.node.entity.player.Player;
|
import core.game.node.entity.player.Player;
|
||||||
import core.game.node.item.Item;
|
import core.game.node.item.Item;
|
||||||
|
|
@ -195,7 +195,7 @@ public enum RunePouch {
|
||||||
*/
|
*/
|
||||||
private void drop(Player player, Item item) {
|
private void drop(Player player, Item item) {
|
||||||
onDrop(player, 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.");
|
player.sendMessage("You can't use this special on an object like that.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Consumable consumable = Consumables.getConsumableById(special.getItem().getId() + 2);
|
Consumable consumable = Consumables.getConsumableById(special.getItem().getId() + 2).getConsumable();
|
||||||
if (consumable == null) {
|
if (consumable == null) {
|
||||||
player.sendMessage("Error: Report to admin.");
|
player.sendMessage("Error: Report to admin.");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -138,7 +138,7 @@ public class BunyipNPC extends Familiar {
|
||||||
public boolean handle(NodeUsageEvent event) {
|
public boolean handle(NodeUsageEvent event) {
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
Fish fish = Fish.forItem(event.getUsedItem());
|
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) {
|
if (consumable == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import core.plugin.ClassScanner;
|
||||||
import core.plugin.Initializable;
|
import core.plugin.Initializable;
|
||||||
import core.tools.RandomFunction;
|
import core.tools.RandomFunction;
|
||||||
|
|
||||||
|
import static core.api.ContentAPIKt.stun;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The plugin used to load the minotaur familiar npcs.
|
* The plugin used to load the minotaur familiar npcs.
|
||||||
* @author Vexia
|
* @author Vexia
|
||||||
|
|
@ -58,7 +60,7 @@ public final class MinotaurFamiliarNPC implements Plugin<Object> {
|
||||||
GameWorld.getPulser().submit(new Pulse(ticks) {
|
GameWorld.getPulser().submit(new Pulse(ticks) {
|
||||||
@Override
|
@Override
|
||||||
public boolean pulse() {
|
public boolean pulse() {
|
||||||
target.getStateManager().set(EntityState.STUNNED, 4);
|
stun(target, 4);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ public class RavenousLocustNPC extends Familiar {
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Consumable consumable = Consumables.getConsumableById(item.getId());
|
Consumable consumable = Consumables.getConsumableById(item.getId()).getConsumable();
|
||||||
if (consumable != null) {
|
if (consumable != null) {
|
||||||
p.getInventory().remove(item);
|
p.getInventory().remove(item);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package content.global.skill.thieving
|
package content.global.skill.thieving
|
||||||
|
|
||||||
|
import core.api.stun
|
||||||
import core.game.node.entity.combat.ImpactHandler
|
import core.game.node.entity.combat.ImpactHandler
|
||||||
import core.game.node.entity.impl.Animator
|
import core.game.node.entity.impl.Animator
|
||||||
import core.game.node.entity.player.Player
|
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
|
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.audioManager.send(hitSoundId, 1, 20) // OSRS defines a delay of 20
|
||||||
|
|
||||||
player.stateManager.set(EntityState.STUNNED, secondsToTicks(pickpocketData.stunTime))
|
stun(player, pickpocketData.stunTime)
|
||||||
player.lock(secondsToTicks(pickpocketData.stunTime))
|
|
||||||
|
|
||||||
player.impactHandler.manualHit(node.asNpc(),RandomFunction.random(pickpocketData.stunDamageMin,pickpocketData.stunDamageMax),ImpactHandler.HitsplatType.NORMAL)
|
player.impactHandler.manualHit(node.asNpc(),RandomFunction.random(pickpocketData.stunDamageMin,pickpocketData.stunDamageMax),ImpactHandler.HitsplatType.NORMAL)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ import core.game.world.map.Location;
|
||||||
import core.game.world.map.RegionManager;
|
import core.game.world.map.RegionManager;
|
||||||
import core.plugin.Plugin;
|
import core.plugin.Plugin;
|
||||||
|
|
||||||
|
import static core.api.ContentAPIKt.isStunned;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the bounty target locate spell.
|
* Handles the bounty target locate spell.
|
||||||
* @author Emperor
|
* @author Emperor
|
||||||
|
|
@ -43,8 +45,8 @@ public final class BountyLocateSpell extends MagicSpell {
|
||||||
player.getPacketDispatch().sendMessage("You don't have a target to teleport to.");
|
player.getPacketDispatch().sendMessage("You don't have a target to teleport to.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (player.getStateManager().hasState(EntityState.FROZEN) || player.getStateManager().hasState(EntityState.STUNNED)) {
|
if (player.getStateManager().hasState(EntityState.FROZEN) || isStunned(player)) {
|
||||||
player.getPacketDispatch().sendMessage("You can't use this when " + (player.getStateManager().hasState(EntityState.STUNNED) ? "stunned." : "frozen."));
|
player.getPacketDispatch().sendMessage("You can't use this when " + (isStunned(player) ? "stunned." : "frozen."));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
boolean combat = player.inCombat();
|
boolean combat = player.inCombat();
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import core.game.dialogue.DialoguePlugin;
|
||||||
import core.game.dialogue.FacialExpression;
|
import core.game.dialogue.FacialExpression;
|
||||||
import core.game.global.action.ClimbActionHandler;
|
import core.game.global.action.ClimbActionHandler;
|
||||||
import core.game.global.action.DoorActionHandler;
|
import core.game.global.action.DoorActionHandler;
|
||||||
import core.game.global.action.DropItemHandler;
|
import core.game.global.action.DropListener;
|
||||||
import core.game.interaction.NodeUsageEvent;
|
import core.game.interaction.NodeUsageEvent;
|
||||||
import core.game.interaction.OptionHandler;
|
import core.game.interaction.OptionHandler;
|
||||||
import core.game.interaction.UseWithHandler;
|
import core.game.interaction.UseWithHandler;
|
||||||
|
|
@ -143,7 +143,7 @@ public final class MerlinCrystalPlugin extends OptionHandler {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
DropItemHandler.drop(player, node.asItem());
|
DropListener.drop(player, node.asItem());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case 40026:
|
case 40026:
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class MortMyreGhastNPC : AbstractNPC {
|
||||||
for(i in player.inventory.toArray()){
|
for(i in player.inventory.toArray()){
|
||||||
if(i == null) continue
|
if(i == null) continue
|
||||||
val consumable = Consumables.getConsumableById(i.id)
|
val consumable = Consumables.getConsumableById(i.id)
|
||||||
if(consumable != null && consumable is Food) {
|
if(consumable != null && consumable.consumable is Food) {
|
||||||
hasFood = true
|
hasFood = true
|
||||||
removeItem(player, i, Container.INVENTORY)
|
removeItem(player, i, Container.INVENTORY)
|
||||||
addItem(player, Items.ROTTEN_FOOD_2959)
|
addItem(player, Items.ROTTEN_FOOD_2959)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ import core.game.world.map.Location;
|
||||||
import core.plugin.Initializable;
|
import core.plugin.Initializable;
|
||||||
import core.tools.RandomFunction;
|
import core.tools.RandomFunction;
|
||||||
|
|
||||||
|
import static core.api.ContentAPIKt.isStunned;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the Dark Energy Core NPC.
|
* Handles the Dark Energy Core NPC.
|
||||||
* @author Emperor
|
* @author Emperor
|
||||||
|
|
@ -70,7 +72,7 @@ public final class DarkEnergyCoreNPC extends AbstractNPC {
|
||||||
public void handleTickActions() {
|
public void handleTickActions() {
|
||||||
ticks++;
|
ticks++;
|
||||||
boolean poisoned = getStateManager().hasState(EntityState.POISONED);
|
boolean poisoned = getStateManager().hasState(EntityState.POISONED);
|
||||||
if (getStateManager().hasState(EntityState.STUNNED) || isInvisible()) {
|
if (isStunned(this) || isInvisible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (fails == 0 && poisoned && (ticks % 100) != 0) {
|
if (fails == 0 && poisoned && (ticks % 100) != 0) {
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,9 @@ import core.game.interaction.InteractionListeners
|
||||||
import content.global.handlers.iface.ge.StockMarket
|
import content.global.handlers.iface.ge.StockMarket
|
||||||
import content.global.skill.slayer.SlayerManager
|
import content.global.skill.slayer.SlayerManager
|
||||||
import core.game.activity.Cutscene
|
import core.game.activity.Cutscene
|
||||||
|
import core.game.interaction.Clocks
|
||||||
|
import core.game.interaction.QueueStrength
|
||||||
|
import core.game.interaction.QueuedScript
|
||||||
import core.game.node.entity.player.info.LogType
|
import core.game.node.entity.player.info.LogType
|
||||||
import core.game.node.entity.player.info.PlayerMonitor
|
import core.game.node.entity.player.info.PlayerMonitor
|
||||||
import core.tools.SystemLogger
|
import core.tools.SystemLogger
|
||||||
|
|
@ -60,7 +63,9 @@ import core.game.system.config.ItemConfigParser
|
||||||
import core.game.system.config.ServerConfigParser
|
import core.game.system.config.ServerConfigParser
|
||||||
import core.game.world.GameWorld
|
import core.game.world.GameWorld
|
||||||
import core.game.world.GameWorld.Pulser
|
import core.game.world.GameWorld.Pulser
|
||||||
|
import core.game.world.map.path.ProjectilePathfinder
|
||||||
import core.game.world.repository.Repository
|
import core.game.world.repository.Repository
|
||||||
|
import core.tools.tick
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2261,6 +2266,97 @@ fun addDialogueAction(player: Player, action: core.game.dialogue.DialogueAction)
|
||||||
player.dialogueInterpreter.addAction(action)
|
player.dialogueInterpreter.addAction(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by content handlers to check if the entity is done moving yet
|
||||||
|
*/
|
||||||
|
fun finishedMoving(entity: Entity) : Boolean {
|
||||||
|
return entity.clocks[Clocks.MOVEMENT] < GameWorld.ticks
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delay the execution of the currently running script
|
||||||
|
*/
|
||||||
|
fun delayScript(entity: Entity, ticks: Int): Boolean {
|
||||||
|
entity.scripts.getActiveScript()?.let { it.nextExecution = GameWorld.ticks + ticks }
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the global delay for the entity, pausing execution of all queues/scripts until passed.
|
||||||
|
*/
|
||||||
|
fun delayEntity(entity: Entity, ticks: Int) {
|
||||||
|
entity.scripts.delay = GameWorld.ticks + ticks
|
||||||
|
lock(entity, 5) //TODO: REMOVE WHEN EVERYTHING IMPORTANT USES PROPER QUEUES - THIS IS INCORRECT BEHAVIOR
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apRange(entity: Entity, apRange: Int) {
|
||||||
|
entity.scripts.apRange = apRange
|
||||||
|
entity.scripts.apRangeCalled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasLineOfSight(entity: Entity, target: Node) : Boolean {
|
||||||
|
return ProjectilePathfinder.find(entity, target).isSuccessful
|
||||||
|
}
|
||||||
|
|
||||||
|
fun animationFinished(entity: Entity) : Boolean {
|
||||||
|
return entity.clocks[Clocks.ANIMATION_END] < GameWorld.ticks
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearScripts(entity: Entity) : Boolean {
|
||||||
|
entity.scripts.reset()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun restartScript(entity: Entity) : Boolean {
|
||||||
|
if (entity.scripts.getActiveScript()?.persist != true) {
|
||||||
|
SystemLogger.logErr(entity.scripts.getActiveScript()!!::class.java, "Tried to call restartScript on a non-persistent script! Either use stopExecuting() or make the script persistent.")
|
||||||
|
return clearScripts(entity)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun keepRunning(entity: Entity) : Boolean {
|
||||||
|
entity.scripts.getActiveScript()?.nextExecution = getWorldTicks() + 1
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopExecuting(entity: Entity) : Boolean {
|
||||||
|
if (entity.scripts.getActiveScript()?.persist == true) {
|
||||||
|
SystemLogger.logErr(entity.scripts.getActiveScript()!!::class.java, "Tried to call stopExecuting() on a persistent script! To halt execution of a persistent script, you MUST call clearScripts()!")
|
||||||
|
return clearScripts(entity)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun queueScript(entity: Entity, delay: Int = 1, strength: QueueStrength = QueueStrength.WEAK, persist: Boolean = false, script: (stage: Int) -> Boolean) {
|
||||||
|
val s = QueuedScript(script, strength, persist)
|
||||||
|
s.nextExecution = getWorldTicks() + delay
|
||||||
|
entity.scripts.addToQueue(s, strength)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun delayAttack(entity: Entity, ticks: Int) {
|
||||||
|
entity.properties.combatPulse.delayNextAttack(3)
|
||||||
|
entity.clocks[Clocks.NEXT_ATTACK] = getWorldTicks() + ticks
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stun(entity: Entity, ticks: Int) {
|
||||||
|
entity.walkingQueue.reset()
|
||||||
|
entity.pulseManager.clear()
|
||||||
|
entity.locks.lockMovement(ticks)
|
||||||
|
entity.clocks[Clocks.STUN] = getWorldTicks() + ticks
|
||||||
|
entity.graphics(Graphics(80, 96))
|
||||||
|
if (entity is Player) {
|
||||||
|
entity.audioManager.send(Audio(2727, 1, 0))
|
||||||
|
entity.animate(Animation(424, Animator.Priority.VERY_HIGH))
|
||||||
|
sendMessage(entity, "You have been stunned!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isStunned(entity: Entity) : Boolean {
|
||||||
|
return entity.clocks[Clocks.STUN] >= getWorldTicks()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modifies prayer points by value
|
* Modifies prayer points by value
|
||||||
* @param player the player to modify prayer points
|
* @param player the player to modify prayer points
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,13 @@ import core.cache.Cache;
|
||||||
import core.cache.def.Definition;
|
import core.cache.def.Definition;
|
||||||
import core.cache.misc.buffer.ByteBufferUtils;
|
import core.cache.misc.buffer.ByteBufferUtils;
|
||||||
import core.game.container.Container;
|
import core.game.container.Container;
|
||||||
import core.game.global.action.DropItemHandler;
|
|
||||||
import core.game.interaction.OptionHandler;
|
import core.game.interaction.OptionHandler;
|
||||||
import core.game.node.Node;
|
|
||||||
import core.game.node.entity.player.Player;
|
import core.game.node.entity.player.Player;
|
||||||
import core.game.node.entity.skill.Skills;
|
import core.game.node.entity.skill.Skills;
|
||||||
import core.game.node.item.Item;
|
import core.game.node.item.Item;
|
||||||
import core.game.node.item.ItemPlugin;
|
import core.game.node.item.ItemPlugin;
|
||||||
import core.net.packet.PacketRepository;
|
import core.net.packet.PacketRepository;
|
||||||
import core.net.packet.out.WeightUpdate;
|
import core.net.packet.out.WeightUpdate;
|
||||||
import core.plugin.Plugin;
|
|
||||||
import core.tools.StringUtils;
|
import core.tools.StringUtils;
|
||||||
import core.tools.SystemLogger;
|
import core.tools.SystemLogger;
|
||||||
import core.game.system.config.ItemConfigParser;
|
import core.game.system.config.ItemConfigParser;
|
||||||
|
|
@ -259,32 +256,6 @@ public class ItemDefinition extends Definition<Item> {
|
||||||
options = new String[] { null, null, null, null, "drop" };
|
options = new String[] { null, null, null, null, "drop" };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the default option handlers.
|
|
||||||
*/
|
|
||||||
static {
|
|
||||||
// TODO: Move this crap in a plugin.
|
|
||||||
OptionHandler handler = new OptionHandler() {
|
|
||||||
@Override
|
|
||||||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handle(final Player player, Node node, String option) {
|
|
||||||
return DropItemHandler.handle(player, node, option);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isWalk() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
setOptionHandler("destroy", handler);
|
|
||||||
setOptionHandler("dissolve", handler);
|
|
||||||
setOptionHandler("drop", handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the item definitions.
|
* Parses the item definitions.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class CombatBot(location: Location) : AIPlayer(location) {
|
||||||
this.lock(3)
|
this.lock(3)
|
||||||
//this.animate(new Animation(829));
|
//this.animate(new Animation(829));
|
||||||
val food = inventory.getItem(foodItem)
|
val food = inventory.getItem(foodItem)
|
||||||
var consumable: Consumable? = Consumables.getConsumableById(food.id)
|
var consumable: Consumable? = Consumables.getConsumableById(food.id)?.consumable
|
||||||
if (consumable == null) {
|
if (consumable == null) {
|
||||||
consumable = Food(IntArray(food.id), HealingEffect(1))
|
consumable = Food(IntArray(food.id), HealingEffect(1))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -133,10 +133,10 @@ public class PvMBots extends AIPlayer {
|
||||||
//this.animate(new Animation(829));
|
//this.animate(new Animation(829));
|
||||||
Item food = this.getInventory().getItem(foodItem);
|
Item food = this.getInventory().getItem(foodItem);
|
||||||
|
|
||||||
Consumable consumable = Consumables.getConsumableById(food.getId());
|
Consumable consumable = Consumables.getConsumableById(food.getId()).getConsumable();
|
||||||
|
|
||||||
if (consumable == null) {
|
if (consumable == null) {
|
||||||
consumable = new Food(new int[] {food.getId()}, new HealingEffect(1));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
consumable.consume(food, this);
|
consumable.consume(food, this);
|
||||||
|
|
|
||||||
|
|
@ -653,7 +653,7 @@ class ScriptAPI(private val bot: Player) {
|
||||||
bot.lock(3)
|
bot.lock(3)
|
||||||
//this.animate(new Animation(829));
|
//this.animate(new Animation(829));
|
||||||
val food = bot.inventory.getItem(foodItem)
|
val food = bot.inventory.getItem(foodItem)
|
||||||
var consumable: Consumable? = Consumables.getConsumableById(foodId)
|
var consumable: Consumable? = Consumables.getConsumableById(foodId)?.consumable
|
||||||
if (consumable == null) {
|
if (consumable == null) {
|
||||||
consumable = Food(intArrayOf(food.id), HealingEffect(1))
|
consumable = Food(intArrayOf(food.id), HealingEffect(1))
|
||||||
}
|
}
|
||||||
|
|
@ -673,7 +673,7 @@ class ScriptAPI(private val bot: Player) {
|
||||||
bot.lock(3)
|
bot.lock(3)
|
||||||
//this.animate(new Animation(829));
|
//this.animate(new Animation(829));
|
||||||
val food = bot.inventory.getItem(foodItem)
|
val food = bot.inventory.getItem(foodItem)
|
||||||
var consumable: Consumable? = Consumables.getConsumableById(foodId)
|
var consumable: Consumable? = Consumables.getConsumableById(foodId)?.consumable
|
||||||
if (consumable == null) {
|
if (consumable == null) {
|
||||||
consumable = Food(intArrayOf(foodId), HealingEffect(1))
|
consumable = Food(intArrayOf(foodId), HealingEffect(1))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ public class Cake extends Food {
|
||||||
player.getInventory().remove(item);
|
player.getInventory().remove(item);
|
||||||
}
|
}
|
||||||
final int initialLifePoints = player.getSkills().getLifepoints();
|
final int initialLifePoints = player.getSkills().getLifepoints();
|
||||||
Consumables.getConsumableById(item.getId()).effect.activate(player);
|
Consumables.getConsumableById(item.getId()).getConsumable().effect.activate(player);
|
||||||
sendMessages(player, initialLifePoints, item, messages);
|
sendMessages(player, initialLifePoints, item, messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import core.plugin.Plugin;
|
||||||
/**
|
/**
|
||||||
* Represents any item that has a consumption option such as 'Eat' or 'Drink'.
|
* Represents any item that has a consumption option such as 'Eat' or 'Drink'.
|
||||||
*/
|
*/
|
||||||
public abstract class Consumable implements Plugin<Object> {
|
public abstract class Consumable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the item IDs of all the variants of a consumable where the last one is often the empty container, if it has any.
|
* Represents the item IDs of all the variants of a consumable where the last one is often the empty container, if it has any.
|
||||||
|
|
@ -58,7 +58,7 @@ public abstract class Consumable implements Plugin<Object> {
|
||||||
addItem(player, nextItemId, 1, Container.INVENTORY);
|
addItem(player, nextItemId, 1, Container.INVENTORY);
|
||||||
}
|
}
|
||||||
final int initialLifePoints = player.getSkills().getLifepoints();
|
final int initialLifePoints = player.getSkills().getLifepoints();
|
||||||
Consumables.getConsumableById(item.getId()).effect.activate(player);
|
Consumables.getConsumableById(item.getId()).getConsumable().effect.activate(player);
|
||||||
sendMessages(player, initialLifePoints, item, messages);
|
sendMessages(player, initialLifePoints, item, messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,17 +100,6 @@ public abstract class Consumable implements Plugin<Object> {
|
||||||
return item.getName().replace("(4)", "").replace("(3)", "").replace("(2)", "").replace("(1)", "").trim().toLowerCase();
|
return item.getName().replace("(4)", "").replace("(3)", "").replace("(2)", "").replace("(1)", "").trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
|
||||||
Consumables.add(this);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object fireEvent(String identifier, Object... args) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getHealthEffectValue(Player player) {
|
public int getHealthEffectValue(Player player) {
|
||||||
return effect.getHealthEffectValue(player);
|
return effect.getHealthEffectValue(player);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ public class Potion extends Drink {
|
||||||
}
|
}
|
||||||
|
|
||||||
final int initialLifePoints = player.getSkills().getLifepoints();
|
final int initialLifePoints = player.getSkills().getLifepoints();
|
||||||
Consumables.getConsumableById(item.getId()).effect.activate(player);
|
Consumables.getConsumableById(item.getId()).getConsumable().effect.activate(player);
|
||||||
if (messages.length == 0) {
|
if (messages.length == 0) {
|
||||||
sendDefaultMessages(player, item);
|
sendDefaultMessages(player, item);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
package core.game.global.action;
|
|
||||||
|
|
||||||
import core.game.node.Node;
|
|
||||||
import core.game.node.entity.player.Player;
|
|
||||||
import core.game.node.entity.player.info.login.PlayerParser;
|
|
||||||
import core.game.node.entity.player.link.audio.Audio;
|
|
||||||
import core.game.node.item.GroundItemManager;
|
|
||||||
import core.game.node.item.Item;
|
|
||||||
import core.game.node.entity.combat.graves.GraveController;
|
|
||||||
import core.tools.SystemLogger;
|
|
||||||
import core.game.system.config.ItemConfigParser;
|
|
||||||
import core.game.world.GameWorld;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the dropping of an item.
|
|
||||||
* @author Vexia
|
|
||||||
*/
|
|
||||||
public final class DropItemHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the droping of an item.
|
|
||||||
* @param player the player.
|
|
||||||
* @param node the node.
|
|
||||||
* @param option the option.
|
|
||||||
* @return {@code True} if so.
|
|
||||||
*/
|
|
||||||
public static boolean handle(final Player player, Node node, String option) {
|
|
||||||
Item item = (Item) node;
|
|
||||||
if (item.getSlot() == -1) {
|
|
||||||
player.getPacketDispatch().sendMessage("Invalid slot!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (option) {
|
|
||||||
case "drop":
|
|
||||||
case "destroy":
|
|
||||||
case "dissolve":
|
|
||||||
if (!player.getInterfaceManager().close()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
player.getDialogueInterpreter().close();
|
|
||||||
player.getPulseManager().clear();
|
|
||||||
if (option.equalsIgnoreCase("destroy") || option.equalsIgnoreCase("dissolve") || (boolean) item.getDefinition().getHandlers().getOrDefault(ItemConfigParser.DESTROY,false)) {
|
|
||||||
player.getDialogueInterpreter().open(9878, item);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (GraveController.hasGraveAt(player.getLocation())) {
|
|
||||||
player.sendMessage("You cannot drop items on top of graves!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (player.getAttribute("equipLock:" + item.getId(), 0) > GameWorld.getTicks()) {
|
|
||||||
SystemLogger.logAlert(DropItemHandler.class, player + ", tried to do the drop & equip dupe.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (player.getInventory().replace(null, item.getSlot()) == item) {
|
|
||||||
item = item.getDropItem();
|
|
||||||
player.getAudioManager().send(new Audio(item.getId() == 995 ? 10 : 2739, 1, 0));//2739 ACTUAL DROP SOUND
|
|
||||||
GroundItemManager.create(item, player.getLocation(), player);
|
|
||||||
PlayerParser.save(player);
|
|
||||||
}
|
|
||||||
player.setAttribute("droppedItem:" + item.getId(), GameWorld.getTicks() + 2);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Drops an item.
|
|
||||||
* @param player the player.
|
|
||||||
* @param item the item.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static boolean drop(Player player, Item item) {
|
|
||||||
return handle(player, item, item.getDefinition().hasDestroyAction() ? "destroy" : "drop");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
51
Server/src/main/core/game/global/action/DropListener.kt
Normal file
51
Server/src/main/core/game/global/action/DropListener.kt
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package core.game.global.action
|
||||||
|
|
||||||
|
import core.api.*
|
||||||
|
import core.game.interaction.IntType
|
||||||
|
import core.game.interaction.InteractionListener
|
||||||
|
import core.game.interaction.QueueStrength
|
||||||
|
import core.game.node.Node
|
||||||
|
import core.game.node.entity.combat.graves.GraveController
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
|
import core.game.node.entity.player.info.login.PlayerParser
|
||||||
|
import core.game.node.entity.player.link.audio.Audio
|
||||||
|
import core.game.node.item.GroundItemManager
|
||||||
|
import core.game.node.item.Item
|
||||||
|
import core.game.system.config.ItemConfigParser
|
||||||
|
|
||||||
|
class DropListener : InteractionListener {
|
||||||
|
override fun defineListeners() {
|
||||||
|
on(IntType.ITEM, "drop", "destroy", "dissolve", handler = ::handleDropAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic fun drop(player: Player, item: Item) : Boolean {
|
||||||
|
return handleDropAction(player, item)
|
||||||
|
}
|
||||||
|
private fun handleDropAction(player: Player, node: Node) : Boolean {
|
||||||
|
val option = getUsedOption(player)
|
||||||
|
var item = node as? Item ?: return false
|
||||||
|
if (option == "drop") {
|
||||||
|
if (GraveController.hasGraveAt(player.location)) {
|
||||||
|
sendMessage(player, "You cannot drop items on top of graves!")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (getAttribute(player, "equipLock:${node.id}", 0 ) > getWorldTicks())
|
||||||
|
return false
|
||||||
|
|
||||||
|
queueScript (player, strength = QueueStrength.SOFT) {
|
||||||
|
if (player.inventory.replace(null, item.slot) != item) return@queueScript stopExecuting(player)
|
||||||
|
item = item.dropItem
|
||||||
|
player.audioManager.send(Audio(if (item.id == 995) 10 else 2739, 1, 0))
|
||||||
|
GroundItemManager.create(item, player.location, player)
|
||||||
|
setAttribute(player, "droppedItem:${item.id}", getWorldTicks() + 2)
|
||||||
|
PlayerParser.save(player)
|
||||||
|
return@queueScript stopExecuting(player)
|
||||||
|
}
|
||||||
|
} else if (option == "destroy" || option == "dissolve" || item.definition.handlers.getOrDefault(ItemConfigParser.DESTROY, false) as Boolean) {
|
||||||
|
player.dialogueInterpreter.open(9878, item)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Server/src/main/core/game/interaction/Clocks.kt
Normal file
11
Server/src/main/core/game/interaction/Clocks.kt
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
package core.game.interaction
|
||||||
|
|
||||||
|
object Clocks {
|
||||||
|
@JvmStatic val MOVEMENT = 0
|
||||||
|
@JvmStatic val ANIMATION_END = 1
|
||||||
|
@JvmStatic val NEXT_EAT = 2
|
||||||
|
@JvmStatic val NEXT_CONSUME = 3
|
||||||
|
@JvmStatic val NEXT_DRINK = 4
|
||||||
|
@JvmStatic val NEXT_ATTACK = 5
|
||||||
|
@JvmStatic val STUN = 6
|
||||||
|
}
|
||||||
|
|
@ -20,7 +20,7 @@ import core.game.world.GameWorld;
|
||||||
* Handles interaction between nodes.
|
* Handles interaction between nodes.
|
||||||
* @author Emperor
|
* @author Emperor
|
||||||
*/
|
*/
|
||||||
public class Interaction {
|
public class InteractPlugin {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current options.
|
* The current options.
|
||||||
|
|
@ -41,7 +41,7 @@ public class Interaction {
|
||||||
* Constructs a new {@code Interaction} {@code Object}.
|
* Constructs a new {@code Interaction} {@code Object}.
|
||||||
* @param node The node reference.
|
* @param node The node reference.
|
||||||
*/
|
*/
|
||||||
public Interaction(Node node) {
|
public InteractPlugin(Node node) {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@ interface InteractionListener : ContentInterface{
|
||||||
fun on(ids: IntArray, type: IntType, vararg option: String, handler: (player: Player, node: Node) -> Boolean){
|
fun on(ids: IntArray, type: IntType, vararg option: String, handler: (player: Player, node: Node) -> Boolean){
|
||||||
InteractionListeners.add(ids, type.ordinal, option, handler)
|
InteractionListeners.add(ids, type.ordinal, option, handler)
|
||||||
}
|
}
|
||||||
fun on(option: String, type: IntType, handler: (player: Player, node: Node) -> Boolean){
|
@Deprecated("Don't use") fun on(option: String, type: IntType, handler: (player: Player, node: Node) -> Boolean){
|
||||||
InteractionListeners.add(option, type.ordinal, handler)
|
InteractionListeners.add(option, type.ordinal, handler)
|
||||||
}
|
}
|
||||||
fun on(type: IntType, vararg option: String, handler: (player: Player, node: Node) -> Boolean){
|
fun on(type: IntType, vararg option: String, handler: (player: Player, node: Node) -> Boolean){
|
||||||
|
|
@ -82,5 +82,16 @@ interface InteractionListener : ContentInterface{
|
||||||
InteractionListeners.instantClasses.add(name)
|
InteractionListeners.instantClasses.add(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun defineInteraction(type: IntType, ids: IntArray, vararg options: String, persistent: Boolean = false, allowedDistance: Int = 1, handler: (player: Player, node: Node, state: Int) -> Boolean) {
|
||||||
|
InteractionListeners.addMetadata(ids, type, options, InteractionMetadata(handler, allowedDistance, persistent))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun defineInteraction(type: IntType, vararg options: String, persist: Boolean = false, allowedDistance: Int = 1, handler: (player: Player, node: Node, state: Int) -> Boolean) {
|
||||||
|
InteractionListeners.addGenericMetadata(options, type, InteractionMetadata(handler, allowedDistance, persist))
|
||||||
|
}
|
||||||
|
|
||||||
|
data class InteractionMetadata(val handler: (player: Player, node: Node, state: Int) -> Boolean, val distance: Int, val persist: Boolean)
|
||||||
|
data class UseWithMetadata(val handler: (player: Player, used: Node, with: Node, state: Int) -> Boolean, val distance: Int, val persist: Boolean)
|
||||||
|
|
||||||
fun defineListeners()
|
fun defineListeners()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package core.game.interaction
|
package core.game.interaction
|
||||||
|
|
||||||
|
import core.api.forceWalk
|
||||||
|
import core.api.queueScript
|
||||||
import core.game.event.InteractionEvent
|
import core.game.event.InteractionEvent
|
||||||
import core.game.event.UseWithEvent
|
import core.game.event.UseWithEvent
|
||||||
import core.game.node.Node
|
import core.game.node.Node
|
||||||
|
|
@ -14,6 +16,8 @@ object InteractionListeners {
|
||||||
private val useWithWildcardListeners = HashMap<Int, ArrayList<Pair<(Int, Int) -> Boolean, (Player, Node, Node) -> Boolean>>>(10)
|
private val useWithWildcardListeners = HashMap<Int, ArrayList<Pair<(Int, Int) -> Boolean, (Player, Node, Node) -> Boolean>>>(10)
|
||||||
private val destinationOverrides = HashMap<String,(Entity, Node) -> Location>(100)
|
private val destinationOverrides = HashMap<String,(Entity, Node) -> Location>(100)
|
||||||
private val equipListeners = HashMap<String,(Player,Node) -> Boolean>(10)
|
private val equipListeners = HashMap<String,(Player,Node) -> Boolean>(10)
|
||||||
|
private val interactions = HashMap<String, InteractionListener.InteractionMetadata>()
|
||||||
|
private val useWithInteractions = HashMap<String, InteractionListener.UseWithMetadata>()
|
||||||
val instantClasses = HashSet<String>()
|
val instantClasses = HashSet<String>()
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|
@ -232,10 +236,25 @@ object InteractionListeners {
|
||||||
|
|
||||||
if(player.locks.isInteractionLocked) return false
|
if(player.locks.isInteractionLocked) return false
|
||||||
|
|
||||||
val method = get(id,type.ordinal,option) ?: get(option,type.ordinal) ?: return false
|
val method = get(id,type.ordinal,option) ?: get(option,type.ordinal)
|
||||||
|
|
||||||
|
player.setAttribute("interact:option", option.lowercase())
|
||||||
|
player.dispatch(InteractionEvent(node, option.toLowerCase()))
|
||||||
|
|
||||||
|
if (method == null) {
|
||||||
|
val inter = interactions["${type.ordinal}:$id:${option.lowercase()}"] ?: interactions["${type.ordinal}:${option.lowercase()}"] ?: return false
|
||||||
|
val script = Interaction(inter.handler, inter.distance, inter.persist)
|
||||||
|
player.scripts.setInteractionScript(node, script)
|
||||||
|
player.pulseManager.run(object : MovementPulse(player, node, flag) {
|
||||||
|
override fun pulse(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
val destOverride = getOverride(type.ordinal, id, option) ?: getOverride(type.ordinal,node.id) ?: getOverride(type.ordinal,option.toLowerCase())
|
val destOverride = getOverride(type.ordinal, id, option) ?: getOverride(type.ordinal,node.id) ?: getOverride(type.ordinal,option.toLowerCase())
|
||||||
|
|
||||||
player.setAttribute("interact:option", option)
|
|
||||||
|
|
||||||
if(option.toLowerCase() == "attack") //Attack needs special handling >.>
|
if(option.toLowerCase() == "attack") //Attack needs special handling >.>
|
||||||
{
|
{
|
||||||
|
|
@ -250,14 +269,12 @@ object InteractionListeners {
|
||||||
override fun pulse(): Boolean {
|
override fun pulse(): Boolean {
|
||||||
if(player.zoneMonitor.interact(node, Option(option, 0))) return true
|
if(player.zoneMonitor.interact(node, Option(option, 0))) return true
|
||||||
player.faceLocation(node.location)
|
player.faceLocation(node.location)
|
||||||
player.dispatch(InteractionEvent(node, option.toLowerCase()))
|
|
||||||
method.invoke(player,node)
|
method.invoke(player,node)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
method.invoke(player,node)
|
method.invoke(player,node)
|
||||||
player.dispatch(InteractionEvent(node, option.toLowerCase()))
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -285,4 +302,29 @@ object InteractionListeners {
|
||||||
val className = handler.javaClass.name.substringBefore("$")
|
val className = handler.javaClass.name.substringBefore("$")
|
||||||
return instantClasses.contains(className)
|
return instantClasses.contains(className)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addMetadata (ids: IntArray, type: IntType, options: Array<out String>, metadata: InteractionListener.InteractionMetadata) {
|
||||||
|
for (id in ids)
|
||||||
|
for (opt in options)
|
||||||
|
interactions["${type.ordinal}:$id:${opt.lowercase()}"] = metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addMetadata (id: Int, type: IntType, options: Array<out String>, metadata: InteractionListener.InteractionMetadata) {
|
||||||
|
for (opt in options)
|
||||||
|
interactions["${type.ordinal}:$id:${opt.lowercase()}"] = metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addGenericMetadata (options: Array<out String>, type: IntType, metadata: InteractionListener.InteractionMetadata) {
|
||||||
|
for (opt in options)
|
||||||
|
interactions["${type.ordinal}:$opt"] = metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addMetadata (used: Int, with: IntArray, type: IntType, metadata: InteractionListener.UseWithMetadata) {
|
||||||
|
for (id in with)
|
||||||
|
useWithInteractions["${type.ordinal}:$used:$with"] = metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addMetadata (used: Int, with: Int, type: IntType, metadata: InteractionListener.UseWithMetadata) {
|
||||||
|
useWithInteractions["${type.ordinal}:$used:$with"] = metadata
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
Server/src/main/core/game/interaction/Script.kt
Normal file
27
Server/src/main/core/game/interaction/Script.kt
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
package core.game.interaction
|
||||||
|
|
||||||
|
import core.game.node.Node
|
||||||
|
import core.game.node.entity.Entity
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
|
import core.game.world.GameWorld
|
||||||
|
|
||||||
|
typealias UseWithExecutor = (Player, Node, Node, Int) -> Boolean
|
||||||
|
typealias InteractExecutor = (Player, Node, Int) -> Boolean
|
||||||
|
typealias VoidExecutor = (Int) -> Boolean
|
||||||
|
|
||||||
|
enum class QueueStrength {
|
||||||
|
WEAK,
|
||||||
|
NORMAL,
|
||||||
|
STRONG,
|
||||||
|
SOFT
|
||||||
|
}
|
||||||
|
|
||||||
|
open class Script<T> (val execution: T, val persist: Boolean) {
|
||||||
|
var state: Int = 0
|
||||||
|
var nextExecution = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
class Interaction(execution: InteractExecutor, val distance: Int, persist: Boolean) : Script<InteractExecutor>(execution, persist)
|
||||||
|
class UseWithInteraction(execution: UseWithExecutor, val distance: Int, persist: Boolean, val used: Node, val with: Node) : Script<UseWithExecutor>(execution, persist)
|
||||||
|
class QueuedScript(executor: VoidExecutor, val strength: QueueStrength, persist: Boolean) : Script<VoidExecutor>(executor, persist)
|
||||||
|
class QueuedUseWith(executor: UseWithExecutor, val strength: QueueStrength, persist: Boolean, val used: Node, val with: Node) : Script<UseWithExecutor>(executor, persist)
|
||||||
295
Server/src/main/core/game/interaction/ScriptProcessor.kt
Normal file
295
Server/src/main/core/game/interaction/ScriptProcessor.kt
Normal file
|
|
@ -0,0 +1,295 @@
|
||||||
|
package core.game.interaction
|
||||||
|
|
||||||
|
import core.api.*
|
||||||
|
import core.game.node.Node
|
||||||
|
import core.game.node.entity.Entity
|
||||||
|
import core.game.node.entity.npc.NPC
|
||||||
|
import core.game.node.entity.player.Player
|
||||||
|
import core.game.node.item.GroundItem
|
||||||
|
import core.game.node.scenery.Scenery
|
||||||
|
import core.game.world.GameWorld
|
||||||
|
import core.game.world.map.Location
|
||||||
|
import core.game.world.map.path.Pathfinder
|
||||||
|
import core.tools.SystemLogger
|
||||||
|
import java.lang.Integer.max
|
||||||
|
|
||||||
|
class ScriptProcessor(val entity: Entity) {
|
||||||
|
private var apScript: Script<*>? = null
|
||||||
|
private var opScript: Script<*>? = null
|
||||||
|
private var interactTarget: Node? = null
|
||||||
|
private var currentScript: Script<*>? = null
|
||||||
|
private val queue = ArrayList<Script<*>>()
|
||||||
|
|
||||||
|
var delay = 0
|
||||||
|
var interacted = false
|
||||||
|
var apRangeCalled = false
|
||||||
|
var apRange = 10
|
||||||
|
var persistent = false
|
||||||
|
var targetDestination: Location? = null
|
||||||
|
|
||||||
|
fun preMovement() {
|
||||||
|
var allSkipped = false
|
||||||
|
while (!allSkipped) {
|
||||||
|
allSkipped = processQueue()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStunned(entity)) return
|
||||||
|
if (entity.delayed()) return
|
||||||
|
|
||||||
|
var canProcess = !entity.delayed()
|
||||||
|
if (entity is Player)
|
||||||
|
canProcess = canProcess && !entity.interfaceManager.isOpened && !entity.interfaceManager.hasChatbox()
|
||||||
|
|
||||||
|
if (entity !is Player) return
|
||||||
|
if (!entity.delayed() && canProcess && interactTarget != null) {
|
||||||
|
if (opScript != null && inOperableDistance()) {
|
||||||
|
face(entity, interactTarget?.centerLocation ?: return)
|
||||||
|
processInteractScript(opScript ?: return)
|
||||||
|
}
|
||||||
|
else if (apScript != null && inApproachDistance(apScript ?: return)) {
|
||||||
|
face(entity, interactTarget?.centerLocation ?: return)
|
||||||
|
processInteractScript(apScript ?: return)
|
||||||
|
}
|
||||||
|
else if (apScript == null && opScript == null && inOperableDistance()) {
|
||||||
|
sendMessage(entity, "Nothing interesting happens.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun postMovement(didMove: Boolean) {
|
||||||
|
if (didMove)
|
||||||
|
entity.clocks[Clocks.MOVEMENT] = GameWorld.ticks + 1
|
||||||
|
var canProcess = !entity.delayed()
|
||||||
|
if (entity is Player)
|
||||||
|
canProcess = canProcess && !entity.interfaceManager.isOpened && !entity.interfaceManager.hasChatbox()
|
||||||
|
|
||||||
|
if (entity !is Player) return
|
||||||
|
if (!entity.delayed() && canProcess && interactTarget != null && !interacted) {
|
||||||
|
if (opScript != null && inOperableDistance()) {
|
||||||
|
face(entity, interactTarget?.centerLocation ?: return)
|
||||||
|
processInteractScript(opScript ?: return)
|
||||||
|
}
|
||||||
|
else if (apScript != null && inApproachDistance(apScript ?: return)) {
|
||||||
|
face(entity, interactTarget?.centerLocation ?: return)
|
||||||
|
processInteractScript(apScript ?: return)
|
||||||
|
}
|
||||||
|
else if (apScript == null && opScript == null && inOperableDistance()) {
|
||||||
|
sendMessage(entity, "Nothing interesting happens.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (canProcess && (apScript != null || opScript != null)) {
|
||||||
|
if (!interacted && !didMove) {
|
||||||
|
sendMessage(entity, "I can't reach that!")
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (interacted && !apRangeCalled && !persistent) reset()
|
||||||
|
if (interactTarget != null && interactTarget?.isActive != true) reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processQueue() : Boolean {
|
||||||
|
var strongInQueue = false
|
||||||
|
var softInQueue = false
|
||||||
|
var anyExecuted = false
|
||||||
|
for (i in 0 until queue.size) {
|
||||||
|
val script = queue[i]
|
||||||
|
if (script is QueuedScript && script.strength == QueueStrength.STRONG)
|
||||||
|
strongInQueue = true
|
||||||
|
if (script is QueuedUseWith && script.strength == QueueStrength.STRONG)
|
||||||
|
strongInQueue = true
|
||||||
|
if (script is QueuedScript && script.strength == QueueStrength.SOFT)
|
||||||
|
softInQueue = true
|
||||||
|
if (script is QueuedUseWith && script.strength == QueueStrength.SOFT)
|
||||||
|
softInQueue = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (softInQueue) {
|
||||||
|
removeWeakScripts()
|
||||||
|
removeNormalScripts()
|
||||||
|
if (entity is Player) {
|
||||||
|
entity.interfaceManager.close()
|
||||||
|
entity.interfaceManager.closeChatbox()
|
||||||
|
entity.dialogueInterpreter.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strongInQueue) {
|
||||||
|
removeWeakScripts()
|
||||||
|
if (entity is Player) {
|
||||||
|
entity.interfaceManager.close()
|
||||||
|
entity.interfaceManager.closeChatbox()
|
||||||
|
entity.dialogueInterpreter.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val toRemove = ArrayList<Script<*>>()
|
||||||
|
|
||||||
|
for (i in 0 until queue.size) {
|
||||||
|
when (val script = queue[i]) {
|
||||||
|
is QueuedScript -> {
|
||||||
|
if (entity.delayed() && script.strength != QueueStrength.SOFT)
|
||||||
|
continue
|
||||||
|
if (script.nextExecution > GameWorld.ticks)
|
||||||
|
continue
|
||||||
|
if ((script.strength == QueueStrength.STRONG || script.strength == QueueStrength.SOFT) && entity is Player) {
|
||||||
|
entity.interfaceManager.close()
|
||||||
|
entity.interfaceManager.closeChatbox()
|
||||||
|
entity.dialogueInterpreter.close()
|
||||||
|
}
|
||||||
|
script.nextExecution = GameWorld.ticks + 1
|
||||||
|
val finished = executeScript(script)
|
||||||
|
script.state++
|
||||||
|
if (finished && !script.persist)
|
||||||
|
toRemove.add(script)
|
||||||
|
else if (finished)
|
||||||
|
script.state = 0
|
||||||
|
anyExecuted = true
|
||||||
|
}
|
||||||
|
is QueuedUseWith -> {
|
||||||
|
if (entity.delayed() && script.strength != QueueStrength.SOFT)
|
||||||
|
continue
|
||||||
|
if (entity !is Player) {
|
||||||
|
toRemove.add(script)
|
||||||
|
SystemLogger.logErr(this::class.java, "Tried to queue an item UseWith interaction for a non-player!")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (script.nextExecution > GameWorld.ticks)
|
||||||
|
continue
|
||||||
|
if ((script.strength == QueueStrength.STRONG || script.strength == QueueStrength.SOFT)) {
|
||||||
|
entity.interfaceManager.close()
|
||||||
|
entity.interfaceManager.closeChatbox()
|
||||||
|
entity.dialogueInterpreter.close()
|
||||||
|
}
|
||||||
|
script.nextExecution = GameWorld.ticks + 1
|
||||||
|
val finished = executeScript(script)
|
||||||
|
script.state++
|
||||||
|
if (finished && !script.persist)
|
||||||
|
toRemove.add(script)
|
||||||
|
else if (finished)
|
||||||
|
script.state = 0
|
||||||
|
anyExecuted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.removeAll(toRemove.toSet())
|
||||||
|
return !anyExecuted
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isPersist (script: Script<*>) : Boolean {
|
||||||
|
return script.persist
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processInteractScript(script: Script<*>) {
|
||||||
|
if (script.nextExecution < GameWorld.ticks) {
|
||||||
|
val finished = executeScript(script)
|
||||||
|
script.state++
|
||||||
|
if (finished && isPersist(script))
|
||||||
|
script.state = 0
|
||||||
|
interacted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun executeScript(script: Script<*>) : Boolean {
|
||||||
|
currentScript = script
|
||||||
|
when (script) {
|
||||||
|
is Interaction -> return script.execution.invoke(entity as? Player ?: return true, interactTarget ?: return true, script.state)
|
||||||
|
is UseWithInteraction -> return script.execution.invoke(entity as? Player ?: return true, script.used, script.with, script.state)
|
||||||
|
is QueuedScript -> return script.execution.invoke(script.state)
|
||||||
|
is QueuedUseWith -> return script.execution.invoke(entity as? Player ?: return true, script.used, script.with, script.state)
|
||||||
|
}
|
||||||
|
currentScript = null
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeWeakScripts() {
|
||||||
|
queue.removeAll(queue.filter { it is QueuedScript && it.strength == QueueStrength.WEAK || it is QueuedUseWith && it.strength == QueueStrength.WEAK }.toSet())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeNormalScripts() {
|
||||||
|
queue.removeAll(queue.filter { it is QueuedScript && it.strength == QueueStrength.NORMAL || it is QueuedUseWith && it.strength == QueueStrength.NORMAL }.toSet())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun inApproachDistance(script: Script<*>) : Boolean {
|
||||||
|
val distance = when (script) {
|
||||||
|
is Interaction -> script.distance
|
||||||
|
is UseWithInteraction -> script.distance
|
||||||
|
else -> 10
|
||||||
|
}
|
||||||
|
targetDestination?.let {
|
||||||
|
return it.location.getDistance(entity.location) <= distance && hasLineOfSight(entity, it)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun inOperableDistance() : Boolean {
|
||||||
|
targetDestination?.let {
|
||||||
|
return it.cardinalTiles.any {loc -> loc == entity.location} && hasLineOfSight(entity, it)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
apScript = null
|
||||||
|
opScript = null
|
||||||
|
apRangeCalled = false
|
||||||
|
interacted = false
|
||||||
|
apRange = 10
|
||||||
|
interactTarget = null
|
||||||
|
persistent = false
|
||||||
|
targetDestination = null
|
||||||
|
resetAnimator(entity as? Player ?: return)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setInteractionScript(target: Node, script: Script<*>?) {
|
||||||
|
reset()
|
||||||
|
interactTarget = target
|
||||||
|
if (script != null) {
|
||||||
|
apRange = when(script) {
|
||||||
|
is Interaction -> script.distance
|
||||||
|
is UseWithInteraction -> script.distance
|
||||||
|
else -> 10
|
||||||
|
}
|
||||||
|
persistent = script.persist
|
||||||
|
if (apRange == -1)
|
||||||
|
opScript = script
|
||||||
|
else
|
||||||
|
apScript = script
|
||||||
|
targetDestination = when (interactTarget) {
|
||||||
|
is NPC -> DestinationFlag.ENTITY.getDestination(entity, interactTarget)
|
||||||
|
is Scenery -> {
|
||||||
|
val path = Pathfinder.find(entity, interactTarget).points.lastOrNull()
|
||||||
|
if (path == null) {
|
||||||
|
clearScripts(entity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Location.create(path.x, path.y, entity.location.z)
|
||||||
|
}
|
||||||
|
is GroundItem -> DestinationFlag.ITEM.getDestination(entity, interactTarget)
|
||||||
|
else -> target.location
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addToQueue(script: Script<*>, strength: QueueStrength) {
|
||||||
|
if (script !is QueuedScript && script !is QueuedUseWith) {
|
||||||
|
SystemLogger.logErr(this::class.java, "Tried to queue ${script::class.java.simpleName} as a queueable script but it's not!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (strength == QueueStrength.STRONG && entity is Player) {
|
||||||
|
entity.interfaceManager.close()
|
||||||
|
entity.interfaceManager.closeChatbox()
|
||||||
|
entity.dialogueInterpreter.close()
|
||||||
|
}
|
||||||
|
script.nextExecution = max(GameWorld.ticks + 1, script.nextExecution)
|
||||||
|
queue.add(script)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getActiveScript() : Script<*>? {
|
||||||
|
return currentScript ?: getActiveInteraction()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getActiveInteraction() : Script<*>? {
|
||||||
|
return opScript ?: apScript
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package core.game.node;
|
package core.game.node;
|
||||||
|
|
||||||
import core.game.interaction.DestinationFlag;
|
import core.game.interaction.DestinationFlag;
|
||||||
import core.game.interaction.Interaction;
|
import core.game.interaction.InteractPlugin;
|
||||||
import core.game.node.entity.npc.NPC;
|
import core.game.node.entity.npc.NPC;
|
||||||
import core.game.node.entity.player.Player;
|
import core.game.node.entity.player.Player;
|
||||||
import core.game.node.item.Item;
|
import core.game.node.item.Item;
|
||||||
|
|
@ -49,7 +49,7 @@ public abstract class Node {
|
||||||
/**
|
/**
|
||||||
* The interaction instance.
|
* The interaction instance.
|
||||||
*/
|
*/
|
||||||
protected Interaction interaction;
|
protected InteractPlugin interactPlugin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The destination flag.
|
* The destination flag.
|
||||||
|
|
@ -220,19 +220,19 @@ public abstract class Node {
|
||||||
* Gets the interaction.
|
* Gets the interaction.
|
||||||
* @return The interaction.
|
* @return The interaction.
|
||||||
*/
|
*/
|
||||||
public Interaction getInteraction() {
|
public InteractPlugin getInteraction() {
|
||||||
if (interaction != null && !interaction.isInitialized()) {
|
if (interactPlugin != null && !interactPlugin.isInitialized()) {
|
||||||
interaction.setDefault();
|
interactPlugin.setDefault();
|
||||||
}
|
}
|
||||||
return interaction;
|
return interactPlugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the interaction.
|
* Sets the interaction.
|
||||||
* @param interaction The interaction to set.
|
* @param interactPlugin The interaction to set.
|
||||||
*/
|
*/
|
||||||
public void setInteraction(Interaction interaction) {
|
public void setInteraction(InteractPlugin interactPlugin) {
|
||||||
this.interaction = interaction;
|
this.interactPlugin = interactPlugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package core.game.node.entity;
|
package core.game.node.entity;
|
||||||
|
|
||||||
import core.game.event.*;
|
import core.game.event.*;
|
||||||
import core.game.interaction.DestinationFlag;
|
import core.game.interaction.*;
|
||||||
import core.game.node.Node;
|
import core.game.node.Node;
|
||||||
import core.game.node.entity.combat.BattleState;
|
import core.game.node.entity.combat.BattleState;
|
||||||
import core.game.node.entity.combat.CombatStyle;
|
import core.game.node.entity.combat.CombatStyle;
|
||||||
|
|
@ -9,6 +9,7 @@ import core.game.node.entity.combat.DeathTask;
|
||||||
import core.game.node.entity.combat.ImpactHandler;
|
import core.game.node.entity.combat.ImpactHandler;
|
||||||
import core.game.node.entity.combat.equipment.ArmourSet;
|
import core.game.node.entity.combat.equipment.ArmourSet;
|
||||||
import core.game.node.entity.impl.*;
|
import core.game.node.entity.impl.*;
|
||||||
|
import core.game.node.entity.impl.Properties;
|
||||||
import core.game.node.entity.lock.ActionLocks;
|
import core.game.node.entity.lock.ActionLocks;
|
||||||
import core.game.node.entity.npc.NPC;
|
import core.game.node.entity.npc.NPC;
|
||||||
import core.game.node.entity.player.Player;
|
import core.game.node.entity.player.Player;
|
||||||
|
|
@ -29,10 +30,9 @@ import core.game.world.update.flag.context.Graphics;
|
||||||
import core.game.node.entity.combat.CombatSwingHandler;
|
import core.game.node.entity.combat.CombatSwingHandler;
|
||||||
import core.game.world.update.UpdateMasks;
|
import core.game.world.update.UpdateMasks;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import static core.api.ContentAPIKt.isStunned;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An entity is a movable node, such as players and NPCs.
|
* An entity is a movable node, such as players and NPCs.
|
||||||
|
|
@ -109,6 +109,8 @@ public abstract class Entity extends Node {
|
||||||
* The reward locks.
|
* The reward locks.
|
||||||
*/
|
*/
|
||||||
private final ActionLocks locks = new ActionLocks();
|
private final ActionLocks locks = new ActionLocks();
|
||||||
|
public final ScriptProcessor scripts = new ScriptProcessor(this);
|
||||||
|
public final int[] clocks = new int[10];
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -122,6 +124,8 @@ public abstract class Entity extends Node {
|
||||||
*/
|
*/
|
||||||
private boolean invisible;
|
private boolean invisible;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@code Entity} {@code Object}.
|
* Constructs a new {@code Entity} {@code Object}.
|
||||||
* @param name The name of the entity.
|
* @param name The name of the entity.
|
||||||
|
|
@ -209,9 +213,12 @@ public abstract class Entity extends Node {
|
||||||
* This methods gets called before the {@link #update()} method.
|
* This methods gets called before the {@link #update()} method.
|
||||||
*/
|
*/
|
||||||
public void tick() {
|
public void tick() {
|
||||||
|
scripts.preMovement();
|
||||||
dispatch(new TickEvent(GameWorld.getTicks()));
|
dispatch(new TickEvent(GameWorld.getTicks()));
|
||||||
skills.pulse();
|
skills.pulse();
|
||||||
|
Location old = location != null ? location.transform(0, 0, 0) : Location.create(0,0,0);
|
||||||
walkingQueue.update();
|
walkingQueue.update();
|
||||||
|
scripts.postMovement(!Objects.equals(location, old));
|
||||||
updateMasks.prepare(this);
|
updateMasks.prepare(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -953,4 +960,8 @@ public abstract class Entity extends Node {
|
||||||
}
|
}
|
||||||
return occupied;
|
return occupied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean delayed() {
|
||||||
|
return scripts.getDelay() > GameWorld.getTicks();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package core.game.node.entity.impl;
|
package core.game.node.entity.impl;
|
||||||
|
|
||||||
|
import core.game.interaction.Clocks;
|
||||||
import core.game.node.entity.Entity;
|
import core.game.node.entity.Entity;
|
||||||
import core.game.node.entity.npc.NPC;
|
import core.game.node.entity.npc.NPC;
|
||||||
import core.game.world.GameWorld;
|
import core.game.world.GameWorld;
|
||||||
|
|
@ -121,7 +122,12 @@ public final class Animator {
|
||||||
animation.setId(-1);
|
animation.setId(-1);
|
||||||
}
|
}
|
||||||
this.animation = animation;
|
this.animation = animation;
|
||||||
|
if (animation.getId() != -1) {
|
||||||
ticks = GameWorld.getTicks() + animation.getDuration();
|
ticks = GameWorld.getTicks() + animation.getDuration();
|
||||||
|
} else {
|
||||||
|
ticks = 0;
|
||||||
|
}
|
||||||
|
entity.clocks[Clocks.getANIMATION_END()] = ticks;
|
||||||
entity.getUpdateMasks().register(entity instanceof NPC ? new NPCAnimation(animation) : new AnimationFlag(animation));
|
entity.getUpdateMasks().register(entity instanceof NPC ? new NPCAnimation(animation) : new AnimationFlag(animation));
|
||||||
priority = animation.getPriority();
|
priority = animation.getPriority();
|
||||||
}
|
}
|
||||||
|
|
@ -151,6 +157,8 @@ public final class Animator {
|
||||||
*/
|
*/
|
||||||
public void reset() {
|
public void reset() {
|
||||||
animate(RESET_A);
|
animate(RESET_A);
|
||||||
|
entity.clocks[Clocks.getANIMATION_END()] = 0;
|
||||||
|
ticks = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -158,7 +166,7 @@ public final class Animator {
|
||||||
* @return {@code True} if so.
|
* @return {@code True} if so.
|
||||||
*/
|
*/
|
||||||
public boolean isAnimating() {
|
public boolean isAnimating() {
|
||||||
return animation != null && ticks > GameWorld.getTicks();
|
return animation != null && animation.getId() != -1 && ticks > GameWorld.getTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package core.game.node.entity.npc;
|
||||||
import core.game.event.NPCKillEvent;
|
import core.game.event.NPCKillEvent;
|
||||||
import core.cache.def.impl.NPCDefinition;
|
import core.cache.def.impl.NPCDefinition;
|
||||||
import core.game.dialogue.DialoguePlugin;
|
import core.game.dialogue.DialoguePlugin;
|
||||||
import core.game.interaction.Interaction;
|
import core.game.interaction.InteractPlugin;
|
||||||
import core.game.interaction.MovementPulse;
|
import core.game.interaction.MovementPulse;
|
||||||
import core.game.node.entity.Entity;
|
import core.game.node.entity.Entity;
|
||||||
import core.game.node.entity.combat.BattleState;
|
import core.game.node.entity.combat.BattleState;
|
||||||
|
|
@ -162,7 +162,7 @@ public class NPC extends Entity {
|
||||||
this.definition = NPCDefinition.forId(id);
|
this.definition = NPCDefinition.forId(id);
|
||||||
super.size = definition.size;
|
super.size = definition.size;
|
||||||
super.direction = direction;
|
super.direction = direction;
|
||||||
super.interaction = new Interaction(this);
|
super.interactPlugin = new InteractPlugin(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -213,14 +213,14 @@ public class NPC extends Entity {
|
||||||
if (getViewport().getRegion().isActive()) {
|
if (getViewport().getRegion().isActive()) {
|
||||||
Repository.addRenderableNPC(this);
|
Repository.addRenderableNPC(this);
|
||||||
}
|
}
|
||||||
interaction.setDefault();
|
interactPlugin.setDefault();
|
||||||
configure();
|
configure();
|
||||||
setDefaultBehavior();
|
setDefaultBehavior();
|
||||||
if (definition.childNPCIds != null) {
|
if (definition.childNPCIds != null) {
|
||||||
children = new NPC[definition.childNPCIds.length];
|
children = new NPC[definition.childNPCIds.length];
|
||||||
for (int i = 0; i < children.length; i++) {
|
for (int i = 0; i < children.length; i++) {
|
||||||
NPC npc = children[i] = new NPC(definition.childNPCIds[i]);
|
NPC npc = children[i] = new NPC(definition.childNPCIds[i]);
|
||||||
npc.interaction.setDefault();
|
npc.interactPlugin.setDefault();
|
||||||
npc.index = index;
|
npc.index = index;
|
||||||
npc.size = size;
|
npc.size = size;
|
||||||
}
|
}
|
||||||
|
|
@ -671,10 +671,10 @@ public class NPC extends Entity {
|
||||||
this.definition = NPCDefinition.forId(id);
|
this.definition = NPCDefinition.forId(id);
|
||||||
super.name = definition.getName();
|
super.name = definition.getName();
|
||||||
super.size = definition.size;
|
super.size = definition.size;
|
||||||
super.interaction = new Interaction(this);
|
super.interactPlugin = new InteractPlugin(this);
|
||||||
initConfig();
|
initConfig();
|
||||||
configure();
|
configure();
|
||||||
interaction.setDefault();
|
interactPlugin.setDefault();
|
||||||
if (id == originalId) {
|
if (id == originalId) {
|
||||||
getUpdateMasks().unregisterSynced(NPCSwitchId.getOrdinal());
|
getUpdateMasks().unregisterSynced(NPCSwitchId.getOrdinal());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import core.game.container.impl.BankContainer;
|
||||||
import core.game.container.impl.EquipmentContainer;
|
import core.game.container.impl.EquipmentContainer;
|
||||||
import core.game.container.impl.InventoryListener;
|
import core.game.container.impl.InventoryListener;
|
||||||
import core.game.dialogue.DialogueInterpreter;
|
import core.game.dialogue.DialogueInterpreter;
|
||||||
import core.game.interaction.Interaction;
|
import core.game.interaction.InteractPlugin;
|
||||||
import core.game.node.entity.Entity;
|
import core.game.node.entity.Entity;
|
||||||
import core.game.node.entity.combat.BattleState;
|
import core.game.node.entity.combat.BattleState;
|
||||||
import core.game.node.entity.combat.CombatStyle;
|
import core.game.node.entity.combat.CombatStyle;
|
||||||
|
|
@ -19,6 +19,7 @@ import content.global.handlers.item.equipment.special.ChinchompaSwingHandler;
|
||||||
import core.game.node.entity.npc.NPC;
|
import core.game.node.entity.npc.NPC;
|
||||||
import core.game.node.entity.player.info.*;
|
import core.game.node.entity.player.info.*;
|
||||||
import core.game.node.entity.player.info.login.LoginConfiguration;
|
import core.game.node.entity.player.info.login.LoginConfiguration;
|
||||||
|
import core.game.node.entity.player.info.login.PlayerParser;
|
||||||
import core.game.node.entity.player.link.*;
|
import core.game.node.entity.player.link.*;
|
||||||
import core.game.node.entity.player.link.appearance.Appearance;
|
import core.game.node.entity.player.link.appearance.Appearance;
|
||||||
import core.game.node.entity.player.link.audio.AudioManager;
|
import core.game.node.entity.player.link.audio.AudioManager;
|
||||||
|
|
@ -324,7 +325,7 @@ public class Player extends Entity {
|
||||||
public Player(PlayerDetails details) {
|
public Player(PlayerDetails details) {
|
||||||
super(details.getUsername(), ServerConstants.START_LOCATION);
|
super(details.getUsername(), ServerConstants.START_LOCATION);
|
||||||
super.active = false;
|
super.active = false;
|
||||||
super.interaction = new Interaction(this);
|
super.interactPlugin = new InteractPlugin(this);
|
||||||
this.details = details;
|
this.details = details;
|
||||||
this.direction = Direction.SOUTH;
|
this.direction = Direction.SOUTH;
|
||||||
}
|
}
|
||||||
|
|
@ -523,6 +524,10 @@ public class Player extends Entity {
|
||||||
PacketRepository.send(SkillLevel.class, new SkillContext(this, Skills.HITPOINTS));
|
PacketRepository.send(SkillLevel.class, new SkillContext(this, Skills.HITPOINTS));
|
||||||
getSkills().setLifepointsUpdate(false);
|
getSkills().setLifepointsUpdate(false);
|
||||||
}
|
}
|
||||||
|
if (getAttribute("flagged-for-save", false)) {
|
||||||
|
PlayerParser.saveImmediately(this);
|
||||||
|
removeAttribute("flagged-for-save");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,10 @@ public final class PlayerParser {
|
||||||
* @param player The player.
|
* @param player The player.
|
||||||
*/
|
*/
|
||||||
public static void save(Player player) {
|
public static void save(Player player) {
|
||||||
|
player.setAttribute("flagged-for-save", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void saveImmediately(Player player) {
|
||||||
new PlayerSaver(player).save();
|
new PlayerSaver(player).save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ public class GroundItem extends Item {
|
||||||
super(item.getId(), item.getAmount(), item.getCharge());
|
super(item.getId(), item.getAmount(), item.getCharge());
|
||||||
super.location = location;
|
super.location = location;
|
||||||
super.index = -1;
|
super.index = -1;
|
||||||
super.interaction.setDefault();
|
super.interactPlugin.setDefault();
|
||||||
this.dropper = player;
|
this.dropper = player;
|
||||||
this.dropperUid = player != null ? player.getDetails().getUid() : -1;
|
this.dropperUid = player != null ? player.getDetails().getUid() : -1;
|
||||||
this.ticks = GameWorld.getTicks();
|
this.ticks = GameWorld.getTicks();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package core.game.node.item;
|
||||||
|
|
||||||
import core.cache.def.impl.ItemDefinition;
|
import core.cache.def.impl.ItemDefinition;
|
||||||
import core.game.interaction.DestinationFlag;
|
import core.game.interaction.DestinationFlag;
|
||||||
import core.game.interaction.Interaction;
|
import core.game.interaction.InteractPlugin;
|
||||||
import core.game.interaction.OptionHandler;
|
import core.game.interaction.OptionHandler;
|
||||||
import core.game.node.Node;
|
import core.game.node.Node;
|
||||||
import core.game.node.entity.combat.equipment.DegradableEquipment;
|
import core.game.node.entity.combat.equipment.DegradableEquipment;
|
||||||
|
|
@ -34,7 +34,7 @@ public class Item extends Node{
|
||||||
*/
|
*/
|
||||||
public Item() {
|
public Item() {
|
||||||
super("null", null);
|
super("null", null);
|
||||||
super.interaction = new Interaction(this);
|
super.interactPlugin = new InteractPlugin(this);
|
||||||
this.idHash = -1 << 16 | 1000;
|
this.idHash = -1 << 16 | 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,7 +65,7 @@ public class Item extends Node{
|
||||||
super(ItemDefinition.forId(id).getName(), null);
|
super(ItemDefinition.forId(id).getName(), null);
|
||||||
super.destinationFlag = DestinationFlag.ITEM;
|
super.destinationFlag = DestinationFlag.ITEM;
|
||||||
super.index = -1; // Item slot.
|
super.index = -1; // Item slot.
|
||||||
super.interaction = new Interaction(this);
|
super.interactPlugin = new InteractPlugin(this);
|
||||||
this.idHash = id << 16 | charge;
|
this.idHash = id << 16 | charge;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
this.definition = ItemDefinition.forId(id);
|
this.definition = ItemDefinition.forId(id);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package core.game.node.scenery;
|
||||||
import core.cache.def.impl.VarbitDefinition;
|
import core.cache.def.impl.VarbitDefinition;
|
||||||
import core.cache.def.impl.SceneryDefinition;
|
import core.cache.def.impl.SceneryDefinition;
|
||||||
import core.game.interaction.DestinationFlag;
|
import core.game.interaction.DestinationFlag;
|
||||||
import core.game.interaction.Interaction;
|
import core.game.interaction.InteractPlugin;
|
||||||
import core.game.node.Node;
|
import core.game.node.Node;
|
||||||
import core.game.node.entity.impl.GameAttributes;
|
import core.game.node.entity.impl.GameAttributes;
|
||||||
import core.game.node.entity.player.Player;
|
import core.game.node.entity.player.Player;
|
||||||
|
|
@ -145,7 +145,7 @@ public class Scenery extends Node {
|
||||||
}
|
}
|
||||||
super.destinationFlag = DestinationFlag.OBJECT;
|
super.destinationFlag = DestinationFlag.OBJECT;
|
||||||
super.direction = Direction.get(rotation);
|
super.direction = Direction.get(rotation);
|
||||||
super.interaction = new Interaction(this);
|
super.interactPlugin = new InteractPlugin(this);
|
||||||
this.rotation = rotation;
|
this.rotation = rotation;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package core.game.system.command.sets
|
||||||
|
|
||||||
import content.global.activity.jobs.JobManager
|
import content.global.activity.jobs.JobManager
|
||||||
import content.global.skill.slayer.Master
|
import content.global.skill.slayer.Master
|
||||||
|
import core.api.removeAttribute
|
||||||
|
import core.api.getItemName
|
||||||
import core.api.sendMessage
|
import core.api.sendMessage
|
||||||
import core.cache.Cache
|
import core.cache.Cache
|
||||||
import core.cache.def.impl.DataMap
|
import core.cache.def.impl.DataMap
|
||||||
|
|
@ -197,5 +199,14 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define("itemsearch") {player, args ->
|
||||||
|
val itemName = args.copyOfRange(1, args.size).joinToString(" ").lowercase()
|
||||||
|
for (i in 0 until 15000) {
|
||||||
|
val name = getItemName(i).lowercase()
|
||||||
|
if (name.contains(itemName) || itemName.contains(name))
|
||||||
|
notify(player, "$i: $name")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -289,6 +289,16 @@ public final class Location extends Node {
|
||||||
return locs;
|
return locs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ArrayList<Location> getCardinalTiles() {
|
||||||
|
ArrayList<Location> locs = new ArrayList<>();
|
||||||
|
|
||||||
|
locs.add(transform(0, 1, 0));
|
||||||
|
locs.add(transform(0, -1, 0));
|
||||||
|
locs.add(transform(-1, 0, 0));
|
||||||
|
locs.add(transform(1, 0, 0));
|
||||||
|
return locs;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a square of 3 x 3 tiles as an ArrayList<Location>
|
* Gets a square of 3 x 3 tiles as an ArrayList<Location>
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -191,8 +191,7 @@ class DisconnectionQueue {
|
||||||
*/
|
*/
|
||||||
fun save(player: Player, sql: Boolean): Boolean {
|
fun save(player: Player, sql: Boolean): Boolean {
|
||||||
try {
|
try {
|
||||||
PlayerParser.save(player)
|
PlayerParser.saveImmediately(player)
|
||||||
return true
|
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
t.printStackTrace()
|
t.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,6 @@ import core.cache.def.impl.NPCDefinition
|
||||||
import core.cache.def.impl.SceneryDefinition
|
import core.cache.def.impl.SceneryDefinition
|
||||||
import core.game.container.Container
|
import core.game.container.Container
|
||||||
import core.game.container.impl.BankContainer
|
import core.game.container.impl.BankContainer
|
||||||
import core.game.interaction.PluginInteractionManager
|
|
||||||
import core.game.interaction.Interaction
|
|
||||||
import core.game.interaction.MovementPulse
|
|
||||||
import core.game.interaction.NodeUsageEvent
|
|
||||||
import core.game.interaction.Option
|
|
||||||
import core.game.interaction.UseWithHandler
|
|
||||||
import core.game.node.Node
|
import core.game.node.Node
|
||||||
import core.game.node.entity.player.Player
|
import core.game.node.entity.player.Player
|
||||||
import core.game.node.entity.player.info.Rights
|
import core.game.node.entity.player.info.Rights
|
||||||
|
|
@ -44,13 +38,11 @@ import core.game.ge.GrandExchange.Companion.getOfferStats
|
||||||
import core.game.ge.GrandExchange.Companion.getRecommendedPrice
|
import core.game.ge.GrandExchange.Companion.getRecommendedPrice
|
||||||
import core.game.ge.GrandExchangeOffer
|
import core.game.ge.GrandExchangeOffer
|
||||||
import core.game.ge.PriceIndex
|
import core.game.ge.PriceIndex
|
||||||
import core.game.interaction.IntType
|
|
||||||
import core.game.interaction.InteractionListeners
|
|
||||||
import core.game.interaction.InterfaceListeners
|
|
||||||
import content.global.handlers.iface.ge.StockMarket
|
import content.global.handlers.iface.ge.StockMarket
|
||||||
import content.global.skill.magic.SpellListener
|
import content.global.skill.magic.SpellListener
|
||||||
import content.global.skill.magic.SpellListeners
|
import content.global.skill.magic.SpellListeners
|
||||||
import content.global.skill.magic.SpellUtils
|
import content.global.skill.magic.SpellUtils
|
||||||
|
import core.game.interaction.*
|
||||||
import core.game.node.entity.player.info.LogType
|
import core.game.node.entity.player.info.LogType
|
||||||
import core.game.node.entity.player.info.PlayerMonitor
|
import core.game.node.entity.player.info.PlayerMonitor
|
||||||
import core.tools.SystemLogger
|
import core.tools.SystemLogger
|
||||||
|
|
@ -78,6 +70,10 @@ object PacketProcessor {
|
||||||
var pkt: Packet
|
var pkt: Packet
|
||||||
while (countThisCycle-- > 0) {
|
while (countThisCycle-- > 0) {
|
||||||
pkt = queue.tryPop(Packet.NoProcess())
|
pkt = queue.tryPop(Packet.NoProcess())
|
||||||
|
if (pkt is Packet.NoProcess) {
|
||||||
|
queue.clear()
|
||||||
|
return
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
process(pkt)
|
process(pkt)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
@ -457,6 +453,7 @@ object PacketProcessor {
|
||||||
|
|
||||||
player.face(null)
|
player.face(null)
|
||||||
player.faceLocation(null)
|
player.faceLocation(null)
|
||||||
|
player.scripts.reset()
|
||||||
|
|
||||||
player.pulseManager.run(object : MovementPulse(player, Location.create(x,y,player.location.z), isRunning) {
|
player.pulseManager.run(object : MovementPulse(player, Location.create(x,y,player.location.z), isRunning) {
|
||||||
override fun pulse(): Boolean {
|
override fun pulse(): Boolean {
|
||||||
|
|
@ -569,6 +566,7 @@ object PacketProcessor {
|
||||||
if (node.id != nodeId)
|
if (node.id != nodeId)
|
||||||
return sendClearMinimap(player)
|
return sendClearMinimap(player)
|
||||||
|
|
||||||
|
player.scripts.reset()
|
||||||
if (player.zoneMonitor.useWith(item, node))
|
if (player.zoneMonitor.useWith(item, node))
|
||||||
return
|
return
|
||||||
if (InteractionListeners.run(item, node, type, player))
|
if (InteractionListeners.run(item, node, type, player))
|
||||||
|
|
@ -590,13 +588,14 @@ object PacketProcessor {
|
||||||
private fun processGroundItemAction(pkt: Packet.GroundItemAction) {
|
private fun processGroundItemAction(pkt: Packet.GroundItemAction) {
|
||||||
val item = GroundItemManager.get(pkt.id, Location.create(pkt.x, pkt.y, pkt.player.location.z), pkt.player)
|
val item = GroundItemManager.get(pkt.id, Location.create(pkt.x, pkt.y, pkt.player.location.z), pkt.player)
|
||||||
val player = pkt.player
|
val player = pkt.player
|
||||||
|
player.scripts.reset()
|
||||||
|
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
return sendClearMinimap(player)
|
return sendClearMinimap(player)
|
||||||
}
|
}
|
||||||
val option = item.interaction[pkt.optIndex]
|
val option = item.interaction[pkt.optIndex]
|
||||||
if (option == null) {
|
if (option == null) {
|
||||||
Interaction.handleInvalidInteraction(player, item, Option.NULL)
|
InteractPlugin.handleInvalidInteraction(player, item, Option.NULL)
|
||||||
return sendClearMinimap(player)
|
return sendClearMinimap(player)
|
||||||
}
|
}
|
||||||
if (PluginInteractionManager.handle(player, item, option))
|
if (PluginInteractionManager.handle(player, item, option))
|
||||||
|
|
@ -613,6 +612,7 @@ object PacketProcessor {
|
||||||
if (pkt.otherIndex !in 1 until ServerConstants.MAX_PLAYERS) {
|
if (pkt.otherIndex !in 1 until ServerConstants.MAX_PLAYERS) {
|
||||||
return sendClearMinimap(player)
|
return sendClearMinimap(player)
|
||||||
}
|
}
|
||||||
|
player.scripts.reset()
|
||||||
val other = Repository.players[pkt.otherIndex]
|
val other = Repository.players[pkt.otherIndex]
|
||||||
if (other == null || !other.isActive)
|
if (other == null || !other.isActive)
|
||||||
return sendClearMinimap(player)
|
return sendClearMinimap(player)
|
||||||
|
|
@ -642,7 +642,7 @@ object PacketProcessor {
|
||||||
|
|
||||||
if (scenery == null || scenery.id != objId || !scenery.isActive) {
|
if (scenery == null || scenery.id != objId || !scenery.isActive) {
|
||||||
player.debug("[SCENERY INTERACT] NULL OR MISMATCH OR INACTIVE")
|
player.debug("[SCENERY INTERACT] NULL OR MISMATCH OR INACTIVE")
|
||||||
Interaction.handleInvalidInteraction(player, scenery, Option.NULL)
|
InteractPlugin.handleInvalidInteraction(player, scenery, Option.NULL)
|
||||||
return sendClearMinimap(player)
|
return sendClearMinimap(player)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -651,7 +651,7 @@ object PacketProcessor {
|
||||||
|
|
||||||
if (option == null) {
|
if (option == null) {
|
||||||
player.debug("[SCENERY INTERACT] NULL OPTION")
|
player.debug("[SCENERY INTERACT] NULL OPTION")
|
||||||
Interaction.handleInvalidInteraction(player, scenery, Option.NULL)
|
InteractPlugin.handleInvalidInteraction(player, scenery, Option.NULL)
|
||||||
return sendClearMinimap(player)
|
return sendClearMinimap(player)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -665,6 +665,7 @@ object PacketProcessor {
|
||||||
}
|
}
|
||||||
player.debug("------------------------------------------------")
|
player.debug("------------------------------------------------")
|
||||||
|
|
||||||
|
player.scripts.reset()
|
||||||
if (InteractionListeners.run(wrapperChild.id, IntType.SCENERY, option.name, player, wrapperChild))
|
if (InteractionListeners.run(wrapperChild.id, IntType.SCENERY, option.name, player, wrapperChild))
|
||||||
return
|
return
|
||||||
if (PluginInteractionManager.handle(player, wrapperChild))
|
if (PluginInteractionManager.handle(player, wrapperChild))
|
||||||
|
|
@ -681,7 +682,7 @@ object PacketProcessor {
|
||||||
val option = wrapperChild.interaction[pkt.optIndex]
|
val option = wrapperChild.interaction[pkt.optIndex]
|
||||||
|
|
||||||
if (option == null) {
|
if (option == null) {
|
||||||
Interaction.handleInvalidInteraction(pkt.player, npc, Option.NULL)
|
InteractPlugin.handleInvalidInteraction(pkt.player, npc, Option.NULL)
|
||||||
return sendClearMinimap(pkt.player)
|
return sendClearMinimap(pkt.player)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -696,6 +697,7 @@ object PacketProcessor {
|
||||||
}
|
}
|
||||||
pkt.player.debug("---------------------------------")
|
pkt.player.debug("---------------------------------")
|
||||||
|
|
||||||
|
pkt.player.scripts.reset()
|
||||||
if (InteractionListeners.run(wrapperChild.id, IntType.NPC,option.name,pkt.player,npc))
|
if (InteractionListeners.run(wrapperChild.id, IntType.NPC,option.name,pkt.player,npc))
|
||||||
return
|
return
|
||||||
if (PluginInteractionManager.handle(pkt.player, wrapperChild, option))
|
if (PluginInteractionManager.handle(pkt.player, wrapperChild, option))
|
||||||
|
|
@ -714,6 +716,7 @@ object PacketProcessor {
|
||||||
if (pkt.player.locks.isInteractionLocked)
|
if (pkt.player.locks.isInteractionLocked)
|
||||||
return
|
return
|
||||||
item.interaction.handleItemOption(pkt.player, option, container)
|
item.interaction.handleItemOption(pkt.player, option, container)
|
||||||
|
pkt.player.scripts.reset()
|
||||||
pkt.player.debug("[ITEM INTERACT] ID: ${item.id}, Slot: ${pkt.slot}, Opt: ${option.name}")
|
pkt.player.debug("[ITEM INTERACT] ID: ${item.id}, Slot: ${pkt.slot}, Opt: ${option.name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package content
|
||||||
|
|
||||||
import TestUtils
|
import TestUtils
|
||||||
import core.api.asItem
|
import core.api.asItem
|
||||||
|
import core.api.setAttribute
|
||||||
|
import core.game.global.action.DropListener
|
||||||
import core.game.node.entity.player.info.Rights
|
import core.game.node.entity.player.info.Rights
|
||||||
import core.game.node.entity.player.link.IronmanMode
|
import core.game.node.entity.player.link.IronmanMode
|
||||||
import core.game.world.map.Location
|
import core.game.world.map.Location
|
||||||
|
|
@ -301,7 +303,8 @@ class DeathTests {
|
||||||
Assertions.assertNotNull(g)
|
Assertions.assertNotNull(g)
|
||||||
Assertions.assertEquals(p.location, g?.location)
|
Assertions.assertEquals(p.location, g?.location)
|
||||||
|
|
||||||
val canDrop = core.game.global.action.DropItemHandler.drop(p, p.inventory[0])
|
setAttribute(p, "interact:option", "drop")
|
||||||
|
val canDrop = DropListener.drop(p, p.inventory[0])
|
||||||
Assertions.assertEquals(false, canDrop)
|
Assertions.assertEquals(false, canDrop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package core
|
||||||
|
|
||||||
import TestUtils
|
import TestUtils
|
||||||
import content.global.skill.gather.GatheringSkillOptionListeners
|
import content.global.skill.gather.GatheringSkillOptionListeners
|
||||||
|
import content.global.skill.gather.woodcutting.WoodcuttingListener
|
||||||
import core.game.node.scenery.Scenery
|
import core.game.node.scenery.Scenery
|
||||||
import core.game.world.map.Location
|
import core.game.world.map.Location
|
||||||
import core.game.world.map.RegionManager
|
import core.game.world.map.RegionManager
|
||||||
|
|
@ -11,7 +12,7 @@ import core.game.interaction.IntType
|
||||||
import core.game.interaction.InteractionListeners
|
import core.game.interaction.InteractionListeners
|
||||||
|
|
||||||
class PathfinderTests {
|
class PathfinderTests {
|
||||||
companion object {init {TestUtils.preTestSetup(); GatheringSkillOptionListeners().defineListeners() }}
|
companion object {init {TestUtils.preTestSetup(); GatheringSkillOptionListeners().defineListeners(); WoodcuttingListener().defineListeners() }}
|
||||||
|
|
||||||
@Test fun getOccupiedTilesShouldReturnCorrectSetOfTilesThatAnObjectOccupiesAtAllRotations() {
|
@Test fun getOccupiedTilesShouldReturnCorrectSetOfTilesThatAnObjectOccupiesAtAllRotations() {
|
||||||
//clay fireplace - 13609 - sizex: 1, sizey: 2
|
//clay fireplace - 13609 - sizex: 1, sizey: 2
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue