mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2026-08-24 03:45:13 -06:00
Merge branch 2009scape:master into master
This commit is contained in:
commit
275cda0dcf
39 changed files with 813 additions and 463 deletions
|
|
@ -1,31 +1,37 @@
|
|||
package content.global.bots
|
||||
|
||||
import content.global.skill.crafting.spinning.SpinningItem
|
||||
import content.global.skill.crafting.spinning.SpinningPulse
|
||||
import core.game.bots.*
|
||||
import core.game.interaction.DestinationFlag
|
||||
import core.game.interaction.MovementPulse
|
||||
import core.game.node.entity.skill.Skills
|
||||
import content.global.skill.crafting.spinning.SpinningItem
|
||||
import content.global.skill.crafting.spinning.SpinningPulse
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.path.Pathfinder
|
||||
import org.rs09.consts.Items
|
||||
import core.game.bots.SkillingBotAssembler
|
||||
import core.game.bots.Script
|
||||
|
||||
@PlayerCompatible
|
||||
@ScriptName("Seers' Village Flax Spinning")
|
||||
@ScriptDescription("Pick flax and spin it into string. Start in the flax field near Seers' Village.")
|
||||
@ScriptIdentifier("seers_flax")
|
||||
class SeersFlax : Script(){
|
||||
val waypoint1 = Location(2730, 3460, 0) // South of Seers' Village
|
||||
val waypoint2 = Location(2737, 3441, 0) // Outside flax field
|
||||
var state = State.PICKING
|
||||
var stage = 0
|
||||
var doorOpen = false
|
||||
override fun tick() {
|
||||
|
||||
when(state){
|
||||
|
||||
State.PICKING -> {
|
||||
val flax = scriptAPI.getNearestNode(2646,true)
|
||||
flax?.interaction?.handle(bot,flax.interaction[1])
|
||||
if(bot.inventory.getAmount(Items.FLAX_1779) > 25){
|
||||
if (bot.inventory.freeSlots() == 0) {
|
||||
state = State.TO_SPINNER
|
||||
return
|
||||
}
|
||||
|
||||
val flax = scriptAPI.getNearestNode("Flax", true)
|
||||
scriptAPI.interact(bot, flax, "Pick")
|
||||
}
|
||||
|
||||
State.TO_SPINNER -> {
|
||||
|
|
@ -101,14 +107,13 @@ class SeersFlax : Script(){
|
|||
}
|
||||
|
||||
State.RETURN_TO_FLAX -> {
|
||||
if(bot.location == Location.create(2756, 3478, 0))
|
||||
Pathfinder.find(bot,Location.create(2726, 3486, 0)).walk(bot)
|
||||
if(stage == 0)
|
||||
Pathfinder.find(bot,Location.create(2726, 3486, 0)).walk(bot).also { stage++ }
|
||||
when(bot.location){
|
||||
Location.create(2726, 3486, 0) -> Pathfinder.find(bot,Location.create(2729, 3469, 0)).walk(bot)
|
||||
Location.create(2729, 3469, 0) -> Pathfinder.find(bot,Location.create(2734, 3447, 0)).walk(bot)
|
||||
Location.create(2734, 3447, 0) -> state = State.PICKING.also { stage = 0 }
|
||||
val flax = scriptAPI.getNearestNode("Flax", true)
|
||||
if (flax != null) {
|
||||
state = State.PICKING
|
||||
} else if (bot.location.withinMaxnormDistance(waypoint1, 5)) {
|
||||
Pathfinder.find(bot, waypoint2).walk(bot)
|
||||
} else {
|
||||
Pathfinder.find(bot, waypoint1).walk(bot)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -124,7 +129,6 @@ class SeersFlax : Script(){
|
|||
|
||||
State.TELE_CAMELOT -> {
|
||||
scriptAPI.teleport(Location.create(2756, 3478, 0))
|
||||
stage = 0
|
||||
state = State.RETURN_TO_FLAX
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,17 @@ public final class AutocastSelectPlugin extends ComponentPlugin {
|
|||
if (!player.getAttribute("autocast_select", false)) {
|
||||
return true;
|
||||
}
|
||||
if (player.timers.getTimer("spellbook:swap") != null) {
|
||||
player.getPacketDispatch().sendMessage("You cannot autocast while using Spellbook Swap.");
|
||||
player.removeAttribute("autocast_select");
|
||||
player.removeAttribute("autocast_component");
|
||||
final WeaponInterface w = player.getExtension(WeaponInterface.class);
|
||||
if (w != null) {
|
||||
w.setAttackStyle(3);
|
||||
player.getInterfaceManager().openTab(w);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
player.removeAttribute("autocast_select");
|
||||
final WeaponInterface w = player.getExtension(WeaponInterface.class);
|
||||
if (w != null) {
|
||||
|
|
|
|||
|
|
@ -50,15 +50,14 @@ class EmptyOptionListener : InteractionListener {
|
|||
POTION(Items.POTION_195, Items.VIAL_229, "You empty the vial.", Sounds.LIQUID_2401),
|
||||
BURNT_STEW(Items.BURNT_STEW_2005, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
|
||||
NETTLE_TEA(Items.NETTLE_TEA_4239, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
|
||||
CUP_OF_TEA(Items.CUP_OF_TEA_4242, Items.EMPTY_CUP_1980, "You empty the cup of tea.", Sounds.LIQUID_2401),
|
||||
NETTLE_WATER(Items.NETTLE_WATER_4237, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
|
||||
NETTLE_TEA_MILKY(Items.NETTLE_TEA_4240, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
|
||||
NETTLE_TEA_CUP(Items.CUP_OF_TEA_4242, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
|
||||
NETTLE_TEA_CUP(Items.CUP_OF_TEA_4242, Items.EMPTY_CUP_1980, "You empty the cup of tea.", Sounds.LIQUID_2401),
|
||||
NETTLE_TEA_CUP_MILKY(Items.CUP_OF_TEA_4243, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
|
||||
TEA_CUP_WATER(Items.CUP_OF_WATER_4458, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
|
||||
TEA_CUP_HOT_WATER(Items.CUP_OF_HOT_WATER_4460, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
|
||||
NETTLE_TEA_PORCELAIN(Items.CUP_OF_TEA_4245, Items.PORCELAIN_CUP_4244, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
|
||||
NETTLE_TEA_PORCELAIN_MILKY(Items.CUP_OF_TEA_4246, Items.PORCELAIN_CUP_4244, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
|
||||
NETTLE_WATER(Items.NETTLE_WATER_4237, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
|
||||
NETTLE_TEA_MILKY(Items.NETTLE_TEA_4240, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
|
||||
TEA_CUP_WATER(Items.CUP_OF_WATER_4458, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
|
||||
TEA_CUP_HOT_WATER(Items.CUP_OF_HOT_WATER_4460, Items.EMPTY_CUP_1980, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
|
||||
BUILDERS_TEA1(Items.CUP_OF_TEA_7730, Items.EMPTY_CUP_7728, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
|
||||
BUILDERS_TEA1_MILKY(Items.CUP_OF_TEA_7731, Items.EMPTY_CUP_7728, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
|
||||
BUILDERS_TEA2(Items.CUP_OF_TEA_7733, Items.PORCELAIN_CUP_7732, "You empty the contents of the cup onto the floor.", Sounds.LIQUID_2401),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import core.api.utils.WeightBasedTable
|
|||
import core.api.utils.WeightedItem
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.IntType
|
||||
import content.global.activity.ttrail.TreasureTrailManager
|
||||
|
||||
class ImplingJarListener : InteractionListener {
|
||||
|
||||
|
|
@ -15,8 +16,14 @@ class ImplingJarListener : InteractionListener {
|
|||
override fun defineListeners() {
|
||||
on(JARS, IntType.ITEM, "loot"){ player, node ->
|
||||
val jar = node.asItem()
|
||||
val table = ImplingLoot.forId(jar.id) ?: return@on false
|
||||
val effectiveTable = if (TreasureTrailManager.getInstance(player).hasClue()) {
|
||||
table.excluding(*WeightBasedTable.CLUE_SCROLL_SLOTS)
|
||||
} else {
|
||||
table
|
||||
}
|
||||
|
||||
val loot = ImplingLoot.forId(jar.id)?.roll()?.firstOrNull() ?: return@on false
|
||||
val loot = effectiveTable.roll().firstOrNull() ?: return@on false
|
||||
|
||||
if(removeItem(player, jar, Container.INVENTORY)) {
|
||||
addItemOrDrop(player, loot.id, loot.amount)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package content.global.handlers.item
|
||||
|
||||
import content.data.LightSource
|
||||
import core.api.*
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.IntType
|
||||
import org.rs09.consts.Items
|
||||
import kotlin.collections.toIntArray
|
||||
|
||||
class LightSourceListeners : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
val lightSourceProducts = LightSource.values().map { it.product.id }.toIntArray()
|
||||
|
||||
on(lightSourceProducts, ITEM, "drop") { player, light ->
|
||||
val active = LightSource.getActiveLightSource(player).product.id
|
||||
if (player.location.isInRegion(10648) && light.id == active) { //Temple of Ikov dungeon
|
||||
sendMessage(player, "Dropping the " + LightSource.getActiveLightSource(player).product.name.lowercase() + " would leave you without a light source.")
|
||||
return@on false
|
||||
}
|
||||
val removed = removeItem(player, light.id)
|
||||
if (removed) {
|
||||
var droppedId = light.id
|
||||
if (light.id == Items.MINING_HELMET_5013) {
|
||||
droppedId = Items.MINING_HELMET_5014
|
||||
sendMessage(player, "The helmet goes out as you drop it.")
|
||||
}
|
||||
produceGroundItem(player, droppedId)
|
||||
}
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package content.global.handlers.item
|
||||
|
||||
import core.api.*
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.IntType
|
||||
import org.rs09.consts.Items
|
||||
|
||||
class MiningHelmetListener : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
on(Items.MINING_HELMET_5013, IntType.ITEM, "drop") { player, _ ->
|
||||
val removed = removeItem(player, Items.MINING_HELMET_5013)
|
||||
if (removed) produceGroundItem(player, Items.MINING_HELMET_5014)
|
||||
sendMessage(player, "The helmet goes out as you drop it.")
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
package content.global.handlers.item.withnpc
|
||||
|
||||
import core.api.removeItem
|
||||
import core.api.sendChat
|
||||
import core.api.sendMessage
|
||||
import content.global.skill.prayer.Bones
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.item.Item
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.NPCs
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.IntType
|
||||
|
||||
class BonesOnStrayDog : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
val bones = Bones.array
|
||||
|
||||
val dogs = intArrayOf(
|
||||
NPCs.STRAY_DOG_4766,
|
||||
NPCs.STRAY_DOG_4767,
|
||||
NPCs.STRAY_DOG_5917,
|
||||
NPCs.STRAY_DOG_5918
|
||||
)
|
||||
|
||||
onUseWith(IntType.NPC, bones, *dogs) { player, used, with ->
|
||||
used as Item; with as NPC
|
||||
|
||||
var woof = "Woof"
|
||||
|
||||
if (removeItem(player, used)) {
|
||||
sendMessage(
|
||||
player,
|
||||
"You feed the ${with.definition.name.lowercase()} your ${used.definition.name.lowercase()}."
|
||||
)
|
||||
|
||||
when (used.id) {
|
||||
Items.BURNT_BONES_528 -> {
|
||||
sendMessage(
|
||||
player,
|
||||
"The dog looks at you, disappointed, but takes the bones anyway."
|
||||
)
|
||||
|
||||
woof = "Woof..."
|
||||
}
|
||||
|
||||
Items.FAYRG_BONES_4830,
|
||||
Items.RAURG_BONES_4832,
|
||||
Items.OURG_BONES_4834 -> {
|
||||
sendMessage(
|
||||
player,
|
||||
"The dog looks at you, confused, but takes the bones anyway."
|
||||
)
|
||||
|
||||
woof = "Woof..?"
|
||||
}
|
||||
|
||||
Items.BABYDRAGON_BONES_534,
|
||||
Items.BIG_BONES_532 -> {
|
||||
sendMessage(
|
||||
player,
|
||||
"The dog seems to be very happy."
|
||||
)
|
||||
|
||||
woof = "Woof!"
|
||||
}
|
||||
|
||||
Items.WYVERN_BONES_6812,
|
||||
Items.DRAGON_BONES_536 -> {
|
||||
sendMessage(
|
||||
player,
|
||||
"The dog seems to be extremely overjoyed."
|
||||
)
|
||||
|
||||
woof = "WOOF!"
|
||||
}
|
||||
}
|
||||
|
||||
sendChat(with, woof)
|
||||
}
|
||||
|
||||
return@onUseWith true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
package content.global.handlers.item.withnpc
|
||||
|
||||
import core.api.removeItem
|
||||
import core.api.sendChat
|
||||
import core.api.sendMessage
|
||||
import content.global.skill.prayer.Bones
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.item.Item
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.NPCs
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.IntType
|
||||
import content.data.Meat
|
||||
import content.data.MeatState
|
||||
import core.api.animate
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import org.rs09.consts.Animations
|
||||
|
||||
/**
|
||||
* Represents the option Listener use-with on a stray dog.
|
||||
* Use bones, raw & cooked meats
|
||||
*/
|
||||
class FeedOnStrayDog : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
|
||||
val bones = Bones.array
|
||||
|
||||
val consumableMeats = Meat.values()
|
||||
.filter { it.state == MeatState.INEDIBLE_RAW || it.state == MeatState.EDIBLE_COOKED }
|
||||
.map { it.id }
|
||||
.toIntArray()
|
||||
|
||||
val allAllowedItems = bones + consumableMeats
|
||||
|
||||
val dogs = intArrayOf(
|
||||
NPCs.STRAY_DOG_4766,
|
||||
NPCs.STRAY_DOG_4767,
|
||||
NPCs.STRAY_DOG_5917,
|
||||
NPCs.STRAY_DOG_5918
|
||||
)
|
||||
|
||||
onUseWith(IntType.NPC, allAllowedItems, *dogs) { player, used, with ->
|
||||
used as Item; with as NPC
|
||||
|
||||
var woof = "Woof"
|
||||
|
||||
if (removeItem(player, used)) {
|
||||
sendMessage(
|
||||
player,
|
||||
"You feed the ${with.definition.name.lowercase()} your ${used.definition.name.lowercase()}."
|
||||
)
|
||||
|
||||
animate(player, Animations.HUMAN_BURYING_BONES_827)
|
||||
|
||||
when {
|
||||
bones.contains(used.id) -> {
|
||||
when (used.id) {
|
||||
Items.BURNT_BONES_528 -> {
|
||||
sendMessage(
|
||||
player,
|
||||
"The dog looks at you, disappointed, but takes the bones anyway."
|
||||
)
|
||||
|
||||
woof = "Woof..."
|
||||
}
|
||||
|
||||
Items.FAYRG_BONES_4830,
|
||||
Items.RAURG_BONES_4832,
|
||||
Items.OURG_BONES_4834 -> {
|
||||
sendMessage(
|
||||
player,
|
||||
"The dog looks at you, confused, but takes the bones anyway."
|
||||
)
|
||||
|
||||
woof = "Woof..?"
|
||||
}
|
||||
|
||||
Items.BABYDRAGON_BONES_534,
|
||||
Items.BIG_BONES_532 -> {
|
||||
sendMessage(
|
||||
player,
|
||||
"The dog seems to be very happy."
|
||||
)
|
||||
|
||||
woof = "Woof!"
|
||||
}
|
||||
|
||||
Items.WYVERN_BONES_6812,
|
||||
Items.DRAGON_BONES_536 -> {
|
||||
sendMessage(
|
||||
player,
|
||||
"The dog seems to be extremely overjoyed."
|
||||
)
|
||||
|
||||
woof = "WOOF!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
consumableMeats.contains(used.id) -> {
|
||||
sendMessage(
|
||||
player,
|
||||
"The dog wagged its tail happily."
|
||||
)
|
||||
|
||||
animate(with, Animation(4777))
|
||||
|
||||
woof = "Woof woof!"
|
||||
}
|
||||
}
|
||||
|
||||
sendChat(with, woof)
|
||||
}
|
||||
|
||||
return@onUseWith true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package content.global.handlers.npc
|
||||
|
||||
import core.api.*
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import org.rs09.consts.NPCs
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.npc.NPCBehavior
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.world.map.Direction
|
||||
import core.game.world.map.Location
|
||||
import org.rs09.consts.Animations
|
||||
import org.rs09.consts.Sounds
|
||||
|
||||
private val strayDogsIds = intArrayOf(
|
||||
NPCs.STRAY_DOG_4766,
|
||||
NPCs.STRAY_DOG_4767,
|
||||
NPCs.STRAY_DOG_5917,
|
||||
NPCs.STRAY_DOG_5918
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents the option Listener used to 'shoo-away' a stray dog.
|
||||
* Convert from legacy plugin src/main/content/region/misthalin/varrock/handlers/ShooAwayStrayDogPlugin.java
|
||||
* @author Vexia + beckrickert
|
||||
*
|
||||
*/
|
||||
class ShooAwayStrayDogListener : NPCBehavior(*strayDogsIds), InteractionListener {
|
||||
override fun defineListeners() {
|
||||
|
||||
on(strayDogsIds,IntType.NPC, "shoo-away") { player, node ->
|
||||
|
||||
val theDog = node as NPC
|
||||
|
||||
//Pause dog location
|
||||
lock(theDog, 2)
|
||||
stopWalk(theDog)
|
||||
theDog.faceTemporary(player, 1)
|
||||
|
||||
sendChat(player,"Thbbbbt!")
|
||||
animate(player, Animations.HUMAN_BLOW_RASPBERRY_2110)
|
||||
|
||||
//Dog whine sound
|
||||
playAudio(player, Sounds.WOLF_DEATH_911)
|
||||
sendChat(theDog,"Whine!")
|
||||
|
||||
//Dog whine kneeling down animation
|
||||
animate(theDog, Animation(4774))
|
||||
|
||||
//run away from player
|
||||
dogWhineRunAway(player, theDog)
|
||||
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun dogWhineRunAway(player: Player, theDog: NPC) {
|
||||
val playerCurLoc = player.getLocation()
|
||||
val dogCurLoc = theDog.getLocation()
|
||||
|
||||
//Get dog direction is facing from player's location
|
||||
val dogDirection = Direction.getDirection(dogCurLoc, playerCurLoc)
|
||||
val dogDirectionOpp = dogDirection.opposite
|
||||
|
||||
val xWalkLoc = dogCurLoc.x + (dogDirectionOpp.stepX * 12)
|
||||
val yWalkLoc = dogCurLoc.y + (dogDirectionOpp.stepY * 12)
|
||||
val dogWalkToNewLoc = Location(xWalkLoc, yWalkLoc, dogCurLoc.z)
|
||||
|
||||
//unlock entity movement so it can run away
|
||||
unlock(theDog)
|
||||
|
||||
//allow to "moon-walk" to the new location
|
||||
//Does weird stuff or ignores this when in 'following' mode
|
||||
//theDog.face(player)
|
||||
|
||||
forceWalk(theDog, dogWalkToNewLoc, "dumb")
|
||||
|
||||
}
|
||||
|
|
@ -53,10 +53,23 @@ public final class RemovalDialogue extends DialoguePlugin {
|
|||
@Override
|
||||
public boolean open(Object... args) {
|
||||
pos = (int[]) args[1];
|
||||
plane = player.getLocation().getZ();
|
||||
if (HouseManager.isInDungeon(player)) {
|
||||
plane = 3;
|
||||
}
|
||||
|
||||
if (args.length > 2 && args[2] instanceof Integer) {
|
||||
plane = (Integer) args[2];
|
||||
} else {
|
||||
plane = player.getLocation().getZ();
|
||||
if (HouseManager.isInDungeon(player)) {
|
||||
plane = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// check if room exists
|
||||
room = player.getHouseManager().getRooms()[plane][pos[0]][pos[1]];
|
||||
if (room == null || room.getProperties().isRoof()) {
|
||||
interpreter.sendPlainMessage(false, "There is no room there to remove.");
|
||||
return false;
|
||||
}
|
||||
|
||||
room = player.getHouseManager().getRooms()[plane][pos[0]][pos[1]];
|
||||
player.getDialogueInterpreter().sendOptions("Remove the " + (room != null ? room.getProperties().getName() : "room") + "?", "Yes", "No");
|
||||
stage = 0;
|
||||
|
|
|
|||
|
|
@ -29,15 +29,19 @@ public final class StaircasePlugin extends OptionHandler {
|
|||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
||||
ClassScanner.definePlugin(new BuildDialogue());
|
||||
ClassScanner.definePlugin(new ClimbPohLadder());
|
||||
for (int i = 13497; i < 13507; i++) {
|
||||
// 13497 - 13502 are normal stairs
|
||||
// 13503 - 13506 are spiral stairs
|
||||
for (int i = 13497; i <= 13506; i++) {
|
||||
SceneryDefinition.forId(i).getHandlers().put("option:climb", this);
|
||||
SceneryDefinition.forId(i).getHandlers().put("option:climb-up", this);
|
||||
SceneryDefinition.forId(i).getHandlers().put("option:climb-down", this);
|
||||
SceneryDefinition.forId(i).getHandlers().put("option:remove-room", this);
|
||||
}
|
||||
// 13409 is dungeon entrance
|
||||
SceneryDefinition.forId(13409).getHandlers().put("option:enter", this);
|
||||
SceneryDefinition.forId(13409).getHandlers().put("option:remove-room", this);
|
||||
for (int id = 13328; id < 13331; id++) {
|
||||
// 13328 - 13330 are dungeon ladders
|
||||
for (int id = 13328; id <= 13330; id++) {
|
||||
SceneryDefinition.forId(id).getHandlers().put("option:climb", this);
|
||||
SceneryDefinition.forId(id).getHandlers().put("option:remove-room", this);
|
||||
}
|
||||
|
|
@ -69,13 +73,47 @@ public final class StaircasePlugin extends OptionHandler {
|
|||
SceneryBuilder.replace(object, object.transform(object.getId() - 3));
|
||||
return true;
|
||||
case "remove-room":
|
||||
if (player.getLocation().getZ() != 0) {
|
||||
player.getPacketDispatch().sendMessage("The room below is supporting this room!");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
int currentZ = player.getLocation().getZ();
|
||||
int chunkX = player.getLocation().getChunkX();
|
||||
int chunkY = player.getLocation().getChunkY();
|
||||
|
||||
// must be in building mode
|
||||
if(!player.getHouseManager().isBuildingMode()) {
|
||||
player.getPacketDispatch().sendMessage("You can only do this in building mode.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// if you're in the dungeon, try and remove the room at Z = 0 from the house region.
|
||||
if (HouseManager.isInDungeon(player)) {
|
||||
player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, 0);
|
||||
|
||||
// From ground floor (z=0), stairs could lead to 1st floor (z=1) or the dungeon (z=3)
|
||||
} else {
|
||||
// garden dungeon entrance and oubliette ladders don't need a height selection
|
||||
if(node.getId() == 13409 || node.getId() == 13675 || node.getId() == 13676 || node.getId() == 13677 || node.getId() == 13678 || node.getId() == 13679 || node.getId() == 13680) {
|
||||
player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, 3);
|
||||
} else {
|
||||
player.getDialogueInterpreter().sendOptions("Remove which room?", "Room Above", "Room Below", "Cancel");
|
||||
player.getDialogueInterpreter().addAction((interId, btn) -> {
|
||||
if (btn == 2) { // Above
|
||||
player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, currentZ + 1);
|
||||
} else if (btn == 3) { // Below
|
||||
if (currentZ - 1 == -1) {
|
||||
player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, 3);
|
||||
} else {
|
||||
player.getDialogueInterpreter().open("con:remove", null, new int[]{chunkX, chunkY}, currentZ - 1);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
|
||||
case "climb":
|
||||
if (house.getDungeonRegion() == player.getViewport().getRegion()) {
|
||||
if (HouseManager.isInDungeon(player)) {
|
||||
climb(player, 1, house, object);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -379,10 +417,11 @@ public final class StaircasePlugin extends OptionHandler {
|
|||
for (Direction d : dirs) {
|
||||
if (d == room.getRotation()) {
|
||||
r.setRotation(d);
|
||||
Hotspot stairs = room.getStairs();
|
||||
int index = stairs != null ? stairs.getDecorationIndex() : -1;
|
||||
// todo this just puts a default set of stairs in the dungeon. The below code works for skill/quest hall, but not a dungeon entrance.
|
||||
//Hotspot stairs = room.getStairs();
|
||||
//int index = stairs != null ? stairs.getDecorationIndex() : -1;
|
||||
BuildingUtils.buildRoom(player, r, plane, roomX, roomY, r.getExits(), true);
|
||||
r.getStairs().setDecorationIndex(index);
|
||||
r.getStairs().setDecorationIndex(/*index*/1);
|
||||
end();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
package content.global.skill.cooking
|
||||
|
||||
import org.rs09.consts.Items
|
||||
import core.api.replaceSlot
|
||||
import core.api.removeItem
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.item.Item
|
||||
|
||||
class NettleWaterListener : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
onUseWith(IntType.ITEM, Items.BOWL_OF_WATER_1921, Items.NETTLES_4241) { player, used, with ->
|
||||
replaceSlot(player, used.asItem().slot, Item(Items.NETTLE_WATER_4237), used.asItem())
|
||||
removeItem(player, with.asItem())
|
||||
return@onUseWith true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package content.global.skill.hunter.falconry
|
||||
|
||||
import core.game.node.Node
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
|
||||
/**
|
||||
* Used to define the zoneBorders for an MapArea listener
|
||||
*/
|
||||
object FalconryZone {
|
||||
val piscatorisFalconryZones = arrayOf(
|
||||
ZoneBorders(2364, 3573, 2394, 3620, 0)
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package content.global.skill.hunter.falconry
|
||||
|
||||
import core.api.*
|
||||
import core.api.MapArea
|
||||
import core.game.activity.ActivityManager
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import core.game.node.entity.player.Player
|
||||
import org.rs09.consts.Items
|
||||
|
||||
class PiscFalconryListener: MapArea {
|
||||
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||
return FalconryZone.piscatorisFalconryZones
|
||||
}
|
||||
|
||||
/**
|
||||
* When player leaves by any method other than logout/disconnect.
|
||||
*/
|
||||
override fun areaLeave(entity: Entity, logout: Boolean) {
|
||||
if (entity is Player) {
|
||||
if (!logout) {
|
||||
// Legacy compatibility of FalconryActivityPlugin.java
|
||||
removeAttribute(entity, "falconry")
|
||||
|
||||
// Compatibility with StileShortcut.kt
|
||||
ActivityManager.getActivity("falconry").leave(entity, false)
|
||||
|
||||
removeAll(entity, Items.FALCONERS_GLOVE_10023)
|
||||
removeAll(entity, Items.FALCONERS_GLOVE_10023, Container.EQUIPMENT)
|
||||
|
||||
removeAll(entity, Items.FALCONERS_GLOVE_10024)
|
||||
removeAll(entity, Items.FALCONERS_GLOVE_10024, Container.EQUIPMENT)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,23 @@
|
|||
package content.global.skill.magic
|
||||
|
||||
import core.api.*
|
||||
import content.data.Quests
|
||||
import core.api.animate
|
||||
import core.api.hasLevelStat
|
||||
import core.api.hasRequirement
|
||||
import core.api.lock
|
||||
import core.api.playAudio
|
||||
import core.api.sendMessage
|
||||
import core.game.event.SpellbookChangeEvent
|
||||
import core.game.interaction.IntType
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.combat.equipment.WeaponInterface
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.SpellBookManager
|
||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook
|
||||
import core.game.node.entity.skill.Skills
|
||||
import org.rs09.consts.Scenery
|
||||
import org.rs09.consts.Sounds
|
||||
import content.data.Quests
|
||||
|
||||
class MagicAltarListener : InteractionListener {
|
||||
override fun defineListeners() {
|
||||
|
|
@ -36,26 +44,36 @@ class MagicAltarListener : InteractionListener {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun swapSpellBook(player: Player, altar: Node) {
|
||||
lock(player, 3)
|
||||
playAudio(player, Sounds.PRAYER_RECHARGE_2674)
|
||||
animate(player, 645)
|
||||
|
||||
if (altar.id == ANCIENT_ALTAR) {
|
||||
player.skills.decrementPrayerPoints(player.skills.prayerPoints)
|
||||
}
|
||||
|
||||
if (SpellBook.forInterface(player.spellBookManager.spellBook) == if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR) {
|
||||
sendMessage(player, if (altar.id == ANCIENT_ALTAR) "You feel a strange drain upon your memory..." else "Modern spells activated!")
|
||||
player.spellBookManager.setSpellBook(SpellBook.MODERN)
|
||||
player.spellBookManager.update(player)
|
||||
} else {
|
||||
sendMessage(player, if (altar.id == ANCIENT_ALTAR) "You feel a strange wisdom fill your mind..." else "Lunar spells activated!")
|
||||
player.spellBookManager.setSpellBook(if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR)
|
||||
player.spellBookManager.update(player)
|
||||
}
|
||||
}
|
||||
|
||||
private fun swapSpellBook(player : Player, altar : Node) {
|
||||
lock(player, 3)
|
||||
playAudio(player, Sounds.PRAYER_RECHARGE_2674)
|
||||
animate(player, 645)
|
||||
if (altar.id == ANCIENT_ALTAR) {
|
||||
player.skills.decrementPrayerPoints(player.skills.prayerPoints)
|
||||
}
|
||||
val weaponInterface = player.getExtension<WeaponInterface>(WeaponInterface::class.java)
|
||||
if (weaponInterface != null && player.properties.autocastSpell != null) {
|
||||
weaponInterface.selectAutoSpell(-1, true)
|
||||
}
|
||||
if (SpellBook.forInterface(player.spellBookManager.spellBook) == if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR) {
|
||||
player.dispatch(SpellbookChangeEvent(
|
||||
SpellBook.forInterface(player.spellBookManager.spellBook),
|
||||
SpellBook.MODERN,
|
||||
SpellBookManager.SpellbookChangeSource.ALTAR))
|
||||
sendMessage(player, if (altar.id == ANCIENT_ALTAR) "You feel a strange drain upon your memory..." else "Modern spells activated!")
|
||||
player.spellBookManager.setSpellBook(SpellBook.MODERN)
|
||||
player.spellBookManager.update(player)
|
||||
} else {
|
||||
player.dispatch(SpellbookChangeEvent(
|
||||
SpellBook.forInterface(player.spellBookManager.spellBook),
|
||||
if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR,
|
||||
SpellBookManager.SpellbookChangeSource.ALTAR))
|
||||
sendMessage(player, if (altar.id == ANCIENT_ALTAR) "You feel a strange wisdom fill your mind..." else "Lunar spells activated!")
|
||||
player.spellBookManager.setSpellBook(if (altar.id == ANCIENT_ALTAR) SpellBook.ANCIENT else SpellBook.LUNAR)
|
||||
player.spellBookManager.update(player)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ANCIENT_ALTAR = Scenery.ALTAR_6552
|
||||
|
|
|
|||
|
|
@ -1,13 +1,25 @@
|
|||
package content.global.skill.magic
|
||||
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.combat.spell.CombinationRune
|
||||
import core.game.node.entity.combat.spell.MagicStaff
|
||||
import core.game.node.entity.combat.spell.Runes
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
|
||||
object SpellUtils {
|
||||
/**
|
||||
* Validates that the spell packet's interface matches the player's actual server-side spellbook.
|
||||
* This prevents exploits where the client has a stale spellbook interface (e.g., after Spellbook Swap reverts).
|
||||
* @param player The player casting the spell
|
||||
* @param packetInterfaceId The interface ID from the client's spell packet
|
||||
* @return true if the interfaces match (valid), false if there's a client/server desync
|
||||
*/
|
||||
@JvmStatic
|
||||
fun validateSpellbookInterface(player : Player, packetInterfaceId : Int) : Boolean {
|
||||
val actualSpellbook = player.spellBookManager.spellBook
|
||||
return packetInterfaceId == actualSpellbook
|
||||
}
|
||||
fun usingStaff(p: Player, rune: Int): Boolean {
|
||||
val weapon = p.equipment[3] ?: return false
|
||||
val staff = MagicStaff.forId(rune) ?: return false
|
||||
|
|
|
|||
|
|
@ -1,10 +1,21 @@
|
|||
package content.global.skill.magic;
|
||||
|
||||
import content.data.Quests;
|
||||
import core.game.component.Component;
|
||||
import core.game.dialogue.DialoguePlugin;
|
||||
import core.game.event.SpellbookChangeEvent;
|
||||
import core.game.interaction.Listener;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.plugin.Initializable;
|
||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook;
|
||||
import core.game.node.entity.player.link.SpellBookManager.SpellbookChangeSource;
|
||||
import core.game.node.entity.skill.Skills;
|
||||
import core.game.node.item.Item;
|
||||
import core.plugin.Initializable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static core.api.ContentAPIKt.hasRequirement;
|
||||
|
||||
|
||||
/**
|
||||
* Handles the SpellbookSwapDialogue dialogue.
|
||||
|
|
@ -73,8 +84,27 @@ public class SpellbookSwapDialogue extends DialoguePlugin {
|
|||
break;
|
||||
}
|
||||
final SpellBook book = type == 1 ? SpellBook.ANCIENT : SpellBook.MODERN;
|
||||
if (book == SpellBook.ANCIENT && !hasRequirement(player, Quests.DESERT_TREASURE)) {
|
||||
player.getPacketDispatch().sendMessage("You need to complete Desert Treasure to use Ancient Magicks.");
|
||||
end();
|
||||
return true;
|
||||
}
|
||||
// Remove runes for Spellbook Swap
|
||||
ArrayList<Item> runes = player.getAttribute("spell:runes", new ArrayList<>());
|
||||
if (!runes.isEmpty() && player.getInventory().remove(runes.toArray(new Item[0]))) {
|
||||
player.removeAttribute("spell:runes");
|
||||
player.removeAttribute("tablet-spell");
|
||||
}
|
||||
// Award XP
|
||||
player.skills.addExperience(Skills.MAGIC, 130);
|
||||
// Change book
|
||||
player.dispatch(new SpellbookChangeEvent(
|
||||
SpellBook.LUNAR,
|
||||
book,
|
||||
SpellbookChangeSource.SPELLBOOK_SWAP_CAST));
|
||||
player.getSpellBookManager().setSpellBook(book);
|
||||
player.getInterfaceManager().openTab(new Component(book.getInterfaceId()));
|
||||
player.getPacketDispatch().sendMessage("You have 2 minutes before your spellbook changes back to the Lunar Spellbook!");
|
||||
end();
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
package content.global.skill.magic.ancient;
|
||||
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.link.diary.DiaryType;
|
||||
import core.game.node.entity.combat.spell.MagicSpell;
|
||||
import core.game.node.entity.combat.spell.Runes;
|
||||
import core.game.event.SpellCastEvent;
|
||||
import core.game.node.Node;
|
||||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.combat.spell.MagicSpell;
|
||||
import core.game.node.entity.combat.spell.Runes;
|
||||
import core.game.node.entity.combat.spell.SpellType;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook;
|
||||
import core.game.node.entity.player.link.TeleportManager.TeleportType;
|
||||
import core.game.node.entity.player.link.diary.DiaryType;
|
||||
import core.game.node.item.Item;
|
||||
import core.game.world.GameWorld;
|
||||
import core.game.world.map.Location;
|
||||
|
|
@ -67,6 +68,7 @@ public final class AncientTeleportPlugin extends MagicSpell {
|
|||
return false;
|
||||
}
|
||||
homeTeleport((Player) entity, Location.create(3087, 3495, 0));
|
||||
entity.dispatch(new SpellCastEvent(SpellBook.ANCIENT, getSpellId(), target));
|
||||
} else if (entity.getTeleporter().send(location.transform(0, RandomFunction.random(3), 0), TeleportType.ANCIENT)) {
|
||||
if (!super.meetsRequirements(entity, true, true)) {
|
||||
entity.getTeleporter().getCurrentTeleport().stop();
|
||||
|
|
|
|||
|
|
@ -24,12 +24,17 @@ import core.game.system.config.NPCConfigParser
|
|||
import core.game.system.task.Pulse
|
||||
import core.game.system.timer.impl.PoisonImmunity
|
||||
import core.game.system.timer.impl.SkillRestore
|
||||
import core.game.system.timer.impl.SpellbookSwap
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager
|
||||
import core.game.world.repository.Repository
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.tools.RandomFunction
|
||||
import org.rs09.consts.*
|
||||
import org.rs09.consts.Animations
|
||||
import org.rs09.consts.Components
|
||||
import org.rs09.consts.Graphics
|
||||
import org.rs09.consts.Items
|
||||
import org.rs09.consts.Sounds
|
||||
import kotlin.math.floor
|
||||
|
||||
class LunarListeners : SpellListener("lunar"), Commands {
|
||||
|
|
@ -280,9 +285,14 @@ class LunarListeners : SpellListener("lunar"), Commands {
|
|||
*/
|
||||
|
||||
// Level 96
|
||||
/**
|
||||
* Spellbook Swap
|
||||
*/
|
||||
onCast(Lunar.SPELLBOOK_SWAP, NONE) { player, _ ->
|
||||
requires(player, 96, arrayOf(
|
||||
Item(Items.LAW_RUNE_563, 1),
|
||||
Item(Items.COSMIC_RUNE_564, 2),
|
||||
Item(Items.ASTRAL_RUNE_9075, 3)
|
||||
))
|
||||
spellbookSwap(player)
|
||||
}
|
||||
}
|
||||
|
||||
// Spell handlers
|
||||
|
|
@ -783,11 +793,20 @@ class LunarListeners : SpellListener("lunar"), Commands {
|
|||
/**
|
||||
* Heal Group
|
||||
*/
|
||||
|
||||
// Level 96
|
||||
/**
|
||||
* Spellbook Swap
|
||||
*/
|
||||
|
||||
// Level 96
|
||||
private fun spellbookSwap(player : Player) {
|
||||
// Runes are removed on successful swap to another spellbook in SpellbookSwapDialogue.java
|
||||
// removeRunes(player, true)
|
||||
lock(player, 4)
|
||||
visualizeSpell(player, 6299, 1062)
|
||||
player.scripts.removeWeakScripts()
|
||||
player.dialogueInterpreter.open(3264731)
|
||||
registerTimer(player, SpellbookSwap())
|
||||
// XP is awarded in SpellbookSwapDialogue.java similarly to rune removal handling
|
||||
// addXP(player, 130.0)
|
||||
setDelay(player, false)
|
||||
}
|
||||
|
||||
// Other/Multi spell use-case
|
||||
private fun sendTeleport(player: Player, xp: Double, loc: Location){
|
||||
|
|
|
|||
|
|
@ -1,84 +0,0 @@
|
|||
package content.global.skill.magic.lunar;
|
||||
|
||||
import core.game.component.Component;
|
||||
import core.game.node.entity.combat.spell.MagicSpell;
|
||||
import core.game.node.entity.combat.spell.Runes;
|
||||
import core.game.node.Node;
|
||||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.combat.spell.SpellType;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook;
|
||||
import core.game.node.item.Item;
|
||||
import core.game.system.task.Pulse;
|
||||
import core.game.world.GameWorld;
|
||||
import core.game.world.update.flag.context.Animation;
|
||||
import core.game.world.update.flag.context.Graphics;
|
||||
import core.plugin.Initializable;
|
||||
import core.plugin.Plugin;
|
||||
import core.tools.RandomFunction;
|
||||
|
||||
/**
|
||||
* The spellbook swap spell.
|
||||
* @author 'Vexia
|
||||
*/
|
||||
@Initializable
|
||||
public class SpellbookSwapSpell extends MagicSpell {
|
||||
|
||||
/**
|
||||
* Represents the animation of this spell.
|
||||
*/
|
||||
private final Animation ANIMATION = new Animation(6299);
|
||||
|
||||
/**
|
||||
* Represents the graphics of this spell.
|
||||
*/
|
||||
private final Graphics GRAPHIC = new Graphics(1062);
|
||||
|
||||
/**
|
||||
* Constructs a new {@code SpellbookSwapSpell} {@code Object}.
|
||||
*/
|
||||
public SpellbookSwapSpell() {
|
||||
super(SpellBook.LUNAR, 96, 130, null, null, null, new Item[] { new Item(Runes.LAW_RUNE.getId(), 1), new Item(Runes.COSMIC_RUNE.getId(), 2), new Item(Runes.ASTRAL_RUNE.getId(), 3) });
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin<SpellType> newInstance(SpellType arg) throws Throwable {
|
||||
SpellBook.LUNAR.register(12, this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(Entity entity, Node target) {
|
||||
final Player player = (Player) entity;
|
||||
if (!super.meetsRequirements(player, true, true)) {
|
||||
return false;
|
||||
}
|
||||
player.lock(9);
|
||||
player.animate(ANIMATION);
|
||||
player.graphics(GRAPHIC);
|
||||
player.getDialogueInterpreter().open(3264731);
|
||||
final int id = RandomFunction.random(1, 500000);
|
||||
player.setAttribute("spell:swap", id);
|
||||
GameWorld.getPulser().submit(new Pulse(20, player) {
|
||||
@Override
|
||||
public boolean pulse() {
|
||||
if (player.getAttribute("spell:swap", 0) == id) {
|
||||
removeTemporarySpell(player);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to remove the temp spell swap.
|
||||
* @param player the player.
|
||||
*/
|
||||
public static void removeTemporarySpell(final Player player) {
|
||||
player.removeAttribute("spell:swap");
|
||||
player.getSpellBookManager().setSpellBook(SpellBook.LUNAR);
|
||||
player.getInterfaceManager().openTab(new Component(SpellBook.LUNAR.getInterfaceId()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,29 @@
|
|||
package content.global.skill.skillcapeperks
|
||||
|
||||
import core.game.component.Component
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.SpellBookManager
|
||||
import core.game.node.entity.player.link.TeleportManager
|
||||
import content.data.Quests
|
||||
import content.global.skill.farming.PatchType
|
||||
import content.global.skill.farming.Plantable
|
||||
import content.global.skill.runecrafting.Altar
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.zone.impl.DarkZone
|
||||
import core.plugin.Initializable
|
||||
import core.game.world.GameWorld
|
||||
import content.global.skill.farming.*
|
||||
import core.ServerStore
|
||||
import core.ServerStore.Companion.getBoolean
|
||||
import core.ServerStore.Companion.getInt
|
||||
import core.api.*
|
||||
import core.api.closeDialogue
|
||||
import core.api.getAttribute
|
||||
import core.api.hasRequirement
|
||||
import core.api.sendDialogue
|
||||
import core.api.sendMessage
|
||||
import core.api.teleport
|
||||
import core.cache.def.impl.ItemDefinition
|
||||
import core.tools.END_DIALOGUE
|
||||
import org.rs09.consts.Items
|
||||
import content.data.Quests
|
||||
import core.game.component.Component
|
||||
import core.game.event.SpellbookChangeEvent
|
||||
import core.game.node.entity.combat.equipment.WeaponInterface
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.SpellBookManager
|
||||
import core.game.node.entity.player.link.TeleportManager
|
||||
import core.game.world.GameWorld
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.zone.impl.DarkZone
|
||||
import core.plugin.Initializable
|
||||
|
||||
enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? = null) {
|
||||
BAREFISTED_SMITHING("cape_perks:barefisted-smithing"),
|
||||
|
|
@ -157,70 +163,74 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)?
|
|||
}
|
||||
|
||||
@Initializable
|
||||
class MagicCapeDialogue(player: Player? = null): core.game.dialogue.DialoguePlugin(player){
|
||||
override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin {
|
||||
return MagicCapeDialogue(player)
|
||||
}
|
||||
class MagicCapeDialogue(player : Player? = null) : core.game.dialogue.DialoguePlugin(player) {
|
||||
override fun newInstance(player : Player?) : core.game.dialogue.DialoguePlugin {
|
||||
return MagicCapeDialogue(player)
|
||||
}
|
||||
|
||||
override fun open(vararg args: Any?): Boolean {
|
||||
when(player.spellBookManager.spellBook){
|
||||
SpellBookManager.SpellBook.ANCIENT.interfaceId -> options("Modern","Lunar")
|
||||
SpellBookManager.SpellBook.MODERN.interfaceId -> options("Ancient","Lunar")
|
||||
SpellBookManager.SpellBook.LUNAR.interfaceId -> options ("Modern","Ancient")
|
||||
}
|
||||
return true
|
||||
}
|
||||
override fun open(vararg args : Any?) : Boolean {
|
||||
when (player.spellBookManager.spellBook) {
|
||||
SpellBookManager.SpellBook.ANCIENT.interfaceId -> options("Modern", "Lunar")
|
||||
SpellBookManager.SpellBook.MODERN.interfaceId -> options("Ancient", "Lunar")
|
||||
SpellBookManager.SpellBook.LUNAR.interfaceId -> options("Modern", "Ancient")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun handle(interfaceId: Int, buttonId: Int): Boolean {
|
||||
val spellbook = when(player.spellBookManager.spellBook){
|
||||
SpellBookManager.SpellBook.ANCIENT.interfaceId -> {
|
||||
when(buttonId){
|
||||
1 -> SpellBookManager.SpellBook.MODERN
|
||||
2 -> SpellBookManager.SpellBook.LUNAR
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
override fun handle(interfaceId : Int, buttonId : Int) : Boolean {
|
||||
val spellbook = when (player.spellBookManager.spellBook) {
|
||||
SpellBookManager.SpellBook.ANCIENT.interfaceId -> {
|
||||
when (buttonId) {
|
||||
1 -> SpellBookManager.SpellBook.MODERN
|
||||
2 -> SpellBookManager.SpellBook.LUNAR
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
SpellBookManager.SpellBook.MODERN.interfaceId -> {
|
||||
when(buttonId){
|
||||
1 -> SpellBookManager.SpellBook.ANCIENT
|
||||
2 -> SpellBookManager.SpellBook.LUNAR
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
SpellBookManager.SpellBook.MODERN.interfaceId -> {
|
||||
when (buttonId) {
|
||||
1 -> SpellBookManager.SpellBook.ANCIENT
|
||||
2 -> SpellBookManager.SpellBook.LUNAR
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
SpellBookManager.SpellBook.LUNAR.interfaceId -> {
|
||||
when(buttonId){
|
||||
1 -> SpellBookManager.SpellBook.MODERN
|
||||
2 -> SpellBookManager.SpellBook.ANCIENT
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
SpellBookManager.SpellBook.LUNAR.interfaceId -> {
|
||||
when (buttonId) {
|
||||
1 -> SpellBookManager.SpellBook.MODERN
|
||||
2 -> SpellBookManager.SpellBook.ANCIENT
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
end()
|
||||
if(spellbook != null){
|
||||
if (spellbook == SpellBookManager.SpellBook.ANCIENT) {
|
||||
if (!hasRequirement(player, Quests.DESERT_TREASURE))
|
||||
return true
|
||||
}
|
||||
else if (spellbook == SpellBookManager.SpellBook.LUNAR) {
|
||||
if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY))
|
||||
return true
|
||||
}
|
||||
player.spellBookManager.setSpellBook(spellbook)
|
||||
player.interfaceManager.openTab(Component(spellbook.interfaceId))
|
||||
player.incrementAttribute("/save:cape_perks:librarian-magus-charges",-1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(509871234)
|
||||
}
|
||||
end()
|
||||
if (spellbook != null) {
|
||||
if (spellbook == SpellBookManager.SpellBook.ANCIENT) {
|
||||
if (!hasRequirement(player, Quests.DESERT_TREASURE)) return true
|
||||
} else if (spellbook == SpellBookManager.SpellBook.LUNAR) {
|
||||
if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) return true
|
||||
}
|
||||
val weaponInterface = player.getExtension<WeaponInterface>(WeaponInterface::class.java)
|
||||
if (weaponInterface != null && player.properties.autocastSpell != null) {
|
||||
weaponInterface.selectAutoSpell(-1, true)
|
||||
}
|
||||
player.dispatch(SpellbookChangeEvent(
|
||||
SpellBookManager.SpellBook.forInterface(player.spellBookManager.spellBook),
|
||||
spellbook,
|
||||
SpellBookManager.SpellbookChangeSource.MAGIC_CAPE_PERK))
|
||||
player.spellBookManager.setSpellBook(spellbook)
|
||||
player.interfaceManager.openTab(Component(spellbook.interfaceId))
|
||||
player.incrementAttribute("/save:cape_perks:librarian-magus-charges", -1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getIds() : IntArray {
|
||||
return intArrayOf(509871234)
|
||||
}
|
||||
}
|
||||
|
||||
@Initializable
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import core.game.interaction.IntType
|
|||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.Item
|
||||
import org.rs09.consts.Sounds
|
||||
import content.global.activity.ttrail.TreasureTrailManager
|
||||
|
||||
class ThievingListeners : InteractionListener {
|
||||
|
||||
|
|
@ -66,7 +67,12 @@ class ThievingListeners : InteractionListener {
|
|||
}
|
||||
|
||||
player.animator.animate(PICKPOCKET_ANIM)
|
||||
val lootTable = pickpocketRoll(player, pickpocketData.low, pickpocketData.high, pickpocketData.table)
|
||||
val effectiveTable = if (TreasureTrailManager.getInstance(player).hasClue()) {
|
||||
pickpocketData.table.excluding(*WeightBasedTable.CLUE_SCROLL_SLOTS)
|
||||
} else {
|
||||
pickpocketData.table
|
||||
}
|
||||
val lootTable = pickpocketRoll(player, pickpocketData.low, pickpocketData.high, effectiveTable)
|
||||
if(lootTable == null){
|
||||
node.asNpc().face(player)
|
||||
node.asNpc().animator.animate(NPC_ANIM)
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ public final class SpinolypNPC extends AbstractNPC {
|
|||
super.impact(entity, victim, state);
|
||||
if (super.getType() == CombatStyle.MAGIC && state.getEstimatedHit() > 0) {
|
||||
victim.getSkills().decrementPrayerPoints(1);
|
||||
} else {
|
||||
} else if (super.getType() == CombatStyle.RANGE && state.getEstimatedHit() > 0) {
|
||||
if (RandomFunction.random(20) == 5) {
|
||||
applyPoison(victim, entity, 30);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -276,8 +276,7 @@ class PlagueCityListeners : InteractionListener {
|
|||
}
|
||||
}
|
||||
1 -> playerl(FacialExpression.NEUTRAL, "I'm a friend of Jethick's, I have come to return a book he borrowed.").also { stage++ }
|
||||
// todo change this back after sendItemDialogue is fixed for having a single line before See #1885
|
||||
2 -> npc(FacialExpression.FRIENDLY, "", "Oh... why didn't you say, come in then.", "").also { stage++ }
|
||||
2 -> npc(FacialExpression.FRIENDLY, "Oh... why didn't you say, come in then.").also { stage++ }
|
||||
3 -> sendItemDialogue(player!!, Items.BOOK_1509, "You hand the book to Ted as you enter.").also {
|
||||
DoorActionHandler.handleAutowalkDoor(player, getScenery(2531, 3328, 0))
|
||||
setQuestStage(player!!, Quests.PLAGUE_CITY, 9)
|
||||
|
|
|
|||
|
|
@ -137,20 +137,6 @@ class TempleOfIkovListeners : InteractionListener {
|
|||
teleport(player, Location.create(2649, 9804, 0))
|
||||
}
|
||||
|
||||
// don't let light extinguish or cape take off
|
||||
val lightSourceProducts = LightSource.values().map { it.product.id }.toIntArray()
|
||||
|
||||
on(lightSourceProducts, ITEM, "drop") { player, light ->
|
||||
val active = LightSource.getActiveLightSource(player).product.id
|
||||
if (player.location.isInRegion(10648) && light.id == active) {
|
||||
sendMessage(player, "Dropping the " + LightSource.getActiveLightSource(player).product.name.lowercase() + " would leave you without a light source.")
|
||||
return@on false
|
||||
}
|
||||
val removed = removeItem(player, light.id)
|
||||
if (removed) produceGroundItem(player, light.id)
|
||||
return@on true
|
||||
}
|
||||
|
||||
// B: Attach lever, authentic if you log out, the lever is lost, and you have to do that bridge again
|
||||
onUseWith(SCENERY, Items.LEVER_83, Scenery.LEVER_BRACKET_86) { player, used, with ->
|
||||
removeItem(player, used)
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ class TribalTotem : Quest(Quests.TRIBAL_TOTEM,126,125,1,200,0,1,5){
|
|||
val started = player?.questRepository?.getStage(Quests.TRIBAL_TOTEM)!! > 0
|
||||
|
||||
if(!started){
|
||||
line(player,"I can start this quest by speaking to !!Kangai Mau?? in",line++)
|
||||
line(player,"!!Shrimp & Parrot?? restaurant in Brimhaven.",line++)
|
||||
line(player,"I can start this quest by speaking to !!Kangai Mau?? in !!the??",line++)
|
||||
line(player,"!!Shrimp & Parrot?? restaurant in Brimhaven",line++)
|
||||
line += 1
|
||||
line(player,"To complete this quest I need:",line++)
|
||||
line(player,"!!Level 21 Theiving??",line++, player?.skills?.getStaticLevel(Skills.THIEVING)!! >= 21)
|
||||
line(player,"!!Level 21 Thieving??",line++, player?.skills?.getStaticLevel(Skills.THIEVING)!! >= 21)
|
||||
}
|
||||
else if(started && stage != 100){
|
||||
if(stage >= 10){
|
||||
|
|
|
|||
|
|
@ -420,7 +420,7 @@ public final class GertrudeDialogue extends DialoguePlugin {
|
|||
* @return the item.
|
||||
*/
|
||||
public Item getKitten() {
|
||||
return new Item(RandomFunction.random(1555, 1560));
|
||||
return new Item(RandomFunction.random(1555, 1561));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
package content.region.misthalin.varrock.handlers;
|
||||
|
||||
import core.cache.def.impl.NPCDefinition;
|
||||
import core.game.interaction.OptionHandler;
|
||||
import core.game.node.Node;
|
||||
import core.game.node.entity.npc.NPC;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.world.update.flag.context.Animation;
|
||||
import core.plugin.Initializable;
|
||||
import core.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents the option plugin used to shoo away a dog.
|
||||
* @author 'Vexia
|
||||
* @version 1.0
|
||||
*/
|
||||
@Initializable
|
||||
public final class ShooAwayStrayDogPlugin extends OptionHandler {
|
||||
|
||||
/**
|
||||
* Represents the animatio to use.
|
||||
*/
|
||||
private static final Animation ANIMATION = new Animation(2110);
|
||||
|
||||
@Override
|
||||
public Plugin<Object> newInstance(Object arg) throws Throwable {
|
||||
NPCDefinition.forId(5917).getHandlers().put("option:shoo-away", this);
|
||||
NPCDefinition.setOptionHandler("shoo-away", this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Player player, Node node, String option) {
|
||||
player.sendChat("Thbbbbt!");
|
||||
player.animate(ANIMATION);
|
||||
NPC dog = (NPC) node;
|
||||
dog.sendChat("Whine!");
|
||||
dog.moveStep();
|
||||
dog.getPulseManager().clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ object Event {
|
|||
@JvmStatic val AttributeSet = AttributeSetEvent::class.java
|
||||
@JvmStatic val AttributeRemoved = AttributeRemoveEvent::class.java
|
||||
@JvmStatic val SpellCast = SpellCastEvent::class.java
|
||||
@JvmStatic val SpellbookChanged = SpellbookChangeEvent::class.java
|
||||
@JvmStatic val ItemAlchemized = ItemAlchemizationEvent::class.java
|
||||
@JvmStatic val ItemEquipped = ItemEquipEvent::class.java
|
||||
@JvmStatic val ItemUnequipped = ItemUnequipEvent::class.java
|
||||
|
|
|
|||
|
|
@ -182,6 +182,22 @@ open class WeightBasedTable : ArrayList<WeightedItem>() {
|
|||
return this
|
||||
}
|
||||
|
||||
fun excluding(vararg ids: Int): WeightBasedTable {
|
||||
val idSet = ids.toHashSet()
|
||||
val filtered = WeightBasedTable()
|
||||
for (item in this) {
|
||||
if (item.id !in idSet) {
|
||||
filtered.add(item)
|
||||
}
|
||||
}
|
||||
for (item in guaranteedItems) {
|
||||
if (item.id !in idSet) {
|
||||
filtered.add(item)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun create(vararg items: WeightedItem): WeightBasedTable {
|
||||
|
|
@ -197,6 +213,7 @@ open class WeightBasedTable : ArrayList<WeightedItem>() {
|
|||
val SLOT_CLUE_EASY = Items.TOOLKIT_1
|
||||
val SLOT_CLUE_MEDIUM = Items.ROTTEN_POTATO_5733
|
||||
val SLOT_CLUE_HARD = Items.GRANITE_LOBSTER_POUCH_12070
|
||||
val CLUE_SCROLL_SLOTS = intArrayOf(SLOT_CLUE_EASY, SLOT_CLUE_MEDIUM, SLOT_CLUE_HARD)
|
||||
val SLOT_CELEDT = Items.NULL_799
|
||||
val SLOT_USDT = Items.SACRED_CLAY_POUCH_CLASS_1_14422
|
||||
val SLOT_HDT = Items.SACRED_CLAY_POUCH_CLASS_2_14424
|
||||
|
|
|
|||
|
|
@ -348,7 +348,7 @@ public final class DialogueInterpreter {
|
|||
ItemDefinition itemDef = ItemDefinition.forId(item.getId());
|
||||
player.getPacketDispatch().sendItemOnInterface(item.getId(), item.getAmount(), interfaceId, 2);
|
||||
player.getPacketDispatch().sendAngleOnInterface(interfaceId, 2, itemDef.getModelZoom() / 2, itemDef.getModelRotationX(), itemDef.getModelRotationY());
|
||||
player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 45, 46);
|
||||
player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 45, 45);
|
||||
// Hide the second item which seems to be used for double items (child 1)
|
||||
player.getPacketDispatch().sendInterfaceConfig(interfaceId, 1, true);
|
||||
// Open the chatbox only after everything is set to avoid lag and flashing default strings (Line 1, Title)
|
||||
|
|
@ -524,11 +524,12 @@ public final class DialogueInterpreter {
|
|||
player.getPacketDispatch().sendInterfaceConfig(interfaceId, 1, true);
|
||||
player.getPacketDispatch().sendInterfaceConfig(interfaceId, 2, false);
|
||||
player.getPacketDispatch().sendAnimationInterface(expression, interfaceId, 2);
|
||||
player.getPacketDispatch().sendItemOnInterface(-1, 1, interfaceId, 1);
|
||||
if (npc) {
|
||||
player.getPacketDispatch().sendItemOnInterface(-1, 1, interfaceId, 1);
|
||||
player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 45, 45);
|
||||
player.getPacketDispatch().sendItemOnInterface(-1, 1, interfaceId, 2);
|
||||
player.getPacketDispatch().sendNpcOnInterface(npcId, interfaceId, 2);
|
||||
player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 45, 45);
|
||||
player.getPacketDispatch().sendAngleOnInterface(interfaceId, 2, 2150, 40, 1882);
|
||||
player.getPacketDispatch().sendString(NPCDefinition.forId(npcId).getName(), interfaceId, 3);
|
||||
} else {
|
||||
player.getPacketDispatch().sendRepositionOnInterface(interfaceId, 2, 426, 45); // 423 is 47 * 9
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import core.game.node.Node
|
|||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook
|
||||
import core.game.node.entity.player.link.SpellBookManager.SpellbookChangeSource
|
||||
import core.game.node.entity.player.link.TeleportManager.TeleportType
|
||||
import core.game.node.entity.player.link.prayer.PrayerType
|
||||
import core.game.node.item.Item
|
||||
|
|
@ -34,6 +35,7 @@ data class InterfaceCloseEvent(val component: Component) : Event
|
|||
data class AttributeSetEvent(val entity: Entity, val attribute: String, val value: Any) : Event
|
||||
data class AttributeRemoveEvent(val entity: Entity, val attribute: String) : Event
|
||||
data class SpellCastEvent(val spellBook: SpellBook, val spellId: Int, val target: Node? = null) : Event
|
||||
data class SpellbookChangeEvent(val oldSpellBook: SpellBook, val newSpellBook: SpellBook, val source: SpellbookChangeSource) : Event
|
||||
data class ItemAlchemizationEvent(val itemId: Int, val isHigh: Boolean) : Event
|
||||
data class ItemEquipEvent(val itemId: Int, val slotId: Int) : Event
|
||||
data class ItemUnequipEvent(val itemId: Int, val slotId: Int) : Event
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package core.game.node.entity.combat.spell;
|
||||
|
||||
import core.game.component.Component;
|
||||
import core.game.event.SpellCastEvent;
|
||||
import core.game.node.entity.skill.Skills;
|
||||
import core.game.node.Node;
|
||||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.combat.CombatSwingHandler;
|
||||
|
|
@ -10,6 +8,7 @@ import core.game.node.entity.combat.equipment.WeaponInterface;
|
|||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook;
|
||||
import core.game.node.entity.player.link.audio.Audio;
|
||||
import core.game.node.entity.skill.Skills;
|
||||
import core.game.node.item.Item;
|
||||
import core.game.world.GameWorld;
|
||||
import core.game.world.update.flag.context.Animation;
|
||||
|
|
@ -121,11 +120,6 @@ public abstract class MagicSpell implements Plugin<SpellType> {
|
|||
p.faceTemporary((Entity) target, 1);
|
||||
}
|
||||
if (spell.cast(p, target)) {
|
||||
if (book != SpellBook.LUNAR && p.getAttribute("spell:swap", 0) != 0) {
|
||||
p.removeAttribute("spell:swap");
|
||||
p.getSpellBookManager().setSpellBook(SpellBook.LUNAR);
|
||||
p.getInterfaceManager().openTab(new Component(SpellBook.LUNAR.getInterfaceId()));
|
||||
}
|
||||
if (!combatSpell) {
|
||||
p.getSkills().addExperience(Skills.MAGIC, spell.getExperience(p), true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,16 @@ public final class SpellBookManager {
|
|||
return spellBook;
|
||||
}
|
||||
|
||||
/**
|
||||
* All the possible ways a SpellBook can get changed.
|
||||
*/
|
||||
public enum SpellbookChangeSource {
|
||||
ALTAR,
|
||||
MAGIC_CAPE_PERK,
|
||||
SPELLBOOK_SWAP_CAST,
|
||||
SPELLBOOK_SWAP_RESTORE
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a characters spell book.
|
||||
* @author 'Vexia
|
||||
|
|
@ -134,5 +144,4 @@ public final class SpellBookManager {
|
|||
return spells.get(buttonId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -81,6 +81,33 @@ class SpawnCommandSet : CommandSet(Privilege.ADMIN){
|
|||
player.inventory.add(item)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Spawns multiple items with the given ID
|
||||
*/
|
||||
define("items", usage = "::items <lt>start-id<gt> <lt>end-id<gt> [amount]", description = "Loops through item ids from 'start-id' to 'end-id' and spawns 'amount' of them in your inventory."){player,args ->
|
||||
if (args.size < 3) {
|
||||
reject(player,"You must specify start and end item ids.")
|
||||
return@define
|
||||
}
|
||||
val start = args[1].toIntOrNull() ?: return@define
|
||||
val end = args[2].toIntOrNull() ?: return@define
|
||||
var amount = (args.getOrNull(3) ?: "1").toInt()
|
||||
for (id in start..end) {
|
||||
if (id > Cache.getItemDefinitionsSize()) {
|
||||
reject(player,"Item ID '$id' out of range.")
|
||||
return@define
|
||||
}
|
||||
val item = Item(id, amount)
|
||||
val max = player.inventory.getMaximumAdd(item)
|
||||
if (amount > max) {
|
||||
amount = max
|
||||
}
|
||||
item.setAmount(amount)
|
||||
player.inventory.add(item)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn object with given ID at the player's location
|
||||
*/
|
||||
|
|
|
|||
99
Server/src/main/core/game/system/timer/impl/SpellbookSwap.kt
Normal file
99
Server/src/main/core/game/system/timer/impl/SpellbookSwap.kt
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package core.game.system.timer.impl
|
||||
|
||||
import core.api.Event.DialogueClosed
|
||||
import core.api.Event.SpellCast
|
||||
import core.api.Event.SpellbookChanged
|
||||
import core.api.clearLogoutListener
|
||||
import core.api.registerLogoutListener
|
||||
import core.api.removeTimer
|
||||
import core.game.event.*
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.combat.equipment.WeaponInterface
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.SpellBookManager.SpellBook
|
||||
import core.game.node.entity.player.link.SpellBookManager.SpellbookChangeSource
|
||||
import core.game.system.timer.PersistTimer
|
||||
import core.tools.minutesToTicks
|
||||
|
||||
/**
|
||||
* A timer dedicated to the handling of the Lunar spell Spellbook Swap.
|
||||
*/
|
||||
class SpellbookSwap : PersistTimer(runInterval = minutesToTicks(2), identifier = "spellbook:swap") {
|
||||
|
||||
private val spellCastHook = object : EventHook<SpellCastEvent> {
|
||||
override fun process(entity : Entity, event : SpellCastEvent) {
|
||||
if (event.spellBook == SpellBook.LUNAR && event.spellId == 12) return // Ignore spellbook swap itself
|
||||
revertSpellbook(entity)
|
||||
removeTimer<SpellbookSwap>(entity)
|
||||
}
|
||||
}
|
||||
|
||||
private val spellBookChangeHook = object : EventHook<SpellbookChangeEvent> {
|
||||
override fun process(entity : Entity, event : SpellbookChangeEvent) {
|
||||
if (event.source != SpellbookChangeSource.SPELLBOOK_SWAP_CAST
|
||||
&& event.source != SpellbookChangeSource.SPELLBOOK_SWAP_RESTORE) {
|
||||
removeTimer<SpellbookSwap>(entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val dialogueCloseHook = object : EventHook<DialogueCloseEvent> {
|
||||
override fun process(entity : Entity, event : DialogueCloseEvent) {
|
||||
if (event.dialogue?.getIds()?.contains(3264731) != true) return // Spellbook swap selection dialogue
|
||||
if (entity !is Player) return
|
||||
if (SpellBook.forInterface(entity.spellBookManager.spellBook) == SpellBook.LUNAR) {
|
||||
entity.removeAttribute("spell:runes")
|
||||
removeTimer<SpellbookSwap>(entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun run(entity : Entity) : Boolean {
|
||||
revertSpellbook(entity)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onRegister(entity : Entity) {
|
||||
entity.hook(SpellCast, spellCastHook)
|
||||
entity.hook(SpellbookChanged, spellBookChangeHook)
|
||||
entity.hook(DialogueClosed, dialogueCloseHook)
|
||||
if (entity is Player) {
|
||||
registerLogoutListener(entity, "spellbook:swap") { player ->
|
||||
revertSpellbook(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRemoval(entity : Entity) {
|
||||
entity.unhook(spellCastHook)
|
||||
entity.unhook(spellBookChangeHook)
|
||||
entity.unhook(dialogueCloseHook)
|
||||
if (entity is Player) {
|
||||
clearLogoutListener(entity, "spellbook:swap")
|
||||
}
|
||||
}
|
||||
|
||||
private fun revertSpellbook(entity : Entity) {
|
||||
if (entity !is Player) return
|
||||
if (SpellBook.forInterface(entity.spellBookManager.spellBook) == SpellBook.LUNAR) return
|
||||
// Clear autocast
|
||||
val weaponInterface = entity.getExtension<WeaponInterface>(WeaponInterface::class.java)
|
||||
if (weaponInterface != null && entity.properties.autocastSpell != null) {
|
||||
weaponInterface.selectAutoSpell(-1, true)
|
||||
}
|
||||
// Close autocast selection interface if it's still open
|
||||
if (entity.getAttribute("autocast_select", false)) {
|
||||
entity.removeAttribute("autocast_select")
|
||||
entity.removeAttribute("autocast_component")
|
||||
if (weaponInterface != null) {
|
||||
entity.interfaceManager.openTab(weaponInterface)
|
||||
}
|
||||
}
|
||||
entity.dispatch(SpellbookChangeEvent(
|
||||
SpellBook.forInterface(entity.spellBookManager.spellBook),
|
||||
SpellBook.LUNAR,
|
||||
SpellbookChangeSource.SPELLBOOK_SWAP_RESTORE))
|
||||
entity.spellBookManager.setSpellBook(SpellBook.LUNAR)
|
||||
entity.spellBookManager.update(entity)
|
||||
}
|
||||
}
|
||||
|
|
@ -257,13 +257,21 @@ public final class RegionPlane {
|
|||
public void add(GroundItem item) {
|
||||
Location l = item.getLocation();
|
||||
RegionChunk c = getRegionChunk(l.getLocalX() / RegionChunk.SIZE, l.getLocalY() / RegionChunk.SIZE);
|
||||
// This adds it to the chunk to be interactable with.
|
||||
if (!c.getItems().add(item)) {
|
||||
return;
|
||||
}
|
||||
GroundItem g = (GroundItem) item;
|
||||
if (g.isPrivate()) {
|
||||
if (g.getDropper() != null) {
|
||||
PacketRepository.send(ConstructGroundItem.class, new BuildItemContext(g.getDropper(), item));
|
||||
// Limit it to the region(8x8 square) you are in +/-2 regions, as the chunk loader will load it all over again.
|
||||
// This is based on MapChunkRenderer which seems to "update" 2 chunks away.
|
||||
// Look at RegionChunk.java ConstructGroundItem.write(buffer, item);
|
||||
if (Math.abs(g.getDropper().getLocation().getRegionX() - l.getRegionX()) <= 2 &&
|
||||
Math.abs(g.getDropper().getLocation().getRegionY() - l.getRegionY()) <= 2 &&
|
||||
g.getDropper().getLocation().getZ() == l.getZ()) {
|
||||
PacketRepository.send(ConstructGroundItem.class, new BuildItemContext(g.getDropper(), item));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,62 +1,61 @@
|
|||
package core.net.packet
|
||||
|
||||
import core.game.event.ButtonClickEvent
|
||||
import content.global.ame.events.maze.MazeInterface
|
||||
import content.global.handlers.iface.ge.StockMarket
|
||||
import content.global.skill.magic.SpellListener
|
||||
import content.global.skill.magic.SpellListeners
|
||||
import content.global.skill.magic.SpellUtils
|
||||
import content.global.skill.summoning.familiar.FamiliarSpecial
|
||||
import core.ServerConstants
|
||||
import core.api.getAttribute
|
||||
import core.api.log
|
||||
import core.api.sendMessage
|
||||
import core.api.tryPop
|
||||
import core.api.utils.Vector
|
||||
import core.cache.def.impl.ItemDefinition
|
||||
import core.cache.def.impl.NPCDefinition
|
||||
import core.cache.def.impl.SceneryDefinition
|
||||
import core.game.container.Container
|
||||
import core.game.container.impl.BankContainer
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.info.Rights
|
||||
import core.game.node.entity.player.info.login.LoginConfiguration
|
||||
import core.game.node.entity.player.link.SpellBookManager
|
||||
import core.game.node.entity.combat.spell.MagicSpell
|
||||
import content.global.ame.events.maze.MazeInterface
|
||||
import content.global.skill.summoning.familiar.FamiliarSpecial
|
||||
import core.game.node.item.GroundItemManager
|
||||
import core.game.node.item.Item
|
||||
import core.game.node.scenery.Scenery
|
||||
import core.game.system.communication.ClanRank
|
||||
import core.game.system.communication.CommunicationInfo
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager
|
||||
import core.game.world.update.flag.context.ChatMessage
|
||||
import core.game.world.update.flag.*
|
||||
import core.net.amsc.MSPacketRepository
|
||||
import core.net.packet.context.PlayerContext
|
||||
import core.net.packet.out.ClearMinimapFlag
|
||||
import org.rs09.consts.Components
|
||||
import proto.management.ClanMessage
|
||||
import proto.management.JoinClanRequest
|
||||
import proto.management.LeaveClanRequest
|
||||
import core.ServerConstants
|
||||
import core.game.event.ButtonClickEvent
|
||||
import core.game.ge.GrandExchange.Companion.getOfferStats
|
||||
import core.game.ge.GrandExchange.Companion.getRecommendedPrice
|
||||
import core.game.ge.GrandExchangeOffer
|
||||
import core.game.ge.PriceIndex
|
||||
import content.global.handlers.iface.ge.StockMarket
|
||||
import content.global.skill.magic.SpellListener
|
||||
import content.global.skill.magic.SpellListeners
|
||||
import content.global.skill.magic.SpellUtils
|
||||
import core.api.log
|
||||
import core.game.interaction.*
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.combat.spell.MagicSpell
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.info.LogType
|
||||
import core.game.node.entity.player.info.PlayerMonitor
|
||||
import core.tools.SystemLogger
|
||||
import core.game.node.entity.player.info.Rights
|
||||
import core.game.node.entity.player.info.login.LoginConfiguration
|
||||
import core.game.node.entity.player.link.SpellBookManager
|
||||
import core.game.node.item.GroundItemManager
|
||||
import core.game.node.item.Item
|
||||
import core.game.node.scenery.Scenery
|
||||
import core.game.system.command.CommandSystem
|
||||
import core.game.system.communication.ClanRank
|
||||
import core.game.system.communication.CommunicationInfo
|
||||
import core.game.system.communication.GlobalChat
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.GameWorld
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.RegionManager
|
||||
import core.game.world.repository.Repository
|
||||
import core.game.world.update.flag.EntityFlag
|
||||
import core.game.world.update.flag.context.ChatMessage
|
||||
import core.net.amsc.MSPacketRepository
|
||||
import core.net.packet.context.PlayerContext
|
||||
import core.net.packet.`in`.Packet
|
||||
import core.net.packet.`in`.RunScript
|
||||
import core.net.packet.out.ClearMinimapFlag
|
||||
import core.tools.Log
|
||||
import core.worker.ManagementEvents
|
||||
import core.api.utils.Vector
|
||||
import org.rs09.consts.Components
|
||||
import proto.management.ClanMessage
|
||||
import proto.management.JoinClanRequest
|
||||
import proto.management.LeaveClanRequest
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.lang.Math.min
|
||||
|
|
@ -423,6 +422,11 @@ object PacketProcessor {
|
|||
return
|
||||
if (player.getAttribute("magic:delay", -1) > GameWorld.ticks)
|
||||
return
|
||||
// Validate spell packet interface matches player's actual spellbook
|
||||
// This is to prevent player being able to cast spells when client has stale interface (e.g. after Spellbook swap reverts)
|
||||
if (iface in intArrayOf(192, 193, 430)) {
|
||||
if (!SpellUtils.validateSpellbookInterface(player, iface)) return
|
||||
}
|
||||
val book = SpellUtils.getBookFromInterface(iface)
|
||||
if (book != "none")
|
||||
SpellListeners.run(child, type, book, player, target)
|
||||
|
|
|
|||
|
|
@ -61,9 +61,9 @@ title: More changes in Gielinor
|
|||
tags: news
|
||||
layout: newspost
|
||||
date: {current_date} 00:00:00 +0000
|
||||
authors: ryannathans
|
||||
authors: "Update Bot"
|
||||
excerpt: "There have been some more changes in Gielinor..."
|
||||
modtype: "Lead Developer"
|
||||
modtype: "Bot"
|
||||
avatar: avatar8fa9.gif
|
||||
---
|
||||
Greetings Explorers
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue