Merge pull request 'Refresh dev environment' (#3) from master into dev

Reviewed-on: #3
This commit is contained in:
randy 2024-11-17 17:16:58 -07:00
commit ee11daa0be
31 changed files with 1339 additions and 1320 deletions

View file

@ -58914,5 +58914,19 @@
"maxAmount": "1"
}
]
},
{
"default": [
{
"minAmount": "1",
"weight": "1.0",
"id": "592",
"maxAmount": "1"
}
],
"charm": [],
"ids": "8127",
"description": "Dark energy core",
"main": []
}
]

View file

@ -2079,6 +2079,18 @@
"region": "12880",
"id": "441"
},
{
"region": "12888",
"id": "445"
},
{
"region": "12889",
"id": "445"
},
{
"region": "12890",
"id": "445"
},
{
"region": "12944",
"id": "606"
@ -2199,6 +2211,18 @@
"region": "13142",
"id": "459"
},
{
"region": "13144",
"id": "445"
},
{
"region": "13145",
"id": "445"
},
{
"region": "13146",
"id": "445"
},
{
"region": "13199",
"id": "388"
@ -2271,6 +2295,18 @@
"region": "13373",
"id": "586"
},
{
"region": "13400",
"id": "445"
},
{
"region": "13401",
"id": "445"
},
{
"region": "13402",
"id": "445"
},
{
"region": "13456",
"id": "452"

View file

@ -9,6 +9,7 @@ import content.global.ame.events.evilbob.EvilBobNPC
import content.global.ame.events.evilchicken.EvilChickenNPC
import content.global.ame.events.freakyforester.FreakyForesterNPC
import content.global.ame.events.genie.GenieNPC
import content.global.ame.events.pillory.PilloryNPC
import content.global.ame.events.rickturpentine.RickTurpentineNPC
import content.global.ame.events.rivertroll.RiverTrollRENPC
import content.global.ame.events.rockgolem.RockGolemRENPC
@ -52,6 +53,7 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n
RICK_TURPENTINE(npc = RickTurpentineNPC(), loot = CERTER.loot),
SURPRISE_EXAM(npc = MysteriousOldManNPC(), type = "sexam"),
FREAKY_FORESTER(npc = FreakyForesterNPC(), skillIds = intArrayOf(Skills.WOODCUTTING)),
PILLORY(npc = PilloryNPC(), skillIds = intArrayOf(Skills.THIEVING)),
TREE_SPIRIT(npc = TreeSpiritRENPC(), skillIds = intArrayOf(Skills.WOODCUTTING)),
RIVER_TROLL(RiverTrollRENPC(), skillIds = intArrayOf(Skills.FISHING)),
ROCK_GOLEM(RockGolemRENPC(), skillIds = intArrayOf(Skills.MINING)),

View file

@ -0,0 +1,220 @@
package content.global.ame.events.pillory
import content.global.ame.RandomEvents
import core.api.*
import core.game.dialogue.FacialExpression
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.player.Player
import core.game.interaction.InterfaceListener
import core.game.interaction.QueueStrength
import core.game.node.entity.Entity
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.ZoneRestriction
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.NPCs
import org.rs09.consts.Scenery
import org.rs09.consts.Sounds
/**
* Pillory Unlocking Interface PILLORY_189
*
* https://www.youtube.com/watch?v=caWn7pE2mkE
* https://www.youtube.com/watch?v=TMVR5cZZwZ0
* https://www.youtube.com/watch?v=Ym9LCDP-Q74
* https://www.youtube.com/watch?v=_vn0QZTtI6U (Failure)
* https://www.youtube.com/watch?v=zmXDikQIua4
*
* Child IDs
* 4 - Rotating Lock Model
* 5 6 7 - Swinging Keys Models
* 8 9 10 - Buttons for the Swinging Keys Models
* 11 12 13 14 15 16 - Padlocks at the Top
* 17 18 19 20 21 22 - Padlocks stars? Model 15272, Anim 4135
*
* Model IDs
* Using the amazeballs ::listifmodels
* 9749, 9750, 9751, 9752 - Swinging Keys Models
* 9753, 9754, 9755, 9756 - Rotating Lock Models
* 9757 9758 locked unlock
*/
class PilloryInterface : InterfaceListener, InteractionListener, MapArea {
companion object {
const val PILLORY_LOCK_INTERFACE = 189
const val PILLORY_ATRRIBUTE_RETURN_LOC = "/save:original-loc"
const val PILLORY_ATTRIBUTE_EVENT_KEYS = "pillory:event-keys"
const val PILLORY_ATTRIBUTE_EVENT_LOCK = "pillory:event-lock"
const val PILLORY_ATRRIBUTE_NEEDED_TO_GET_CORRECT = "/save:pillory:target-correct"
const val PILLORY_ATRRIBUTE_CORRECT_COUNTER = "/save:pillory:num-correct"
val LOCATIONS = arrayOf(
// Varrock Cages
Location(3226, 3407, 0),
Location(3228, 3407, 0),
Location(3230, 3407, 0),
// Seers Village Cages
Location(2681, 3489, 0),
Location(2683, 3489, 0),
Location(2685, 3489, 0),
// Yannile Cages
Location(2604, 3105, 0),
Location(2606, 3105, 0),
Location(2608, 3105, 0),
)
fun initPillory(player: Player) {
setAttribute(player, PILLORY_ATRRIBUTE_NEEDED_TO_GET_CORRECT, 3)
setAttribute(player, PILLORY_ATRRIBUTE_CORRECT_COUNTER, 0)
player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from.")
}
fun randomPillory(player: Player) {
// Shuffle all 4 kinds of keys in, pick 3 for the keys, pick 1 from the 3 as the lock.
val keys = (0..3).toIntArray().let{ keys -> keys.shuffle(); return@let keys }
val lock = intArrayOf(keys[1], keys[2], keys[3]).random() // Last 3 as there are 4 keys. key[0] is fallback.
setAttribute(player, PILLORY_ATTRIBUTE_EVENT_KEYS, keys)
setAttribute(player, PILLORY_ATTRIBUTE_EVENT_LOCK, lock)
player.packetDispatch.sendModelOnInterface(9753 + lock, PILLORY_LOCK_INTERFACE, 4, 0)
player.packetDispatch.sendModelOnInterface(9749 + keys[1], PILLORY_LOCK_INTERFACE, 5, 0)
player.packetDispatch.sendModelOnInterface(9749 + keys[2], PILLORY_LOCK_INTERFACE, 6, 0)
player.packetDispatch.sendModelOnInterface(9749 + keys[3], PILLORY_LOCK_INTERFACE, 7, 0)
val numberToGetCorrect = getAttribute(player, PILLORY_ATRRIBUTE_NEEDED_TO_GET_CORRECT, 3)
val correctCount = getAttribute(player, PILLORY_ATRRIBUTE_CORRECT_COUNTER, 0)
for (i in 1.. 6) {
// Set if lock is red or green.
if (i <= correctCount) {
player.packetDispatch.sendModelOnInterface(9758, PILLORY_LOCK_INTERFACE, 10 + i, 0)
} else {
player.packetDispatch.sendModelOnInterface(9757, PILLORY_LOCK_INTERFACE, 10 + i, 0)
}
// Set if hide or show lock.
player.packetDispatch.sendInterfaceConfig(PILLORY_LOCK_INTERFACE, 10 + i, i > numberToGetCorrect)
}
}
fun selectedKey(player: Player, buttonID: Int) {
val keys = getAttribute(player, PILLORY_ATTRIBUTE_EVENT_KEYS, intArrayOf(0, 0, 0))
val lock = getAttribute(player, PILLORY_ATTRIBUTE_EVENT_LOCK, -1)
if (keys[buttonID] == lock) {
// CORRECT ANSWER
setAttribute(player, PILLORY_ATRRIBUTE_CORRECT_COUNTER, getAttribute(player, PILLORY_ATRRIBUTE_CORRECT_COUNTER, 0) + 1)
if (getAttribute(player, PILLORY_ATRRIBUTE_NEEDED_TO_GET_CORRECT, 3) <= getAttribute(player, PILLORY_ATRRIBUTE_CORRECT_COUNTER, -1)) {
player.dialogueInterpreter.sendPlainMessage(true, "", "You've escaped!")
sendMessage(player, "You've escaped!")
removeAttribute(player, PILLORY_ATRRIBUTE_NEEDED_TO_GET_CORRECT)
removeAttribute(player, PILLORY_ATRRIBUTE_CORRECT_COUNTER)
closeInterface(player)
queueScript(player, 0, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
lock(player, 6)
sendGraphics(Graphics(1576, 0, 0), player.location)
animate(player,8939)
playAudio(player, Sounds.TELEPORT_ALL_200)
return@queueScript delayScript(player, 3)
}
1 -> {
val loot = RandomEvents.CERTER.loot!!.roll(player)[0]
addItemOrDrop(player, loot.id, loot.amount)
teleport(player, getAttribute(player, PILLORY_ATRRIBUTE_RETURN_LOC, Location.create(3222, 3218, 0)))
sendGraphics(Graphics(1577, 0, 0), player.location)
animate(player,8941)
removeAttribute(player, PILLORY_ATRRIBUTE_RETURN_LOC)
closeInterface(player)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
}
return
}
randomPillory(player)
player.dialogueInterpreter.sendPlainMessage(
true,
"",
"Correct!",
"" + getAttribute(player, PILLORY_ATRRIBUTE_CORRECT_COUNTER, 0) + " down, " +
(getAttribute(player, PILLORY_ATRRIBUTE_NEEDED_TO_GET_CORRECT, 3) - getAttribute(player, PILLORY_ATRRIBUTE_CORRECT_COUNTER, 0)) + " to go!")
// Animation for the star, but it doesn't work.
player.packetDispatch.sendInterfaceConfig(PILLORY_LOCK_INTERFACE, 16 + getAttribute(player, PILLORY_ATRRIBUTE_CORRECT_COUNTER, 1), false)
sendAnimationOnInterface(player, 4135, PILLORY_LOCK_INTERFACE, 16 + getAttribute(player, PILLORY_ATRRIBUTE_CORRECT_COUNTER, 1))
} else {
// WRONG ANSWER
player.dialogueInterpreter.close()
player.dialogueInterpreter.sendDialogues(NPCs.TRAMP_2794 , FacialExpression.OLD_ANGRY1, "Bah, that's not right.","Use the key that matches the hole", "in the spinning lock.")
if (getAttribute(player, PILLORY_ATRRIBUTE_NEEDED_TO_GET_CORRECT, 0) < 6) {
setAttribute(player, PILLORY_ATRRIBUTE_NEEDED_TO_GET_CORRECT, getAttribute(player, PILLORY_ATRRIBUTE_NEEDED_TO_GET_CORRECT, 0) + 1)
}
setAttribute(player, PILLORY_ATRRIBUTE_CORRECT_COUNTER, 0)
closeInterface(player)
}
}
}
override fun defineInterfaceListeners() {
on(PILLORY_LOCK_INTERFACE){ player, component, opcode, buttonID, slot, itemID ->
when (buttonID) {
8 -> selectedKey(player, 1)
9 -> selectedKey(player, 2)
10 -> selectedKey(player, 3)
}
return@on true
}
onOpen(PILLORY_LOCK_INTERFACE){ player, component ->
return@onOpen true
}
}
override fun defineListeners() {
on(Scenery.CAGE_6836, IntType.SCENERY, "unlock") { player, node ->
if (player.location in LOCATIONS) { // When you aren't inside.
randomPillory(player)
openInterface(player, PILLORY_LOCK_INTERFACE)
player.dialogueInterpreter.sendPlainMessage(true, "", "Pick the <col=8A0808>swinging key</col> that matches the", "hole in the <col=8A0808>spinning lock</col>.")
} else {
sendMessage(player, "You can't unlock the pillory, you'll let all the prisoners out!")
}
return@on true
}
}
override fun defineAreaBorders(): Array<ZoneBorders> {
return arrayOf(
// Varrock Cages
ZoneBorders(3226, 3407, 3226, 3407),
ZoneBorders(3228, 3407, 3228, 3407),
ZoneBorders(3230, 3407, 3230, 3407),
// Seers Village Cages
ZoneBorders(2681, 3489, 2681, 3489),
ZoneBorders(2683, 3489, 2683, 3489),
ZoneBorders(2685, 3489, 2685, 3489),
// Yannile Cages
ZoneBorders(2604, 3105, 2604, 3105),
ZoneBorders(2606, 3105, 2606, 3105),
ZoneBorders(2608, 3105, 2608, 3105),
)
}
override fun getRestrictions(): Array<ZoneRestriction> {
return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT)
}
override fun areaEnter(entity: Entity) {
if (entity is Player) {
entity.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 12)
}
}
override fun areaLeave(entity: Entity, logout: Boolean) {
if (entity is Player) {
entity.interfaceManager.restoreTabs()
}
}
}

View file

@ -0,0 +1,50 @@
package content.global.ame.events.pillory
import content.global.ame.RandomEventNPC
import core.api.*
import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC
import core.game.system.timer.impl.AntiMacro
import core.game.world.map.Location
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.NPCs
import org.rs09.consts.Sounds
// "::revent [-p] <lt>player name<gt> [-e <lt>event name<gt>]"
class PilloryNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PILLORY_GUARD_2791) {
override fun init() {
super.init()
sendChat("${player.username}, you're under arrest!")
face(player)
player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from.")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int ->
when (stage) {
0 -> {
lock(player, 6)
sendGraphics(Graphics(1576, 0, 0), player.location)
animate(player,8939)
playAudio(player, Sounds.TELEPORT_ALL_200)
return@queueScript delayScript(player, 3)
}
1 -> {
if (getAttribute<Location?>(player, PilloryInterface.PILLORY_ATRRIBUTE_RETURN_LOC, null) == null) {
setAttribute(player, PilloryInterface.PILLORY_ATRRIBUTE_RETURN_LOC, player.location)
}
PilloryInterface.initPillory(player)
teleport(player, PilloryInterface.LOCATIONS.random()) // 9 random spots!
AntiMacro.terminateEventNpc(player)
sendGraphics(Graphics(1577, 0, 0), player.location)
animate(player,8941)
return@queueScript stopExecuting(player)
}
else -> return@queueScript stopExecuting(player)
}
}
}
override fun talkTo(npc: NPC) {
//player.dialogueInterpreter.open(FreakyForesterDialogue(),npc)
}
}

View file

@ -59,7 +59,7 @@ public enum BuildHotspot {
DINING_BENCH_1(15300, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.BENCH_WOODEN, Decoration.BENCH_OAK, Decoration.BENCH_CARVED_OAK, Decoration.BENCH_TEAK, Decoration.BENCH_CARVED_TEAK, Decoration.BENCH_MAHOGANY, Decoration.BENCH_GILDED),
DINING_BENCH_2(15299, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.BENCH_WOODEN, Decoration.BENCH_OAK, Decoration.BENCH_CARVED_OAK, Decoration.BENCH_TEAK, Decoration.BENCH_CARVED_TEAK, Decoration.BENCH_MAHOGANY,Decoration.BENCH_GILDED),
ROPE_BELL_PULL(15304, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.ROPE_PULL, Decoration.BELL_PULL, Decoration.FANCY_BELL_PULL),
WALL_DECORATION(15303, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.OAK_DECORATION, Decoration.TEAK_DECORATION, Decoration.GILDED_DECORATION),
WALL_DECORATION(15303, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.OAK_DECORATION, Decoration.TEAK_DECORATION, Decoration.GILDED_DECORATION),
/**
* Low-level Work shop hotspots.
@ -67,11 +67,11 @@ public enum BuildHotspot {
REPAIR(15448, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.REPAIR_BENCH, Decoration.WHETSTONE, Decoration.ARMOUR_STAND),
WORKBENCH(15439, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.WORKBENCH_WOODEN, Decoration.WORKBENCH_OAK,Decoration.WORKBENCH_STEEL_FRAME, Decoration.WORKBENCH_WITH_VICE,Decoration.WORKBENCH_WITH_LATHE),
CRAFTING(15441, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CRAFTING_TABLE_1, Decoration.CRAFTING_TABLE_2,Decoration.CRAFTING_TABLE_3, Decoration.CRAFTING_TABLE_4),
TOOL1(15443, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2, Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4, Decoration.TOOL_STORE_5),
TOOL2(15444, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5),
TOOL3(15445, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5),
TOOL4(15446, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5),
TOOL5(15447, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5),
TOOL1(15443, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2, Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4, Decoration.TOOL_STORE_5),
TOOL2(15444, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5),
TOOL3(15445, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5),
TOOL4(15446, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5),
TOOL5(15447, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.TOOL_STORE_1, Decoration.TOOL_STORE_2,Decoration.TOOL_STORE_3, Decoration.TOOL_STORE_4,Decoration.TOOL_STORE_5),
HERALDRY(15450, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.PLUMING_STAND, Decoration.SHIELD_EASEL,Decoration.BANNER_EASEL),
/**
@ -188,20 +188,10 @@ public enum BuildHotspot {
MAP(15396, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.SMALL_MAP, Decoration.MEDIUM_MAP, Decoration.LARGE_MAP),
BOOKCASE2(15397, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_BOOKCASE, Decoration.OAK_BOOKCASE, Decoration.MAHOGANY_BOOKCASE),
/**
* Manegerie Hotspots
*/
OBELISK(44911, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.MINI_OBELISK),
PET_FEEDER(44910, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.OAK_PET_FEEDER, Decoration.TEAK_PET_FEEDER, Decoration.MAHOGANY_PET_FEEDER),
PET_HOUSE(44909, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.OAK_PET_HOUSE, Decoration.TEAK_PET_HOUSE, Decoration.MAHOGANY_PET_HOUSE, Decoration.CONSECRATED_PET_HOUSE, Decoration.DESECRATED_PET_HOUSE, Decoration.NATURAL_PET_HOUSE),
HABITAT_1(44907, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.GARDEN_HABITAT, Decoration.JUNGLE_HABITAT, Decoration.DESERT_HABITAT, Decoration.POLAR_HABITAT, Decoration.VOLCANIC_HABITAT),
HABITAT_2(44908, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.GARDEN_HABITAT, Decoration.JUNGLE_HABITAT, Decoration.DESERT_HABITAT, Decoration.POLAR_HABITAT, Decoration.VOLCANIC_HABITAT),
/**
* Combat room hotspots.
*/
WALL_DECORATION2(15297, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.OAK_DECORATION, Decoration.TEAK_DECORATION, Decoration.GILDED_DECORATION),
WALL_DECORATION2(15297, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.OAK_DECORATION, Decoration.TEAK_DECORATION, Decoration.GILDED_DECORATION),
STORAGE_SPACE(15296, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.GLOVE_RACK, Decoration.WEAPONS_RACK, Decoration.EXTRA_WEAPONS_RACK),
CR_RING(15277, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.BOXING_RING, Decoration.FENCING_RING, Decoration.COMBAT_RING, Decoration.NOTHING, Decoration.NOTHING2),
CR_CORNER(15278, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.BOXING_RING, Decoration.FENCING_RING, Decoration.COMBAT_RING, Decoration.NOTHING, Decoration.NOTHING2),
@ -238,7 +228,7 @@ public enum BuildHotspot {
LECTERN(15420, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.OAK_LECTERN, Decoration.EAGLE_LECTERN, Decoration.DEMON_LECTERN, Decoration.TEAK_EAGLE_LECTERN, Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN),
CRYSTAL_BALL(15422, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CRYSTAL_BALL, Decoration.ELEMENTAL_SPHERE, Decoration.CRYSTAL_OF_POWER),
BOOKCASE3(15425, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_BOOKCASE, Decoration.OAK_BOOKCASE, Decoration.MAHOGANY_BOOKCASE),
WALL_CHART(15423, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.ALCHEMICAL_CHART, Decoration.ASTRONOMICAL_CHART, Decoration.INFERNAL_CHART),
WALL_CHART(15423, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.ALCHEMICAL_CHART, Decoration.ASTRONOMICAL_CHART, Decoration.INFERNAL_CHART),
TELESCOPE(15424, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.TELESCOPE1, Decoration.TELESCOPE2, Decoration.TELESCOPE3),
/**
@ -394,7 +384,6 @@ public enum BuildHotspot {
linkedHotspots.add(new BuildHotspot[] { PRISON, PRISON_DOOR });
linkedHotspots.add(new BuildHotspot[] { DUNGEON_DOOR_LEFT, DUNGEON_DOOR_RIGHT });
linkedHotspots.add(new BuildHotspot[] { DUNGEON_DOOR_LEFT2, DUNGEON_DOOR_RIGHT2 });
linkedHotspots.add(new BuildHotspot[] { HABITAT_1, HABITAT_2 });
linkedHotspots.add(new BuildHotspot[] { SMALL_PLANT_1, SMALL_PLANT1 });
linkedHotspots.add(new BuildHotspot[] { SHELVES, SHELVES_2 });
}

View file

@ -349,7 +349,7 @@ public final class BuildingUtils {
*/
public static void removeDecoration(Player player, Scenery object) {
if (object.getId() == Decoration.PORTAL.getObjectId() && player.getHouseManager().getPortalAmount() <= 1) {
player.getPacketDispatch().sendMessage("You need atleast one portal, how else would you leave your house?");
sendMessage(player, "You need at least one portal, how else would you leave your house?");
return;
}
Location l = object.getLocation();
@ -369,6 +369,10 @@ public final class BuildingUtils {
if (objectId == object.getId() && hotspot.getCurrentX() == l.getChunkOffsetX() && hotspot.getCurrentY() == l.getChunkOffsetY()) {
player.animate(REMOVE_ANIMATION);
removeDecoration(player, region, room, hotspot, object, style);
Decoration decoration = Decoration.forObjectId(object.getId());
for (Item item : decoration.getRefundItems()) {
addItemOrDrop(player, item.getId(), item.getAmount());
}
break;
}
}

View file

@ -4,8 +4,10 @@ import core.api.playGlobalAudio
import core.api.teleport
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.node.scenery.Scenery
import core.game.system.task.Pulse
import core.game.world.map.Location
import core.game.world.update.flag.context.Animation
@ -27,27 +29,31 @@ class MountedGlory : InteractionListener {
)
override fun defineListeners() {
on(MOUNTED_GLORY, IntType.SCENERY, "Edgeville") { player, _ ->
mountedGloryTeleport(player,0)
on(MOUNTED_GLORY, IntType.SCENERY, "Edgeville") { player, `object` ->
mountedGloryAction(player, `object`, 0)
return@on true
}
on(MOUNTED_GLORY, IntType.SCENERY, "Karamja") { player, _ ->
mountedGloryTeleport(player,1)
on(MOUNTED_GLORY, IntType.SCENERY, "Karamja") { player, `object` ->
mountedGloryAction(player, `object`, 1)
return@on true
}
on(MOUNTED_GLORY, IntType.SCENERY, "Draynor Village") { player, _ ->
mountedGloryTeleport(player,2)
on(MOUNTED_GLORY, IntType.SCENERY, "Draynor Village") { player, `object` ->
mountedGloryAction(player, `object`, 2)
return@on true
}
on(MOUNTED_GLORY, IntType.SCENERY, "Al Kharid") { player, _ ->
mountedGloryTeleport(player,3)
on(MOUNTED_GLORY, IntType.SCENERY, "Al Kharid") { player, `object` ->
mountedGloryAction(player, `object`, 3)
return@on true
}
}
private fun mountedGloryTeleport(player : Player, int : Int) {
private fun mountedGloryAction(player : Player, `object` : Node, int : Int) {
if (player.houseManager.isBuildingMode) {
player.dialogueInterpreter.open("con:removedec", `object` as Scenery)
return
}
if (!player.zoneMonitor.teleport(1, Item(Items.AMULET_OF_GLORY_1704))) {
return
}

View file

@ -26,25 +26,31 @@ class TelescopePlugin : OptionHandler() {
}
override fun handle(player: Player?, node: Node?, option: String?): Boolean {
val obj = node?.asScenery() as Scenery
val star = ShootingStarPlugin.getStar()
val delay: Int = 25000 + (25000 / 3)
val timeLeft = delay - star.ticks
val fakeTimeLeftBecauseFuckPlayers = TimeUnit.MILLISECONDS.toMinutes(timeLeft * 600L) + if(RandomFunction.random(0,100) % 2 == 0) 2 else -2
val obj = node?.asScenery() as Scenery
val window = when (obj.id) {
13657 -> 9
13658 -> 2
else -> 24
}
val fakeTimeLeft = RandomFunction.random(-window, window+1) + TimeUnit.MILLISECONDS.toMinutes(timeLeft * 600L)
player?.lock()
player?.animate(ANIMATION)
player?.interfaceManager?.open(Component(782)).also { player?.unlock()
Pulser.submit(object : Pulse(2, player) {
override fun pulse(): Boolean {
if (obj.isActive) {
player?.dialogueInterpreter?.sendDialogue("You see a shooting star! The star looks like it will land","in about $fakeTimeLeftBecauseFuckPlayers minutes!")
player?.interfaceManager?.open(Component(782)).also {
player?.unlock()
Pulser.submit(object : Pulse(2, player) {
override fun pulse(): Boolean {
if (obj.isActive) {
player?.dialogueInterpreter?.sendDialogue("You see a shooting star! The star looks like it will land","in about $fakeTimeLeft minutes!")
return true
}
return true
}
return true
}
})
return true
}
})
return true
}
}
companion object {

View file

@ -1,104 +0,0 @@
package content.global.skill.magic.lunar;
import core.game.node.entity.combat.spell.MagicSpell;
import core.game.node.entity.combat.spell.Runes;
import core.game.node.entity.skill.Skills;
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;
/**
* Represents the dream magic spell.
* @author 'Vexia
* @version 1.0
*/
@Initializable
public final class DreamSpell extends MagicSpell {
/**
* Represents the starting animation.
*/
private static final Animation START = Animation.create(6295);
/**
* Represents the dreaming animation.
*/
private static final Animation DREAMING = Animation.create(6296);
/**
* Represents the end animation.
*/
private static final Animation END = Animation.create(6297);
/**
* Represents the graphics of this spell.
*/
private static final Graphics GRAPHIC = new Graphics(1056);
/**
* Constructs a new {@code CureOtherSpell} {@code Object}.
*/
public DreamSpell() {
super(SpellBook.LUNAR, 79, 82, null, null, null, new Item[] { new Item(Runes.COSMIC_RUNE.getId(), 1), new Item(Runes.ASTRAL_RUNE.getId(), 2), new Item(Runes.BODY_RUNE.getId(), 5) });
}
@Override
public Plugin<SpellType> newInstance(SpellType arg) throws Throwable {
SpellBook.LUNAR.register(10, this);
return this;
}
@Override
public boolean cast(Entity entity, Node target) {
final Player p = (Player) entity;
if (p.getSkills().getLifepoints() == p.getSkills().getStaticLevel(Skills.HITPOINTS)) {
p.getPacketDispatch().sendMessage("You have no need to cast this spell since your hitpoints are already full.");
return false;
}
if (!meetsRequirements(entity, true, true)) {
return false;
}
p.animate(START);
p.lock();
GameWorld.getPulser().submit(new Pulse(4, p) {
@Override
public boolean pulse() {
p.animate(DREAMING);
p.graphics(GRAPHIC);
p.unlock();
return true;
}
});
p.getPulseManager().run(new Pulse(18, p) {
@Override
public boolean pulse() {
p.graphics(GRAPHIC);
p.getSkills().heal(1);
if (p.getSkills().getLifepoints() == p.getSkills().getStaticLevel(Skills.HITPOINTS)) {
stop();
return true;
}
return false;
}
@Override
public void stop() {
super.stop();
p.graphics(new Graphics(-1));
p.animate(END);
}
});
return true;
}
}

View file

@ -9,6 +9,7 @@ import content.global.skill.magic.spellconsts.Lunar
import core.api.*
import core.game.component.CloseEvent
import core.game.component.Component
import core.game.interaction.QueueStrength
import core.game.node.Node
import core.game.node.entity.combat.ImpactHandler
import core.game.node.entity.npc.NPC
@ -169,9 +170,10 @@ class LunarListeners : SpellListener("lunar"), Commands {
}
// Level 79
/**
* Dream
*/
onCast(Lunar.DREAM, NONE) { player, _ ->
requires(player, 79, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.BODY_RUNE_559, 5), Item(Items.COSMIC_RUNE_564, 1)))
dream(player)
}
// Level 80
onCast(Lunar.STRING_JEWELLERY, NONE) { player, _ ->
@ -586,9 +588,38 @@ Max-hit:${npc.getSwingHandler(false).calculateHit(npc, player, 1.0)}""", Compone
}
// Level 79
/**
* Dream
*/
private fun dream(player: Player) {
if(player.skills.lifepoints >= getStatLevel(player, Skills.HITPOINTS)) {
sendMessage(player, "You have no need to cast this spell since your hitpoints are already full.")
return
}
animate(player, Animations.LUNAR_SPELLBOOK_DREAM_START_6295)
delayEntity(player, 4)
queueScript(player, 4, QueueStrength.WEAK) { stage: Int ->
when(stage) {
0 -> {
animate(player, Animations.LUNAR_SPELLBOOK_DREAM_MID_6296)
sendGraphics(Graphics.LUNAR_SPELLBOOK_DREAM_1056, player.location)
playAudio(player, Sounds.LUNAR_SLEEP_3619)
return@queueScript delayScript(player, 5)
}
else -> {
sendGraphics(Graphics.LUNAR_SPELLBOOK_DREAM_1056, player.location)
// This heals 2 HP every min. Naturally you heal 1 for a total of 3
// The script steps every 5 ticks and we want 50 ticks before a heal
if (stage.mod(10) == 0){
heal(player, 1)
if(player.skills.lifepoints >= getStatLevel(player, Skills.HITPOINTS)) {
animate(player, Animations.LUNAR_SPELLBOOK_DREAM_END_6297)
return@queueScript stopExecuting(player)
}
}
return@queueScript delayScript(player, 5)
}
}
}
}
private fun stringJewellery(player: Player) {
val playerJewellery = ArrayDeque<Item>()

View file

@ -1,5 +1,6 @@
package content.global.skill.runecrafting
import core.api.*
import core.game.container.Container
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
@ -7,109 +8,131 @@ import core.game.node.item.Item
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import org.rs09.consts.Items
import core.tools.colorize
/**
* A class for managing rune pouches.
* @param player the player this manager instance belongs to.
* @author Ceikry
* @author Ceikry, Player Name
*/
class PouchManager(val player: Player) {
val pouches = mapOf(
Items.SMALL_POUCH_5509 to RCPouch(3,1),
Items.MEDIUM_POUCH_5510 to RCPouch(6,25),
Items.LARGE_POUCH_5512 to RCPouch(9,50),
Items.GIANT_POUCH_5514 to RCPouch(12,75)
Items.SMALL_POUCH_5509 to RCPouch(3, 3, 1),
Items.MEDIUM_POUCH_5510 to RCPouch(6, 264, 25),
Items.LARGE_POUCH_5512 to RCPouch(9, 186, 50),
Items.GIANT_POUCH_5514 to RCPouch(12,140, 75)
)
/**
* Method to add essence to a pouch
* @param pouchId the id of the pouch we are adding to
* @param itemId the item ID of the pouch we are adding to
* @param amount the amount of essence to add
* @param essence the ID of the essence item we are trying to add
* @author Ceikry
* @author Ceikry, Player Name
*/
fun addToPouch(pouchId: Int, amount: Int, essence: Int){
if(!checkRequirement(pouchId)){
player.sendMessage(colorize("%RYou lack the required level to use this pouch."))
fun addToPouch(itemId: Int, amount: Int, essence: Int) {
val pouchId = if (isDecayedPouch(itemId)) itemId - 1 else itemId
if (!checkRequirement(pouchId)) {
sendMessage(player, "You lack the required level to use this pouch.")
return
}
var amt = amount
val pouch = pouches[pouchId]
val otherEssence = when(essence){
val otherEssence = when(essence) {
Items.RUNE_ESSENCE_1436 -> Items.PURE_ESSENCE_7936
Items.PURE_ESSENCE_7936 -> Items.RUNE_ESSENCE_1436
else -> 0
}
pouch ?: return
if(amount > pouch.container.freeSlots()){
if (amount > pouch.container.freeSlots()) {
amt = pouch.container.freeSlots()
}
if(amt == 0){
player.sendMessage("This pouch is already full.")
if (amt == pouch.container.freeSlots()) {
sendMessage(player, "Your pouch is full.") //https://www.youtube.com/watch?v=wbYtRwODKTo
}
if(pouch.container.contains(otherEssence,1)){
player.sendMessage("You can only store one type of essence in each pouch.")
if (pouch.container.contains(otherEssence,1)) {
sendMessage(player, "You can only store one type of essence in each pouch.")
return
}
player.inventory.remove(Item(essence,amt))
pouch.container.add(Item(essence,amt))
}
var disappeared = false
if (itemId != Items.SMALL_POUCH_5509) {
pouch.charges -= amt
}
if (pouch.charges <= 0) {
pouch.currentCap -= when (pouchId) {
Items.MEDIUM_POUCH_5510 -> 1
Items.LARGE_POUCH_5512 -> 2
Items.GIANT_POUCH_5514 -> 3
else /*small pouch*/ -> 0
}
if (pouch.currentCap <= 0) {
// The pouch will disappear: https://runescape.wiki/w/Runecrafting_pouches?oldid=708494, https://oldschool.runescape.wiki/w/Essence_pouch
// "Degraded pouches will continue to degrade and lose essence capacity until they disappear or are repaired." implies that this is the end result of a gradual decay process
if (removeItem(player, itemId)) {
disappeared = true
sendMessage(player, "Your pouch has degraded completely.")
// Reset the pouch for when the player obtains a new one
pouch.currentCap = pouch.capacity
pouch.charges = pouch.maxCharges
pouch.remakeContainer()
}
} else {
if (!isDecayedPouch(itemId)) {
val slot = player.inventory.getSlot(Item(itemId))
replaceSlot(player, slot, Item(itemId + 1))
}
sendMessage(player, "Your pouch has decayed through use.") //https://www.youtube.com/watch?v=FUcPYrgPUlQ
pouch.charges = 9 * pouch.currentCap //implied by multiple contemporaneous sources, quantified only by https://oldschool.runescape.wiki/w/Large_pouch
pouch.remakeContainer()
if (amt > pouch.currentCap) {
amt = pouch.currentCap
}
}
}
val essItem = Item(essence, amt)
if (!disappeared && removeItem(player, essItem)) {
pouch.container.add(essItem)
}
}
/**
* Method to withdraw rune essence from a pouch.
* @param pouchId the item ID of the pouch to withdraw from
* @author Ceikry
* @param itemId the item ID of the pouch to withdraw from
* @author Ceikry, Player Name
*/
fun withdrawFromPouch(pouchId: Int){
fun withdrawFromPouch(itemId: Int) {
val pouchId = if (isDecayedPouch(itemId)) itemId - 1 else itemId
val pouch = pouches[pouchId]
pouch ?: return
val playerFree = player.inventory.freeSlots()
val playerFree = freeSlots(player)
var amount = pouch.currentCap - pouch.container.freeSlots()
if (amount > playerFree) amount = playerFree
player.debug("$amount")
if(amount == 0) return
val essence = Item(pouch.container.get(0).id,amount)
pouch.container.remove(essence)
pouch.container.shift()
player.inventory.add(essence)
if(pouch.charges-- <= 0){
pouch.currentCap -= when(pouchId){
5510 -> 1
5512 -> 2
5514 -> 3
else -> 0
}
if(pouch.currentCap <= 0){
player.inventory.remove(Item(pouchId))
player.inventory.add(Item(pouchId + 1))
player.sendMessage(colorize("%RYour ${Item(pouchId).name} has degraded completely."))
}
pouch.remakeContainer()
pouch.charges = 10
if(pouchId != 5509) {
player.sendMessage(colorize("%RYour ${Item(pouchId).name.toLowerCase()} has degraded slightly from use."))
if (amount > playerFree) {
amount = playerFree
} else {
sendMessage(player, "Your pouch has no essence left in it.") //https://www.youtube.com/watch?v=wbYtRwODKTo
if (amount == 0) {
return
}
}
val essence = Item(pouch.container.get(0).id, amount)
pouch.container.remove(essence)
pouch.container.shift()
addItem(player, essence.id, essence.amount)
}
/**
* Method to save pouches to a root JSONObject
* @param root the JSONObject we are adding the "pouches" JSONArray to
* @author Ceikry
*/
fun save(root: JSONObject){
fun save(root: JSONObject) {
val pouches = JSONArray()
for(i in this.pouches){
for(i in this.pouches) {
val pouch = JSONObject()
pouch.put("id",i.key.toString())
val items = JSONArray()
for(item in i.value.container.toArray()){
for(item in i.value.container.toArray()) {
item ?: continue
val it = JSONObject()
it.put("itemId",item.id.toString())
@ -124,14 +147,13 @@ class PouchManager(val player: Player) {
root.put("pouches",pouches)
}
/**
* Method to parse save data from a JSONArray
* @param data the JSONArray that contains the data to parse
* @author Ceikry
*/
fun parse(data: JSONArray){
for(e in data){
fun parse(data: JSONArray) {
for (e in data){
val pouch = e as JSONObject
val id = pouch["id"].toString().toInt()
val p = pouches[id]
@ -141,7 +163,7 @@ class PouchManager(val player: Player) {
p.charges = charges
p.currentCap = currentCap
p.remakeContainer()
for(i in pouch["container"] as JSONArray){
for (i in pouch["container"] as JSONArray) {
val it = i as JSONObject
it["itemId"] ?: continue
val item = it["itemId"].toString().toInt()
@ -151,33 +173,31 @@ class PouchManager(val player: Player) {
}
}
/**
* Method for checking the level requirement for a given pouch.
* @param pouchId the item ID of the pouch to check
* @author Ceikry
*/
fun checkRequirement(pouchId: Int): Boolean{
fun checkRequirement(pouchId: Int): Boolean {
val p = pouches[pouchId]
p ?: return false
return player.skills.getLevel(Skills.RUNECRAFTING) >= p.levelRequirement
}
/**
* Method for sending the player a message about how much space is left in a pouch
* @param pouchId the item ID of the pouch to check
* @author Ceikry
* @param itemId the item ID of the pouch to check
* @author Ceikry, Player Name
*/
fun checkAmount(pouchId: Int){
fun checkAmount(itemId: Int) {
val pouchId = if (isDecayedPouch(itemId)) itemId - 1 else itemId
val p = pouches[pouchId]
p ?: return
player.sendMessage("This pouch has space for ${p.container.freeSlots()} more essence.")
}
fun isDecayedPouch(pouchId: Int): Boolean{
if(pouchId == 5510) return false
fun isDecayedPouch(pouchId: Int): Boolean {
if (pouchId == Items.MEDIUM_POUCH_5510) return false
return pouches[pouchId - 1] != null
}
@ -185,12 +205,12 @@ class PouchManager(val player: Player) {
* A class that represents a runecrafting pouch.
* @author Ceikry
*/
class RCPouch(val capacity: Int, val levelRequirement: Int){
class RCPouch(val capacity: Int, val maxCharges: Int, val levelRequirement: Int) {
var container = Container(capacity)
var currentCap = capacity
var charges = 10
fun remakeContainer(){
var charges = maxCharges
fun remakeContainer() {
this.container = Container(currentCap)
}
}
}
}

View file

@ -11,7 +11,7 @@ import core.tools.colorize
/**
* Handles the rune pouches.
* @author Ceikry
* @author Ceikry, Player Name
*/
class RunePouchPlugin : OptionHandler() {
@Throws(Throwable::class)
@ -37,21 +37,11 @@ class RunePouchPlugin : OptionHandler() {
if(preferenceFlag == 0) rEssAmt else pEssAmt
)
if(player.pouchManager.isDecayedPouch(node.id)){
player.debug("E2")
when(option) { //Handling for IF the pouch has already completely decayed
"drop" -> player.dialogueInterpreter.open(9878,Item(node.id))
else -> player.sendMessage(colorize("%RThis pouch has completely decayed and needs to be repaired."))
}
} else {
player.debug("E")
when (option) { //Normal handling
"fill" -> player.pouchManager.addToPouch(node.id, essence.amount, essence.id)
"empty" -> player.pouchManager.withdrawFromPouch(node.id)
"check" -> player.pouchManager.checkAmount(node.id)
"drop" -> player.dialogueInterpreter.open(9878,Item(node.id))
}
when (option) {
"fill" -> player.pouchManager.addToPouch(node.id, essence.amount, essence.id)
"empty" -> player.pouchManager.withdrawFromPouch(node.id)
"check" -> player.pouchManager.checkAmount(node.id)
"drop" -> player.dialogueInterpreter.open(9878,Item(node.id))
}
return true
}
@ -59,4 +49,4 @@ class RunePouchPlugin : OptionHandler() {
override fun isWalk(): Boolean {
return false
}
}
}

View file

@ -4,6 +4,7 @@ import core.game.dialogue.DialoguePlugin;
import core.game.node.entity.npc.NPC;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import org.rs09.consts.Items;
/**
* Handles the dark mages dialogue.
@ -140,18 +141,18 @@ public final class DarkMageDialogue extends DialoguePlugin {
private boolean repair() {
player.pouchManager.getPouches().forEach((id, pouch) -> {
pouch.setCurrentCap(pouch.getCapacity());
pouch.setCharges(10);
Item essence = null;
if(!pouch.getContainer().isEmpty()){
int ess = pouch.getContainer().get(0).getId();
int amount = pouch.getContainer().getAmount(ess);
essence = new Item(ess,amount);
pouch.setCharges(pouch.getMaxCharges());
Item essItem = null;
if (!pouch.getContainer().isEmpty()) {
int essence = pouch.getContainer().get(0).getId();
int amount = pouch.getContainer().getAmount(essence);
essItem = new Item(essence, amount);
}
pouch.remakeContainer();
if(essence != null){
pouch.getContainer().add(essence);
if (essItem != null) {
pouch.getContainer().add(essItem);
}
if(id != 5509) {
if (id != Items.SMALL_POUCH_5509) {
if (player.getInventory().contains(id + 1, 1)) {
player.getInventory().remove(new Item(id + 1, 1));
player.getInventory().add(new Item(id, 1));

View file

@ -26,27 +26,74 @@ class AFURepairClimbHandler : InteractionListener {
override fun defineListeners() {
on(repairIDs, IntType.SCENERY, "repair"){ player, _ ->
var rco: RepairClimbObject = RepairClimbObject.GWD
for(ent in RepairClimbObject.values()) if(ent.destinationDown?.withinDistance(player.location,2) == true || ent.destinationUp?.withinDistance(player.location,2) == true) rco = ent
repair(player,rco)
return@on true
if (hasRequirement(player, "All Fired Up")){
val rco: RepairClimbObject? = getClimbingObject(player)
repair(player,rco!!)
return@on true
}
return@on false
}
on(climbIDs, IntType.SCENERY, "climb"){ player, node ->
var rco: RepairClimbObject = RepairClimbObject.GWD
for(ent in RepairClimbObject.values()) if(ent.destinationDown?.withinDistance(player.location,2) == true || ent.destinationUp?.withinDistance(player.location,2) == true) rco = ent
climb(player,rco,node.location)
val rco: RepairClimbObject? = getClimbingObject(player)
climb(player,rco!!,node.location)
return@on true
}
}
private fun getClimbingObject(player: Player): RepairClimbObject?{
for(ent in RepairClimbObject.values())
if(ent.destinationDown?.withinDistance(player.location,2) == true ||
ent.destinationUp?.withinDistance(player.location,2) == true){
return ent
}
return null
}
private fun repair(player: Player,rco: RepairClimbObject){
if (rco == RepairClimbObject.TEMPLE){
// You can do this 2 different ways
val hasSmithingLevel = getDynLevel(player, Skills.SMITHING) >= 70
val hasConstructionLevel = getDynLevel(player, Skills.CONSTRUCTION) >= 59
if (!hasConstructionLevel && !hasSmithingLevel){
sendDialogue(player, "You need level 70 smithing or 59 construction for this.")
return
}
val hasHammer = inInventory(player, Items.HAMMER_2347)
val hasSmithingItems = hasHammer && inInventory(player, Items.IRON_BAR_2351, 2)
val hasConstructionItems = hasHammer && inInventory(player, Items.PLANK_960, 2)
if (hasSmithingLevel && hasSmithingItems){
if (removeItem(player,Item(Items.IRON_BAR_2351, 2))) {
setVarbit(player, rco.varbit, 1, true)
return
}
}
// Only check this if the smithing repair didn't work
if (hasConstructionLevel && hasConstructionItems){
val nails = NailType.get(player, 4)
if (nails != null){
if (removeItem(player, Item(Items.PLANK_960, 2)) && removeItem(player, Item(nails.itemId, 4))) {
setVarbit(player, rco.varbit, 1, true)
return
}
}
}
var msg = "You need "
msg += if (hasSmithingLevel) "a hammer and 2 iron bars" else ""
msg += if (hasSmithingLevel && hasConstructionLevel) " or " else ""
msg += if (hasConstructionLevel) "a hammer, 2 planks and 4 nails for this." else " for this."
sendDialogue(player, msg)
return
}
val skill = rco.levelRequirement?.first ?: 0
val level = rco.levelRequirement?.second ?: 0
if(player.skills.getLevel(skill) < level){
player.dialogueInterpreter.sendDialogue("You need level $level ${Skills.SKILL_NAME[skill]} for this.")
return
}
var requiresNeedle = false
@ -64,10 +111,7 @@ class AFURepairClimbHandler : InteractionListener {
requiresNeedle = true
arrayOf(Item(Items.JUTE_FIBRE_5931,3))
}
RepairClimbObject.TEMPLE -> {
arrayOf(Item(Items.IRON_BAR_2351,2))
}
else -> return
}
if(requiresNeedle){
@ -75,7 +119,7 @@ class AFURepairClimbHandler : InteractionListener {
player.inventory.remove(*requiredItems)
if (Random().nextBoolean()) player.inventory.remove(Item(Items.NEEDLE_1733))
} else {
player.dialogueInterpreter.sendDialogue("You need a needle and ${requiredItems.map { "${it.amount} ${it.name.toLowerCase()}s" }.toString().replace("[","").replace("]","")} for this.")
player.dialogueInterpreter.sendDialogue("You need a needle and ${requiredItems.map { "${it.amount} ${it.name.lowercase()}s" }.toString().replace("[","").replace("]","")} for this.")
return
}
} else {
@ -89,11 +133,10 @@ class AFURepairClimbHandler : InteractionListener {
}
player.inventory.remove(*requiredItems)
} else {
player.dialogueInterpreter.sendDialogue("You need a hammer and ${requiredItems.map { "${it.amount} ${it.name.toLowerCase()}s" }.toString().replace("[","").replace("]","")} for this.")
player.dialogueInterpreter.sendDialogue("You need a hammer and ${requiredItems.map { "${it.amount} ${it.name.lowercase()}s" }.toString().replace("[","").replace("]","")} for this.")
return
}
}
setVarbit(player, rco.varbit, 1, true)
}
@ -105,7 +148,7 @@ class AFURepairClimbHandler : InteractionListener {
DEATH_PLATEAU(5161,Location.create(2949, 3623, 0),Location.create(2954, 3623, 0), Pair(Skills.CONSTRUCTION,42)),
BURTHORPE(5160,Location.create(2941, 3563, 0),Location.create(2934, 3563, 0),Pair(Skills.SMITHING,56)),
GWD(5163,null,null,Pair(Skills.CRAFTING,60)),
TEMPLE(5164,Location.create(2949, 3835, 0),Location.create(2956, 3835, 0),Pair(Skills.SMITHING,64));
TEMPLE(5164,Location.create(2949, 3835, 0),Location.create(2956, 3835, 0),Pair(0,0)); // This needs to be handled specially so don't have levels here
fun getOtherLocation(player: Player): Location?{
if(player.location == destinationDown) return destinationUp

View file

@ -305,6 +305,8 @@ class RecruitmentDriveListeners : InteractionListener {
// Clear inventory whenever you leave the recruitment drive area
entity.inventory.clear()
entity.equipment.clear()
// Restore player normal tabs on leave
entity.interfaceManager.openDefaultTabs()
// Teleport you out if you log out. You should do this in one sitting.
if (logout) {
PacketRepository.send(MinimapState::class.java, MinimapStateContext(entity, 0))

View file

@ -200,7 +200,6 @@ class JarvaldDialogue(player: Player? = null) : DialoguePlugin(player) {
}
override fun getIds(): IntArray {
// 2435 is a wrapper for 2436
return intArrayOf(2435, NPCs.JARVALD_2436, NPCs.JARVALD_2437, NPCs.JARVALD_2438)
}
}

View file

@ -1,6 +1,5 @@
package content.region.fremennik.rellekka.handlers;
import content.region.fremennik.rellekka.dialogue.JarvaldDialogue;
import content.region.fremennik.rellekka.dialogue.MariaGunnarsDialogue;
import core.cache.def.impl.SceneryDefinition;
import core.game.system.task.Pulse;
@ -41,7 +40,6 @@ public final class RellekkaZone extends MapZone implements Plugin<Object> {
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
ZoneBuilder.configure(this);
ClassScanner.definePlugin(new JarvaldDialogue());
ClassScanner.definePlugins(new RellekaOptionHandler(), new MariaGunnarsDialogue());
ClassScanner.definePlugin(new OptionHandler() {

View file

@ -87,9 +87,9 @@ class SCThormacDialogue(val questStage: Int) : DialogueFile() {
WAITING_FOR_SCORPIONS -> {
if (!hasAnItem(player!!, Items.SCORPION_CAGE_456, Items.SCORPION_CAGE_457, Items.SCORPION_CAGE_458,
if (!hasAnItem(player!!, arrayOf(Items.SCORPION_CAGE_456, Items.SCORPION_CAGE_457, Items.SCORPION_CAGE_458,
Items.SCORPION_CAGE_459, Items.SCORPION_CAGE_460, Items.SCORPION_CAGE_461,
Items.SCORPION_CAGE_462).exists()){
Items.SCORPION_CAGE_462), false).exists()){
playerl(FacialExpression.SAD, "I've lost my cage.").also { stage = GIVE_ANOTHER_CAGE }
}
else{

View file

@ -187,10 +187,15 @@ class KingBolrenDialogue : DialogueFile() {
}
isQuestComplete(player!!, questName) -> {
when(stage) {
0 -> playerl("Hello Bolren.").also { stage++ }
1 -> npcl("Thank you for your help traveler.").also { stage = END_DIALOGUE }
0 -> playerl("Hello again Bolren.").also { stage++ }
1 -> npcl("Well hello, it's good to see you again.").also { stage = if (hasAnItem(player!!, Items.GNOME_AMULET_589).container != null) END_DIALOGUE else 2 }
2 -> playerl("I've lost my amulet.").also { stage++ }
3 -> npcl("Oh dear. Here, take another. We are truly indebted to you.").also {
addItemOrDrop(player!!, Items.GNOME_AMULET_589)
stage = END_DIALOGUE
}
}
}
}
}
}
}

View file

@ -1,30 +0,0 @@
package content.region.kandarin.seers.handlers;
import core.cache.def.impl.SceneryDefinition;
import core.game.interaction.OptionHandler;
import core.game.node.Node;
import core.game.node.entity.player.Player;
import core.plugin.Initializable;
import core.plugin.Plugin;
/**
* Represents the plugin used to unlock the sheers cage.
* @author 'Vexia
* @versio 1.0
*/
@Initializable
public final class SeersCageUnlockPlugin extends OptionHandler {
@Override
public boolean handle(Player player, Node node, String option) {
player.getPacketDispatch().sendMessage("You can't unlock the pillory, you'll let all the prisoners out!");
return true;
}
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
SceneryDefinition.forId(6836).getHandlers().put("option:unlock", this);
return this;
}
}

View file

@ -30,7 +30,7 @@ class DimintheisDialogue(player: Player? = null): core.game.dialogue.DialoguePlu
return true
}
val hasGauntlets = hasAnItem(player, Items.COOKING_GAUNTLETS_775, Items.GOLDSMITH_GAUNTLETS_776, Items.CHAOS_GAUNTLETS_777, Items.FAMILY_GAUNTLETS_778).container != null
val hasGauntlets = hasAnItem(player, arrayOf(Items.COOKING_GAUNTLETS_775, Items.GOLDSMITH_GAUNTLETS_776, Items.CHAOS_GAUNTLETS_777, Items.FAMILY_GAUNTLETS_778), true).container != null
if (questComplete && hasGauntlets) {
npc("Thank you for saving our family honour, ",

View file

@ -2,15 +2,11 @@ package content.region.wilderness.handlers;
import content.data.BossKillCounter;
import core.game.node.entity.Entity;
import core.game.node.entity.combat.BattleState;
import core.game.node.entity.combat.CombatStyle;
import core.game.node.entity.combat.CombatSwingHandler;
import core.game.node.entity.combat.*;
import core.game.node.entity.combat.ImpactHandler.HitsplatType;
import core.game.node.entity.combat.MultiSwingHandler;
import core.game.node.entity.combat.equipment.SwitchAttack;
import core.game.node.entity.combat.equipment.Weapon;
import core.game.node.entity.impl.Projectile;
import core.game.node.entity.npc.AbstractNPC;
import core.game.node.entity.npc.NPC;
import core.game.node.entity.npc.NPCBehavior;
import core.game.node.entity.player.Player;
@ -22,7 +18,6 @@ import core.game.world.map.RegionManager;
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;
import org.rs09.consts.NPCs;
@ -32,7 +27,6 @@ import java.util.List;
/**
* Handles the Corporeal beast NPC.
* @author Emperor
*
*/
@Initializable
public final class CorporealBeastNPC extends NPCBehavior {
@ -41,19 +35,24 @@ public final class CorporealBeastNPC extends NPCBehavior {
* The combat handler.
*/
private final MultiSwingHandler combatHandler = new CombatHandler();
/**
* The dark energy core NPC.
*/
public NPC darkEnergyCore;
/**
* Whether to force a dark core spawn roll on our next swing (only done if we just got hit >= 32 damage).
*/
public boolean forceCoreRoll = false;
/**
* Constructs a new {@code CorporealBeastNPC} {@code Object}.
*/
public CorporealBeastNPC() {
super(new int[]{NPCs.CORPOREAL_BEAST_8133});
super(new int[] { NPCs.CORPOREAL_BEAST_8133 });
}
@Override
public void onCreation(NPC self) {
self.configureBossData();
@ -64,27 +63,31 @@ public final class CorporealBeastNPC extends NPCBehavior {
return combatHandler;
}
@Override
public void beforeDamageReceived(NPC self, Entity attacker, BattleState state) {
if(state.getStyle() == CombatStyle.MELEE || state.getStyle() == CombatStyle.RANGE) {
Weapon w = state.getWeapon();
String name = w != null ? w.getName() : "";
if(w == null || name.toLowerCase().indexOf("spear") == -1) {
if(state.getEstimatedHit() > 0) {
state.setEstimatedHit(state.getEstimatedHit()/2);
}
if(state.getSecondaryHit() > 0) {
state.setSecondaryHit(state.getSecondaryHit()/2);
}
}
}
if(state.getEstimatedHit() > 100) {
state.setEstimatedHit(100);
}
if(state.getSecondaryHit() > 100) {
state.setSecondaryHit(100);
}
}
@Override
public void beforeDamageReceived(NPC self, Entity attacker, BattleState state) {
if (state.getStyle() == CombatStyle.MELEE || state.getStyle() == CombatStyle.RANGE) {
Weapon w = state.getWeapon();
String name = w != null ? w.getName() : "";
if (w == null || name.toLowerCase().indexOf("spear") == -1) {
if (state.getEstimatedHit() > 0) {
state.setEstimatedHit(state.getEstimatedHit() / 2);
}
if (state.getSecondaryHit() > 0) {
state.setSecondaryHit(state.getSecondaryHit() / 2);
}
}
}
if (state.getEstimatedHit() >= 32) {
CorporealBeastNPC corp = (CorporealBeastNPC) self.behavior;
corp.forceCoreRoll = true;
}
if (state.getEstimatedHit() > 100) {
state.setEstimatedHit(100);
}
if (state.getSecondaryHit() > 100) {
state.setSecondaryHit(100);
}
}
@Override
public void onDeathFinished(NPC self, Entity killer) {
@ -94,41 +97,46 @@ public final class CorporealBeastNPC extends NPCBehavior {
darkEnergyCore = null;
}
}
/**
* Handles the Corporeal beast's combat.
* @author Emperor
*
*/
static class CombatHandler extends MultiSwingHandler {
/**
* Constructs a new {@code CombatHandler} {@code Object}.
*/
public CombatHandler() {
super(
//Melee (crush)
new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10057)).setMaximumHit(52),
//Melee (slash)
new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10058)).setMaximumHit(51),
//Magic (drain skill)
new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1823, 60, 36, 41, 46)).setMaximumHit(55),
//Magic (location based)
new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1824, 60, 36, 41, 46)).setMaximumHit(42),
//Magic (hit through prayer)
new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1825, 60, 36, 41, 46)).setMaximumHit(66)
);
//Melee (crush)
new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10057)).setMaximumHit(51),
//Melee (slash)
new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10058)).setMaximumHit(51),
//Magic (drain skill, blocked by prayer)
new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1823, 60, 36, 41, 46)).setMaximumHit(55),
//Magic (location-based, hits through prayer)
new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1824, 60, 36, 41, 46)).setMaximumHit(42),
//Magic (hits through prayer)
new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1825, 60, 36, 41, 46)).setMaximumHit(65)
);
}
@Override
public int swing(Entity entity, Entity victim, BattleState state) {
spawnDarkCore(entity, (CorporealBeastNPC)((NPC) entity).behavior, victim);
// If we're below the right HP threshold, roll a chance to spawn the dark core
CorporealBeastNPC corp = (CorporealBeastNPC) ((NPC) entity).behavior;
double thresh = entity.getSkills().getMaximumLifepoints() * (0.3 + (entity.getViewport().getCurrentPlane().getPlayers().size() * 0.05));
if (corp.forceCoreRoll || entity.getSkills().getLifepoints() < thresh) {
rollDarkCore(entity, corp, victim);
corp.forceCoreRoll = false;
}
// If we can stomp, do that for our turn
if (doStompAttack(entity)) {
entity.getProperties().getCombatPulse().setNextAttack(entity.getProperties().getAttackSpeed());
return -1;
}
//Location based attack.
// Location-based attack.
if (super.getNext().getProjectile() != null && super.getNext().getProjectile().getProjectileId() == 1824) {
setCurrent(getNext());
CombatStyle style = getCurrent().getStyle();
@ -142,18 +150,17 @@ public final class CorporealBeastNPC extends NPCBehavior {
}
return super.swing(entity, victim, state);
}
/**
* Spawns a dark core.
* Rolls a 1/8 chance to spawn a dark core.
* @param npc The corporeal beast NPC.
* @param victim The victim.
*/
private void spawnDarkCore(Entity corp, final CorporealBeastNPC npc, Entity victim) {
if (npc.darkEnergyCore != null && npc.darkEnergyCore.isActive()) {
private void rollDarkCore(Entity corp, final CorporealBeastNPC npc, Entity victim) {
if (npc.darkEnergyCore != null && npc.darkEnergyCore.isActive() && !DeathTask.isDead(npc.darkEnergyCore)) {
return;
}
double max = corp.getSkills().getMaximumLifepoints() * (0.3 + (corp.getViewport().getCurrentPlane().getPlayers().size() * 0.05));
if (corp.getSkills().getLifepoints() > max) {
if (!RandomFunction.roll(8)) {
return;
}
Location l = RegionManager.getTeleportLocation(victim.getLocation(), 3);
@ -163,8 +170,8 @@ public final class CorporealBeastNPC extends NPCBehavior {
GameWorld.getPulser().submit(new Pulse(2, corp) {
@Override
public boolean pulse() {
if (npc.darkEnergyCore == null)
return true;
if (npc.darkEnergyCore == null)
return true;
npc.darkEnergyCore.init();
return true;
}
@ -184,6 +191,7 @@ public final class CorporealBeastNPC extends NPCBehavior {
boolean secondStage = false;
List<Player> players = RegionManager.getLocalPlayers(entity);
Location[] locations = null;
@Override
public boolean pulse() {
if (!secondStage) {
@ -215,11 +223,14 @@ public final class CorporealBeastNPC extends NPCBehavior {
locations = null;
return true;
}
private void hit(Player p) {
int max = p.hasProtectionPrayer(CombatStyle.MAGIC) ? 13 : 42;
int hit = 0;
if (isAccurateImpact(entity, p)) {
hit = RandomFunction.random(max);
hit = RandomFunction.random(42);
if (p.hasProtectionPrayer(CombatStyle.MAGIC)) {
hit = (int) (hit * 0.6);
}
}
p.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC);
}
@ -252,7 +263,7 @@ public final class CorporealBeastNPC extends NPCBehavior {
}
return false;
}
@Override
public void adjustBattleState(Entity entity, Entity victim, BattleState state) {
super.adjustBattleState(entity, victim, state);
@ -262,10 +273,9 @@ public final class CorporealBeastNPC extends NPCBehavior {
int skill = random == 0 ? Skills.PRAYER : random == 1 ? Skills.MAGIC : Skills.SUMMONING;
int drain = 1 + RandomFunction.random(6);
if ((skill == Skills.PRAYER ? victim.getSkills().getPrayerPoints() : victim.getSkills().getLevel(skill)) < 1) {
victim.getImpactHandler().manualHit(entity, drain, HitsplatType.NORMAL,2);
victim.getImpactHandler().manualHit(entity, drain, HitsplatType.NORMAL, 2);
((Player) victim).getPacketDispatch().sendMessage("Your Hitpoints have been slightly drained!");
}
else {
} else {
if (skill == Skills.PRAYER) {
victim.getSkills().decrementPrayerPoints(drain);
} else {
@ -278,11 +288,13 @@ public final class CorporealBeastNPC extends NPCBehavior {
}
}
}
@Override
protected int getFormattedHit(Entity entity, Entity victim, BattleState state, int hit) {
if (getCurrent().getProjectile() == null || getCurrent().getProjectile().getProjectileId() != 1825) {
hit = (int) entity.getFormattedHit(state, hit);
} else if (victim.hasProtectionPrayer(CombatStyle.MAGIC)) {
hit = (int) (hit * 0.6);
}
return formatHit(victim, hit);
}

View file

@ -9,6 +9,8 @@ import core.game.node.entity.player.Player;
import core.game.system.task.Pulse;
import core.game.world.GameWorld;
import core.game.world.map.Location;
import core.game.world.map.path.Path;
import core.game.world.map.path.Pathfinder;
import core.plugin.Initializable;
import core.tools.RandomFunction;
@ -16,8 +18,7 @@ import static core.api.ContentAPIKt.*;
/**
* Handles the Dark Energy Core NPC.
* @author Emperor
*
* @author Emperor, Player Name
*/
@Initializable
public final class DarkEnergyCoreNPC extends AbstractNPC {
@ -31,21 +32,22 @@ public final class DarkEnergyCoreNPC extends AbstractNPC {
* The amount of ticks.
*/
private int ticks = 0;
/**
* The amount of failed attacks.
*/
private int fails = 0;
/**
* Constructs a new {@code DarkEnergyCoreNPC} {@code Object}.
*/
public DarkEnergyCoreNPC() {
this(8127, null);
}
/**
* Constructs a new {@code DarkEnergyCoreNPC} {@code Object}.
*
* @param id The NPC id.
* @param location The location.
*/
@ -59,14 +61,15 @@ public final class DarkEnergyCoreNPC extends AbstractNPC {
if (objects.length > 0) {
core.master = (NPC) objects[0];
}
core.setRespawn(false);
return core;
}
@Override
public boolean canStartCombat(Entity victim) {
return false; //No combat needed.
}
@Override
public void handleTickActions() {
ticks++;
@ -91,8 +94,11 @@ public final class DarkEnergyCoreNPC extends AbstractNPC {
if (jump) {
Entity victim = master.getProperties().getCombatPulse().getVictim();
if (++fails >= 3 && victim != null && victim.getViewport().getCurrentPlane() == getViewport().getCurrentPlane()) {
jump(victim.getLocation());
fails = 0;
Path path = Pathfinder.find(getLocation(), victim.getLocation(), 1);
if (path.isSuccessful() || !path.isMoveNear()) {
jump(victim.getLocation());
fails = 0;
}
}
} else {
fails = 0;
@ -119,7 +125,6 @@ public final class DarkEnergyCoreNPC extends AbstractNPC {
@Override
public int[] getIds() {
return new int[] { 8127 };
return new int[]{8127};
}
}

View file

@ -247,14 +247,40 @@ class ContainerisedItem(val container: core.game.container.Container?, val itemI
}
/**
* Check if player has any of the specified item IDs equipped, in inventory, or in banks
* Returns a ContainerisedItem containing the container and the item ID if found, otherwise ContainerisedItem(null, -1) if not found
* Check if player has the specified item ID equipped, in inventory, or in their bank
* @param id The item ID to check
* @return A ContainerisedItem containing the container and the item ID if found, otherwise ContainerisedItem(null, -1) if not found
*/
fun hasAnItem(player: Player, vararg ids: Int): ContainerisedItem {
for (searchSpace in arrayOf(player.inventory, player.equipment, player.bankPrimary, player.bankSecondary)) {
fun hasAnItem(player: Player, id: Int): ContainerisedItem {
return hasAnItem(player, arrayOf(id), false)
}
/**
* Check if player has the specified item ID equipped, in inventory, or in their bank
* @param id The item ID to check
* @param checkSecondBank Whether to check the player's second bank.
* @return A ContainerisedItem containing the container and the item ID if found, otherwise ContainerisedItem(null, -1) if not found
*/
fun hasAnItem(player: Player, id: Int, checkSecondBank: Boolean): ContainerisedItem {
return hasAnItem(player, arrayOf(id), checkSecondBank)
}
/**
* Check if player has any of the specified item IDs equipped, in inventory, or in their bank
* @param ids An array of item IDs to check
* @param checkSecondBank Whether to check the player's second bank.
* @return A ContainerisedItem containing the container and the item ID if found, otherwise ContainerisedItem(null, -1) if not found
*/
fun hasAnItem(player: Player, ids: Array<Int>, checkSecondBank: Boolean): ContainerisedItem {
val searchSpace = if (checkSecondBank) {
arrayOf(player.inventory, player.equipment, player.bankPrimary, player.bankSecondary)
} else {
arrayOf(player.inventory, player.equipment, player.bankPrimary)
}
for (container in searchSpace) {
for (id in ids) {
if (searchSpace.containItems(id)) {
return ContainerisedItem(searchSpace, id)
if (container.containItems(id)) {
return ContainerisedItem(container, id)
}
}
}

View file

@ -296,6 +296,7 @@ class CombatPulse(
}
setVictim(victim)
entity.onAttack(victim as Entity?)
victim.scripts.removeWeakScripts()
if (!isAttacking)
entity.pulseManager.run(this)

View file

@ -8,7 +8,7 @@ import core.tools.RandomFunction
/**
* Handles combat swings with switching combat styles.
* @author Emperor
* @author Ceirky, Kotlin conversion
* @author Ceikry, Kotlin conversion
*/
open class MultiSwingHandler(meleeDistance: Boolean, vararg attacks: SwitchAttack) : CombatSwingHandler(CombatStyle.RANGE) {
/**

View file

@ -195,5 +195,6 @@ enum class QuestRequirements (val questName: String, vararg val requirements: Re
SUMMERS_END ("Summer's End", QuestReq(SPIRIT_OF_SUMMER), SkillReq(Skills.FIREMAKING, 47), SkillReq(Skills.HUNTER, 35), SkillReq(Skills.MINING, 45), SkillReq(Skills.PRAYER, 55), SkillReq(Skills.SUMMONING, 23), SkillReq(Skills.WOODCUTTING, 37)),
SEERGAZE ("Legacy of Seergaze", QuestReq(HALLOWVALE), SkillReq(Skills.AGILITY, 29), SkillReq(Skills.CONSTRUCTION, 20), SkillReq(Skills.CRAFTING, 47), SkillReq(Skills.FIREMAKING, 40), SkillReq(Skills.MAGIC, 49), SkillReq(Skills.MINING, 35), SkillReq(Skills.SLAYER, 31)),
SMOKING_KILLS ("Smoking Kills", QuestReq(RESTLESS_GHOST), QuestReq(ICTHLARIN), SkillReq(Skills.CRAFTING, 25), SkillReq(Skills.SLAYER, 35)),
WHILE_GUTHIX_SLEEPS ("While Guthix Sleeps", SkillReq(Skills.SUMMONING, 23), SkillReq(Skills.HUNTER, 55), SkillReq(Skills.THIEVING, 60), SkillReq(Skills.DEFENCE, 65), SkillReq(Skills.FARMING, 65), SkillReq(Skills.HERBLORE, 65), SkillReq(Skills.MAGIC, 75), QuestReq(DEFENDER_VARROCK), QuestReq(DREAM_MENTOR), QuestReq(SAND), QuestReq(KINGS_RANSOM), QuestReq(LEGEND), QuestReq(MEP_2), QuestReq(PATH_GLOUPHRIE), QuestReq(RFD), QuestReq(SUMMERS_END), QuestReq(SWAN), QuestReq(TEARS_OF_GUTHIX), QuestReq(ZOGRE))
WHILE_GUTHIX_SLEEPS ("While Guthix Sleeps", SkillReq(Skills.SUMMONING, 23), SkillReq(Skills.HUNTER, 55), SkillReq(Skills.THIEVING, 60), SkillReq(Skills.DEFENCE, 65), SkillReq(Skills.FARMING, 65), SkillReq(Skills.HERBLORE, 65), SkillReq(Skills.MAGIC, 75), QuestReq(DEFENDER_VARROCK), QuestReq(DREAM_MENTOR), QuestReq(SAND), QuestReq(KINGS_RANSOM), QuestReq(LEGEND), QuestReq(MEP_2), QuestReq(PATH_GLOUPHRIE), QuestReq(RFD), QuestReq(SUMMERS_END), QuestReq(SWAN), QuestReq(TEARS_OF_GUTHIX), QuestReq(ZOGRE)),
ALL_FIRED_UP ("All Fired Up", QuestReq(PRIEST), SkillReq(Skills.FIREMAKING, 43))
}

View file

@ -19,7 +19,7 @@ import java.util.*
object SystemLogger {
val t = Terminal()
val errT = t.forStdErr()
val formatter = SimpleDateFormat("HH:mm:ss")
val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXX")
private fun getTime(): String{
return "[" + formatter.format(Date(System.currentTimeMillis())) +"]"

View file

@ -66,12 +66,18 @@ class MajorUpdateWorker {
ServerStore.clearDailyEntries()
if (ServerConstants.DAILY_RESTART) {
for (player in Repository.players.filter { !it.isArtificial }) {
player.packetDispatch.sendSystemUpdate(500)
}
Repository.sendNews(colorize("%RSERVER GOING DOWN FOR DAILY RESTART IN 5 MINUTES!"))
ServerConstants.DAILY_RESTART = false
submitWorldPulse(object : Pulse(100) {
var counter = 0
override fun pulse(): Boolean {
counter++
for (player in Repository.players.filter { !it.isArtificial }) {
player.packetDispatch.sendSystemUpdate((5 - counter) * 100)
}
if (counter == 5) {
exitProcess(0)
}